summaryrefslogtreecommitdiff
path: root/tools/proxyclient/m1n1/hv/gdbserver/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/proxyclient/m1n1/hv/gdbserver/__init__.py')
-rw-r--r--tools/proxyclient/m1n1/hv/gdbserver/__init__.py480
1 files changed, 480 insertions, 0 deletions
diff --git a/tools/proxyclient/m1n1/hv/gdbserver/__init__.py b/tools/proxyclient/m1n1/hv/gdbserver/__init__.py
new file mode 100644
index 0000000..ade807f
--- /dev/null
+++ b/tools/proxyclient/m1n1/hv/gdbserver/__init__.py
@@ -0,0 +1,480 @@
+# SPDX-License-Identifier: MIT
+import errno, io, os, pkgutil, re, selectors, socketserver, threading, traceback
+from construct import Array, BytesInteger, Container, Int32ul, Int64ul, Struct
+
+from ...proxy import *
+from ...sysreg import *
+from ...utils import *
+
+from ..types import *
+
+__all__ = ["GDBServer"]
+
+class GDBServer:
+ __g = Struct(
+ "regs" / Array(32, Int64ul),
+ "pc" / Int64ul,
+ "spsr" / Int32ul,
+ "q" / Array(32, BytesInteger(16, swapped=True)),
+ "fpsr" / Int32ul,
+ "fpcr" / Int32ul,
+ )
+ __seperator = re.compile("[,;:]")
+
+ def __init__(self, hv, address, log):
+ self.__hc = None
+ self.__hg = None
+ self.__hv = hv
+ self.__interrupt_eventfd = os.eventfd(0, flags=os.EFD_CLOEXEC | os.EFD_NONBLOCK)
+ self.__interrupt_selector = selectors.DefaultSelector()
+ self.__request = None
+ self.log = log
+
+ self.__interrupt_selector.register(self.__interrupt_eventfd, selectors.EVENT_READ)
+
+ handle = self.__handle
+
+ class Handler(socketserver.BaseRequestHandler):
+ def handle(self):
+ handle(self.request)
+
+ self.__server = socketserver.UnixStreamServer(address, Handler, False)
+ self.__thread = threading.Thread(target=self.__server.serve_forever,)
+
+ def __add_wp(self, addr, kind, lsc):
+ start = addr & 7
+ if start + kind > 8:
+ return b"E01"
+
+ self.__hv.add_hw_wp(addr & ~7, ((1 << kind) - 1) << start, lsc)
+ return b"OK"
+
+ def __remove_wp(self, addr):
+ self.__hv.remove_hw_wp(addr & ~7)
+ return b"OK"
+
+ def __cpu(self, cpu):
+ if cpu is None:
+ return
+
+ self.__hv.cpu(cpu)
+
+ def __stop_reply(self):
+ self.__hc = None
+ self.__hg = None
+
+ prefix = b"T05thread:"
+
+ if self.__hv.exc_reason == START.EXCEPTION_LOWER:
+ if self.__hv.exc_code == EXC.SYNC:
+ if self.__hv.ctx.esr.EC == ESR_EC.BKPT_LOWER:
+ prefix = b"T05hwbreak:;thread:"
+ elif self.__hv.ctx.esr.EC == ESR_EC.WATCH_LOWER:
+ bas = self.__hv.get_wp_bas(self.__hv.ctx.far)
+ if not bas is None and bas != 0:
+ offset = 0
+ while (bas & (1 << offset)) == 0:
+ offset += 1
+ addr = self.__hv.ctx.far + offset
+ formatted_addr = bytes(format(addr, "x"), "utf-8")
+ prefix = b"T05watch:" + formatted_addr + b";thread:"
+ elif self.__hv.exc_reason == START.HV:
+ if self.__hv.exc_code == HV_EVENT.USER_INTERRUPT:
+ prefix = b"T02thread:"
+
+ return prefix + bytes(format(self.__hv.ctx.cpu_id, "x"), "utf-8") + b";"
+
+ def __wait_shell(self):
+ try:
+ os.eventfd_read(self.__interrupt_eventfd)
+ except BlockingIOError:
+ pass
+
+ while not self.__interrupt_eventfd in (key.fileobj for key, mask in self.__interrupt_selector.select()):
+ recv = self.__request.recv(1)
+ if not recv:
+ break
+
+ for byte in recv:
+ if byte in b"\1\3":
+ self.__hv.interrupt()
+ break
+
+ def __eval(self, data):
+ if self.log:
+ self.log(f"eval: {data}")
+
+ if len(data) < 1:
+ return b""
+
+ if data[0] in b"?":
+ return self.__stop_reply()
+
+ if data[0] in b"c":
+ if len(data) != 1:
+ self.__cpu(self.__hc)
+ self.__hv.ctx.elr = int(data[1:].decode(), 16)
+
+ self.__hv.cont()
+ self.__wait_shell()
+ return self.__stop_reply()
+
+ if data[0] in b"g":
+ self.__cpu(self.__hg)
+ g = Container()
+ g.regs = self.__hv.ctx.regs.copy()
+ g.regs[31] = self.__hv.ctx.sp[1]
+ g.pc = self.__hv.ctx.elr
+ g.spsr = self.__hv.ctx.spsr.value
+ g.q = self.__hv.u.q
+ g.fpsr = self.__hv.u.mrs(FPSR)
+ g.fpcr = self.__hv.u.mrs(FPCR)
+
+ return bytes(GDBServer.__g.build(g).hex(), "utf-8")
+
+ if data[0] in b"G":
+ g = GDBServer.__g.parse(bytes.fromhex(data[1:].decode()))
+ self.__cpu(self.__hg)
+
+ for index in range(31):
+ self.__hv.ctx.regs[index] = g.regs[index]
+
+ self.__hv.ctx.sp[1] = g.regs[31]
+ self.__hv.ctx.elr = g.pc
+ self.__hv.ctx.spsr = g.spsr.value
+
+ q = self.__hv.u.q
+ for index, value in enumerate(g.q):
+ q[index] = value
+ self.__hv.u.push_simd()
+
+ self.__hv.u.msr(FPSR, g.fpsr, silent=True)
+ self.__hv.u.msr(FPCR, g.fpsr, silent=True)
+
+ return b"OK"
+
+ if data[0] in b"H":
+ if len(data) > 1:
+ if data[1] in b"c":
+ cpu_id = int(data[2:].decode(), 16)
+ if cpu_id in self.__hv.started_cpus:
+ self.__hc = cpu_id
+ return b"OK"
+
+ return b"E01"
+
+ if data[1] in b"g":
+ cpu_id = int(data[2:].decode(), 16)
+ if cpu_id in self.__hv.started_cpus:
+ self.__hg = cpu_id
+ return b"OK"
+
+ return b"E01"
+
+ return b""
+
+ if data[0] in b"krR":
+ self.__hv.reboot()
+
+ if data[0] in b"m":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=1)
+ fields = [int(field, 16) for field in split]
+ return bytes(self.__hv.readmem(fields[0], fields[1]).hex(), "utf-8")
+
+ if data[0] in b"M":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=2)
+ mem = bytes.fromhex(split[2])[:int(split[1], 16)]
+ if self.__hv.writemem(int(split[0], 16), mem) < len(mem):
+ return "E22"
+
+ return b"OK"
+
+ if data[0] in b"p":
+ number = int(data[1:].decode(), 16)
+ self.__cpu(self.__hg)
+ if number < 31:
+ reg = GDBServer.__g.regs.subcon.subcon.build(self.__hv.ctx.regs[number])
+ elif number == 31:
+ reg = GDBServer.__g.regs.subcon.subcon.build(self.__hv.ctx.sp[1])
+ elif number == 32:
+ reg = GDBServer.__g.pc.build(self.__hv.ctx.elr)
+ elif number == 33:
+ reg = GDBServer.__g.spsr.build(self.__hv.ctx.spsr.value)
+ elif number < 66:
+ reg = GDBServer.__g.q.subcon.subcon.build(self.__hv.u.q[number - 34])
+ elif number == 66:
+ reg = GDBServer.__g.fpsr.build(self.__hv.u.mrs(FPSR))
+ elif number == 67:
+ reg = GDBServer.__g.fpcr.build(self.__hv.u.mrs(FPCR))
+ else:
+ return b"E01"
+
+ return bytes(reg.hex(), "utf-8")
+
+ if data[0] in b"P":
+ partition = data[1:].partition(b"=")
+ number = int(partition[0].decode(), 16)
+ reg = bytes.fromhex(partition[2].decode())
+ self.__cpu(self.__hg)
+ if number < 31:
+ self.__hv.ctx.regs[number] = GDBServer.__g.regs.subcon.subcon.unpack(reg)
+ elif number == 31:
+ self.__hv.ctx.regs[1] = GDBServer.__g.regs.subcon.subcon.unpack(reg)
+ elif number == 32:
+ self.__hv.ctx.elr = GDBServer.__g.pc.parse(reg)
+ elif number == 33:
+ self.__hv.ctx.spsr.value = GDBServer.__g.spsr.parse(reg)
+ elif number < 66:
+ self.__hv.u.q[number - 34] = GDBServer.__g.q.subcon.subcon.parse(reg)
+ self.__hv.u.push_simd()
+ elif number == 66:
+ self.__hv.u.msr(FPSR, GDBServer.__g.fpsr.parse(reg), silent=True)
+ elif number == 67:
+ self.__hv.u.msr(FPCR, GDBServer.__g.fpcr.parse(reg), silent=True)
+ else:
+ return b"E01"
+
+ return b"OK"
+
+ if data[0] in b"q":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=1)
+ if split[0] == "C":
+ cpu_id = self.__hg or self.__hv.ctx.cpu_id
+ return b"QC" + bytes(format(cpu_id, "x"), "utf-8")
+
+ if split[0] == "fThreadInfo":
+ cpu_ids = b",".join(bytes(format(cpu.cpu_id, "x"), "utf-8") for cpu in self.__hv.adt["cpus"])
+ return b"m" + cpu_ids
+
+ if split[0] == "sThreadInfo":
+ return b"l"
+
+ if split[0] == "Rcmd":
+ self.__cpu(self.__hg)
+ self.__hv.run_code(split[1])
+ return b"OK"
+
+ if split[0] == "Supported":
+ return b"PacketSize=65536;qXfer:features:read+;hwbreak+"
+
+ if split[0] == "ThreadExtraInfo":
+ thread_id = int(split[1], 16)
+ for node in self.__hv.adt["cpus"]:
+ if node.cpu_id == thread_id:
+ return bytes(bytes(str(node), "utf-8").hex(), "utf-8")
+
+ return b""
+
+ if split[0] == "Xfer":
+ xfer = GDBServer.__seperator.split(split[1], maxsplit=4)
+ if xfer[0] == "features" and xfer[1] == "read":
+ resource = os.path.join("features", xfer[2])
+ annex = pkgutil.get_data(__name__, resource)
+ if annex is None:
+ return b"E00"
+
+ request_offset = int(xfer[3], 16)
+ request_len = int(xfer[4], 16)
+ read = annex[request_offset:request_offset + request_len]
+ return (b"l" if len(read) < request_len else b"m") + read
+
+ return b""
+
+ if split[0] == "HostInfo":
+ addressing_bits = bytes(str(64 - self.__hv.pac_mask.bit_count()), "utf-8")
+ return b"cputype:16777228;cpusubtype:2;endian:little;ptrsize:64;watchpoint_exceptions_received:before;addressing_bits:" + addressing_bits + b";"
+
+ return b""
+
+ if data[0] in b"s":
+ self.__cpu(self.__hc)
+
+ if len(data) != 1:
+ self.__hv.ctx.elr = int(data[1:].decode(), 16)
+
+ self.__hv.step()
+ return self.__stop_reply()
+
+ if data[0] in b"T":
+ if int(data[1:].decode(), 16) in self.__hv.started_cpus:
+ return b"OK"
+
+ return b"E01"
+
+ if data[0] in b"X":
+ partition = data[1:].partition(b":")
+ split = GDBServer.__seperator.split(partition[0].decode(), maxsplit=1)
+ mem = partition[2][:int(split[1], 16)]
+ if self.__hv.writemem(int(split[0], 16), mem) < len(mem):
+ return b"E22"
+
+ return b"OK"
+
+ if data[0] in b"z":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=2)
+ if split[0] == "1":
+ self.__hv.remove_hw_bp(int(split[1], 16))
+ return b"OK"
+
+ if split[0] == "2":
+ return self.__remove_wp(int(split[1], 16))
+
+ if split[0] == "3":
+ return self.__remove_wp(int(split[1], 16))
+
+ if split[0] == "4":
+ return self.__remove_wp(int(split[1], 16))
+
+ return b""
+
+ if data[0] in b"Z":
+ split = GDBServer.__seperator.split(data[1:].decode(), maxsplit=2)
+ if split[0] == "1":
+ self.__hv.add_hw_bp(int(split[1], 16))
+ return b"OK"
+
+ if split[0] == "2":
+ addr = int(split[1], 16)
+ kind = int(split[2], 16)
+ return self.__add_wp(addr, kind, DBGWCR_LSC.S)
+
+ if split[0] == "3":
+ addr = int(split[1], 16)
+ kind = int(split[2], 16)
+ return self.__add_wp(addr, kind, DBGWCR_LSC.L)
+
+ if split[0] == "4":
+ addr = int(split[1], 16)
+ kind = int(split[2], 16)
+ return self.__add_wp(addr, kind, DBGWCR_LSC.S | DBGWCR_LSC.L)
+
+ return b""
+
+ return b""
+
+ def __send(self, prefix, data):
+ with io.BytesIO(prefix) as buffer:
+ buffer.write(prefix)
+
+ last = 0
+ for index, byte in enumerate(data):
+ if not byte in b"#$}*":
+ continue
+
+ buffer.write(data[last:index])
+ buffer.write(b"}")
+ buffer.write(bytes([byte ^ 0x20]))
+ last = index + 1
+
+ buffer.write(data[last:])
+ checksum = (sum(buffer.getvalue()) - sum(prefix)) % 256
+
+ buffer.write(b"#")
+ buffer.write(bytes(format(checksum, "02x"), "utf-8"))
+
+ value = buffer.getvalue()
+
+ if self.log:
+ self.log(f"send: {value}")
+
+ self.__request.send(value)
+
+ def __handle(self, request):
+ self.__request = request
+ input_buffer = b""
+
+ if not self.__hv.in_shell:
+ self.__hv.interrupt()
+ self.__wait_shell()
+
+ self.__interrupt_selector.register(self.__request, selectors.EVENT_READ)
+ try:
+ while True:
+ recv = self.__request.recv(65536)
+ if not recv:
+ break
+
+ input_buffer += recv
+
+ while True:
+ dollar = input_buffer.find(b"$")
+ if dollar < 0:
+ input_buffer = b""
+ break
+
+ sharp = input_buffer.find(b"#", dollar)
+ if sharp < 0 or len(input_buffer) < sharp + 3:
+ input_buffer = input_buffer[dollar:]
+ break
+
+ input_data = input_buffer[dollar + 1:sharp]
+ input_checksum = input_buffer[sharp + 1:sharp + 3]
+ input_buffer = input_buffer[sharp + 3:]
+
+ try:
+ parsed_input_checksum = int(input_checksum.decode(), 16)
+ except ValueError as error:
+ print(error)
+ continue
+
+ if (sum(input_data) % 256) != parsed_input_checksum:
+ self.__request.send(b"-")
+ continue
+
+ self.__request.send(b"+")
+
+ with io.BytesIO() as input_decoded:
+ input_index = 0
+ input_last = 0
+ while input_index < len(input_data):
+ if input_data[input_index] == b"*":
+ input_decoded.write(input_data[input_last:input_index])
+ instance = input_decoded.getvalue()[-1]
+ input_index += 1
+ input_run_len = input_data[input_index] - 29
+ input_run = bytes([instance]) * input_run_len
+ input_decoded.write(input_run)
+ input_index += 1
+ input_last = input_index
+ elif input_data[input_index] == b"}":
+ input_decoded.write(input_data[input_last:input_index])
+ input_index += 1
+ input_decoded.write(bytes([input_data[input_index] ^ 0x20]))
+ input_index += 1
+ input_last = input_index
+ else:
+ input_index += 1
+
+ input_decoded.write(input_data[input_last:])
+
+ try:
+ output_decoded = self.__eval(input_decoded.getvalue())
+ except Exception:
+ output_decoded = b"E." + bytes(traceback.format_exc(), "utf-8")
+
+ self.__send(b"$", output_decoded)
+ finally:
+ self.__interrupt_selector.unregister(self.__request)
+
+ def notify_in_shell(self):
+ os.eventfd_write(self.__interrupt_eventfd, 1)
+
+ def activate(self):
+ try:
+ self.__server.server_bind()
+ except OSError as error:
+ if error.errno != errno.EADDRINUSE:
+ raise
+
+ os.remove(self.__server.server_address)
+ self.__server.server_bind()
+
+ self.__server.server_activate()
+ self.__thread.start()
+
+ def shutdown(self):
+ os.close(self.__interrupt_eventfd)
+ self.__interrupt_selector.close()
+ self.__server.shutdown()
+ self.__server.server_close()
+ self.__thread.join()