diff options
| author | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
|---|---|---|
| committer | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
| commit | e80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch) | |
| tree | 79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/hv | |
Diffstat (limited to 'tools/proxyclient/hv')
23 files changed, 3878 insertions, 0 deletions
diff --git a/tools/proxyclient/hv/README.md b/tools/proxyclient/hv/README.md new file mode 100644 index 0000000..c4fce23 --- /dev/null +++ b/tools/proxyclient/hv/README.md @@ -0,0 +1,4 @@ +## m1n1 hypervisor scripts + +This directory contains scripts that can be executed to configure the hypervisor using the `-m` +option to `run_guest.py`. diff --git a/tools/proxyclient/hv/trace_agx.py b/tools/proxyclient/hv/trace_agx.py new file mode 100644 index 0000000..024130c --- /dev/null +++ b/tools/proxyclient/hv/trace_agx.py @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: MIT +import datetime + +from m1n1.constructutils import show_struct_trace, Ver +from m1n1.utils import * + +Ver.set_version(hv.u) + +trace_device("/arm-io/sgx", False) +trace_device("/arm-io/pmp", False) +trace_device("/arm-io/gfx-asc", False) + +from m1n1.trace.agx import AGXTracer +AGXTracer = AGXTracer._reloadcls(True) + +agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1) + +#agx_tracer.encoder_id_filter = lambda i: (i >> 16) == 0xc0de +agx_tracer.start() + +def resume_tracing(ctx): + fname = f"{datetime.datetime.now().isoformat()}.log" + hv.set_logfile(open(f"gfxlogs/{fname}", "a")) + agx_tracer.resume() + return True + +def pause_tracing(ctx): + agx_tracer.pause() + hv.set_logfile(None) + return True + +hv.add_hvcall(100, resume_tracing) +hv.add_hvcall(101, pause_tracing) + +mode = TraceMode.SYNC +trace_range(irange(agx_tracer.gpu_region, agx_tracer.gpu_region_size), mode=mode, name="gpu_region") +trace_range(irange(agx_tracer.gfx_shared_region, agx_tracer.gfx_shared_region_size), mode=mode, name="gfx_shared_region") + +## Trace the entire mmio range around the GPU +node = hv.adt["/arm-io/sgx"] +addr, size = node.get_reg(0) +hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx") +#hv.trace_range(irange(addr, 0x1000000), TraceMode.OFF, name="sgx") +hv.trace_range(irange(0x204017030, 8), TraceMode.SYNC, name="faultcode") + +trace_device("/arm-io/sgx", True) +trace_device("/arm-io/gfx-asc", False) + +def trace_all_gfx_io(): + # These are all the IO ranges that get mapped into the UAT iommu pagetable + # Trace them so we can see if any of them are being written by the CPU + + # page (8): fa010020000 ... fa010023fff -> 000000020e100000 [8000020e100447] + hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) + + # page (10): fa010028000 ... fa01002bfff -> 000000028e104000 [c000028e104447] + hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) + + # page (22): fa010058000 ... fa01005bfff -> 000000028e494000 [8000028e494447] + hv.trace_range(irange(0x28e494000, 0x4000), mode=TraceMode.SYNC) + + # page (28): fa010070000 ... fa010073fff -> 0000000204d60000 [c0000204d60447] + hv.trace_range(irange(0x204d60000, 0x4000), mode=TraceMode.SYNC) + + # page (30): fa010078000 ... fa01007bfff -> 0000000200000000 [c0000200000447] + # to + # page (83): fa01014c000 ... fa01014ffff -> 00000002000d4000 [c00002000d4447] + hv.trace_range(irange(0x200000000, 0xd5000), mode=TraceMode.SYNC) + + # page (84): fa010150000 ... fa010153fff -> 0000000201000000 [c0000201000447] + #page (137): fa010224000 ... fa010227fff -> 00000002010d4000 [c00002010d4447] + hv.trace_range(irange(0x201000000, 0xd5000), mode=TraceMode.SYNC) + + # page (138): fa010228000 ... fa01022bfff -> 0000000202000000 [c0000202000447] + # page (191): fa0102fc000 ... fa0102fffff -> 00000002020d4000 [c00002020d4447] + hv.trace_range(irange(0x202000000, 0xd5000), mode=TraceMode.SYNC) + + # page (192): fa010300000 ... fa010303fff -> 0000000203000000 [c0000203000447] + hv.trace_range(irange(0x203000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x204000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x205000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x206000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x207000000, 0xd5000), mode=TraceMode.SYNC) + + # page (464): fa010740000 ... fa010743fff -> 00000002643c4000 [c00002643c4447] + hv.trace_range(irange(0x2643c4000, 0x4000), mode=TraceMode.SYNC) + # page (466): fa010748000 ... fa01074bfff -> 000000028e3d0000 [c000028e3d0447] + hv.trace_range(irange(0x28e3d0000, 0x4000), mode=TraceMode.SYNC) + # page (468): fa010750000 ... fa010753fff -> 000000028e3c0000 [8000028e3c0447] + hv.trace_range(irange(0x28e3c0000, 0x4000), mode=TraceMode.SYNC) + + # page (8): f9100020000 ... f9100023fff -> 0000000406000000 [60000406000447] + # page (263): f910041c000 ... f910041ffff -> 00000004063fc000 [600004063fc447] + hv.trace_range(irange(0x2643c4000, 0x63fc000), mode=TraceMode.SYNC) + +def trace_gpu_irqs(): + # Trace sgx interrupts + node = hv.adt["/arm-io/sgx"] + for irq in getattr(node, "interrupts"): + hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) + + ## Trace gfx-asc interrupts + #node = hv.adt["/arm-io/gfx-asc"] + #for irq in getattr(node, "interrupts"): + #hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) + +trace_gpu_irqs() diff --git a/tools/proxyclient/hv/trace_agx_defer.py b/tools/proxyclient/hv/trace_agx_defer.py new file mode 100644 index 0000000..257fdc5 --- /dev/null +++ b/tools/proxyclient/hv/trace_agx_defer.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: MIT +import datetime + +from m1n1.constructutils import show_struct_trace, Ver +from m1n1.utils import * + +Ver.set_version(hv.u) + +from m1n1.trace.agx import AGXTracer +AGXTracer = AGXTracer._reloadcls(True) + +agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1) + +agx_tracer.pause_after_init = True +agx_tracer.trace_usermap = False +agx_tracer.trace_kernmap = False +agx_tracer.redump = True + +agx_tracer.start() + +def resume_tracing(ctx): + fname = f"{datetime.datetime.now().isoformat()}.log" + hv.set_logfile(open(f"gfxlogs/{fname}", "a")) + agx_tracer.start() + agx_tracer.resume() + return True + +def pause_tracing(ctx): + agx_tracer.pause() + agx_tracer.stop() + hv.set_logfile(None) + return True + +hv.add_hvcall(100, resume_tracing) +hv.add_hvcall(101, pause_tracing) diff --git a/tools/proxyclient/hv/trace_agx_pwr.py b/tools/proxyclient/hv/trace_agx_pwr.py new file mode 100644 index 0000000..dd5acf4 --- /dev/null +++ b/tools/proxyclient/hv/trace_agx_pwr.py @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: MIT +import datetime + +from m1n1.constructutils import show_struct_trace, Ver +from m1n1.utils import * + +Ver.set_version(hv.u) + +trace_device("/arm-io/sgx", True) +#trace_device("/arm-io/pmp", True) +#trace_device("/arm-io/gfx-asc", False) + +from m1n1.trace.agx import AGXTracer +AGXTracer = AGXTracer._reloadcls(True) + +agx_tracer = AGXTracer(hv, "/arm-io/gfx-asc", verbose=1) +agx_tracer.trace_kernmap = False +agx_tracer.trace_kernva = False +agx_tracer.trace_usermap = False + +sgx = hv.adt["/arm-io/sgx"] + +freqs = [] +voltages = [] + +for j in range(8): + for i, v in enumerate(voltages): + if j != 0: + v = 1 + sgx.perf_states[i+j*len(voltages)].freq = freqs[i] * 1000000 + sgx.perf_states[i+j*len(voltages)].volt = v + sgx.perf_states_sram[i+j*len(voltages)].freq = freqs[i] * 1000000 + sgx.perf_states_sram[i+j*len(voltages)].volt = 1 + if j >= 1: + getattr(sgx, f"perf_states{j}")[i].freq = freqs[i] * 1000000 + getattr(sgx, f"perf_states{j}")[i].volt = v + getattr(sgx, f"perf_states_sram{j}")[i].freq = freqs[i] * 1000000 + getattr(sgx, f"perf_states_sram{j}")[i].volt = 1 + +def after_init(): + plat = hv.adt.compatible[0].lower() + fname = f"initdata/{datetime.datetime.now().isoformat()}-{plat}.log" + idlog = open(fname, "w") + print(f"Platform: {plat}", file=idlog) + fw = hv.adt["/chosen"].firmware_version.split(b"\0")[0].decode("ascii") + print(f"Firmware: {fw}", file=idlog) + sfw = hv.adt["/chosen"].system_firmware_version + print(f"System firmware: {sfw}", file=idlog) + print(file=idlog) + + print("ADT SGX:", file=idlog) + print(sgx, file=idlog) + open("adt_hv.txt","w").write(str(hv.adt)) + + print("InitData:", file=idlog) + print(agx_tracer.state.initdata, file=idlog) + + power = [int(i) for i in agx_tracer.state.initdata.regionB.hwdata_b.rel_max_powers] + volt = [int(i[0]) for i in agx_tracer.state.initdata.regionB.hwdata_b.voltages] + freq = [int(i) for i in agx_tracer.state.initdata.regionB.hwdata_b.frequencies] + + print("p/v", [p/max(1, v) for p,v in zip(power,volt)]) + print("p/f", [p/max(1, f) for p,f in zip(power,freq)]) + print("p/v2", [p/max(1, (v*v)) for p,v in zip(power,volt)]) + hv.reboot() + +agx_tracer.after_init_hook = after_init + +#agx_tracer.encoder_id_filter = lambda i: (i >> 16) == 0xc0de +agx_tracer.start() + +def resume_tracing(ctx): + fname = f"{datetime.datetime.now().isoformat()}.log" + hv.set_logfile(open(f"gfxlogs/{fname}", "a")) + agx_tracer.resume() + return True + +def pause_tracing(ctx): + agx_tracer.pause() + hv.set_logfile(None) + return True + +hv.add_hvcall(100, resume_tracing) +hv.add_hvcall(101, pause_tracing) + +mode = TraceMode.SYNC +trace_range(irange(agx_tracer.gpu_region, agx_tracer.gpu_region_size), mode=mode, name="gpu_region") +trace_range(irange(agx_tracer.gfx_shared_region, agx_tracer.gfx_shared_region_size), mode=mode, name="gfx_shared_region") + +## Trace the entire mmio range around the GPU +node = hv.adt["/arm-io/sgx"] +addr, size = node.get_reg(0) +hv.trace_range(irange(addr, 0x1000000), TraceMode.SYNC, name="sgx") +#hv.trace_range(irange(addr, 0x1000000), TraceMode.OFF, name="sgx") +hv.trace_range(irange(0x204017030, 8), TraceMode.SYNC, name="faultcode") + +trace_device("/arm-io/sgx", True) +trace_device("/arm-io/gfx-asc", False) + +def trace_all_gfx_io(): + # These are all the IO ranges that get mapped into the UAT iommu pagetable + # Trace them so we can see if any of them are being written by the CPU + + # page (8): fa010020000 ... fa010023fff -> 000000020e100000 [8000020e100447] + hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) + + # page (10): fa010028000 ... fa01002bfff -> 000000028e104000 [c000028e104447] + hv.trace_range(irange(0x20e100000, 0x4000), mode=TraceMode.SYNC) + + # page (22): fa010058000 ... fa01005bfff -> 000000028e494000 [8000028e494447] + hv.trace_range(irange(0x28e494000, 0x4000), mode=TraceMode.SYNC) + + # page (28): fa010070000 ... fa010073fff -> 0000000204d60000 [c0000204d60447] + hv.trace_range(irange(0x204d60000, 0x4000), mode=TraceMode.SYNC) + + # page (30): fa010078000 ... fa01007bfff -> 0000000200000000 [c0000200000447] + # to + # page (83): fa01014c000 ... fa01014ffff -> 00000002000d4000 [c00002000d4447] + hv.trace_range(irange(0x200000000, 0xd5000), mode=TraceMode.SYNC) + + # page (84): fa010150000 ... fa010153fff -> 0000000201000000 [c0000201000447] + #page (137): fa010224000 ... fa010227fff -> 00000002010d4000 [c00002010d4447] + hv.trace_range(irange(0x201000000, 0xd5000), mode=TraceMode.SYNC) + + # page (138): fa010228000 ... fa01022bfff -> 0000000202000000 [c0000202000447] + # page (191): fa0102fc000 ... fa0102fffff -> 00000002020d4000 [c00002020d4447] + hv.trace_range(irange(0x202000000, 0xd5000), mode=TraceMode.SYNC) + + # page (192): fa010300000 ... fa010303fff -> 0000000203000000 [c0000203000447] + hv.trace_range(irange(0x203000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x204000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x205000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x206000000, 0xd5000), mode=TraceMode.SYNC) + hv.trace_range(irange(0x207000000, 0xd5000), mode=TraceMode.SYNC) + + # page (464): fa010740000 ... fa010743fff -> 00000002643c4000 [c00002643c4447] + hv.trace_range(irange(0x2643c4000, 0x4000), mode=TraceMode.SYNC) + # page (466): fa010748000 ... fa01074bfff -> 000000028e3d0000 [c000028e3d0447] + hv.trace_range(irange(0x28e3d0000, 0x4000), mode=TraceMode.SYNC) + # page (468): fa010750000 ... fa010753fff -> 000000028e3c0000 [8000028e3c0447] + hv.trace_range(irange(0x28e3c0000, 0x4000), mode=TraceMode.SYNC) + + # page (8): f9100020000 ... f9100023fff -> 0000000406000000 [60000406000447] + # page (263): f910041c000 ... f910041ffff -> 00000004063fc000 [600004063fc447] + hv.trace_range(irange(0x2643c4000, 0x63fc000), mode=TraceMode.SYNC) + +def trace_gpu_irqs(): + # Trace sgx interrupts + node = hv.adt["/arm-io/sgx"] + for irq in getattr(node, "interrupts"): + hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) + + ## Trace gfx-asc interrupts + #node = hv.adt["/arm-io/gfx-asc"] + #for irq in getattr(node, "interrupts"): + #hv.trace_irq(f"{node.name} {irq}", irq, 1, hv.IRQTRACE_IRQ) + +trace_gpu_irqs() diff --git a/tools/proxyclient/hv/trace_all.py b/tools/proxyclient/hv/trace_all.py new file mode 100644 index 0000000..392dd21 --- /dev/null +++ b/tools/proxyclient/hv/trace_all.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT + +from m1n1.utils import irange + +# Map the entire MMIO range as traceable +for r in hv.adt["/arm-io"].ranges: + trace_range(irange(r.parent_addr, r.size), mode=TraceMode.ASYNC) + +# Skip some noisy devices +try: + trace_device("/arm-io/usb-drd0", False) +except KeyError: + pass +try: + trace_device("/arm-io/usb-drd1", False) +except KeyError: + pass +try: + trace_device("/arm-io/uart2", False) +except KeyError: + pass +trace_device("/arm-io/error-handler", False) +trace_device("/arm-io/aic", False) +trace_device("/arm-io/spi1", False) +trace_device("/arm-io/pmgr", False) diff --git a/tools/proxyclient/hv/trace_all_more.py b/tools/proxyclient/hv/trace_all_more.py new file mode 100644 index 0000000..7296075 --- /dev/null +++ b/tools/proxyclient/hv/trace_all_more.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: MIT + +from m1n1.utils import irange + +# Map the entire MMIO range as traceable +for r in hv.adt["/arm-io"].ranges: + trace_range(irange(r.parent_addr, r.size), mode=TraceMode.ASYNC) + +# Skip some noisy devices +try: + trace_device("/arm-io/usb-drd0", False) +except KeyError: + pass +try: + trace_device("/arm-io/usb-drd1", False) +except KeyError: + pass +try: + trace_device("/arm-io/uart2", False) +except KeyError: + pass +trace_device("/arm-io/aic", False) +trace_device("/arm-io/spi1", False) diff --git a/tools/proxyclient/hv/trace_aop.py b/tools/proxyclient/hv/trace_aop.py new file mode 100644 index 0000000..489c9b3 --- /dev/null +++ b/tools/proxyclient/hv/trace_aop.py @@ -0,0 +1,349 @@ +# SPDX-License-Identifier: MIT + +from m1n1.trace import Tracer +from m1n1.trace.dart import DARTTracer +from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR, EPContainer +from m1n1.utils import * +from m1n1.constructutils import * +from m1n1.fw.afk.rbep import * +from m1n1.fw.afk.epic import * +from m1n1.fw.aop import * +from m1n1.fw.aop.ipc import * + +import sys + +class AFKRingBufSniffer(AFKRingBuf): + def __init__(self, ep, state, base, size): + super().__init__(ep, base, size) + self.state = state + self.rptr = getattr(state, "rptr", 0) + + def update_rptr(self, rptr): + self.state.rptr = rptr + + def update_wptr(self): + raise NotImplementedError() + + def get_wptr(self): + return struct.unpack("<I", self.read_buf(2 * self.BLOCK_SIZE, 4))[0] + + def read_buf(self, off, size): + return self.ep.dart.ioread(0, self.base + off, size) + +class AFKEp(EP): + BASE_MESSAGE = AFKEPMessage + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.txbuf = None + self.rxbuf = None + self.state.txbuf = EPState() + self.state.rxbuf = EPState() + self.state.shmem_iova = None + self.state.txbuf_info = None + self.state.rxbuf_info = None + self.state.verbose = 1 + + def start(self): + self.create_bufs() + + def create_bufs(self): + if not self.state.shmem_iova: + return + if not self.txbuf and self.state.txbuf_info: + off, size = self.state.txbuf_info + self.txbuf = AFKRingBufSniffer(self, self.state.txbuf, + self.state.shmem_iova + off, size) + if not self.rxbuf and self.state.rxbuf_info: + off, size = self.state.rxbuf_info + self.rxbuf = AFKRingBufSniffer(self, self.state.rxbuf, + self.state.shmem_iova + off, size) + + Init = msg_log(0x80, DIR.TX) + Init_Ack = msg_log(0xa0, DIR.RX) + + GetBuf = msg_log(0x89, DIR.RX) + + Shutdown = msg_log(0xc0, DIR.TX) + Shutdown_Ack = msg_log(0xc1, DIR.RX) + + @msg(0xa1, DIR.TX, AFKEP_GetBuf_Ack) + def GetBuf_Ack(self, msg): + self.state.shmem_iova = msg.DVA + self.txbuf = None + self.rxbuf = None + self.state.txbuf = EPState() + self.state.rxbuf = EPState() + self.state.txbuf_info = None + self.state.rxbuf_info = None + + @msg(0xa2, DIR.TX, AFKEP_Send) + def Send(self, msg): + for data in self.txbuf.read(): + #if self.state.verbose >= 3: + if True: + self.log(f"===TX DATA=== epid={self.epid} rptr={self.txbuf.state.rptr:#x}") + chexdump(data) + self.log(f"===END DATA===") + self.log("Backtrace on TX data:") + self.hv.bt() + self.handle_ipc(data, dir=">") + return True + + Hello = msg_log(0xa3, DIR.TX) + + @msg(0x85, DIR.RX, AFKEPMessage) + def Recv(self, msg): + for data in self.rxbuf.read(): + #if self.state.verbose >= 3: + if True: + self.log(f"===RX DATA=== epid={self.epid} rptr={self.rxbuf.state.rptr:#x}") + chexdump(data) + self.log(f"===END DATA===") + self.handle_ipc(data, dir="<") + return True + + def handle_ipc(self, data, dir=None): + pass + + @msg(0x8a, DIR.RX, AFKEP_InitRB) + def InitTX(self, msg): + off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE + size = msg.SIZE * AFKRingBuf.BLOCK_SIZE + self.state.txbuf_info = (off, size) + self.create_bufs() + + @msg(0x8b, DIR.RX, AFKEP_InitRB) + def InitRX(self, msg): + off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE + size = msg.SIZE * AFKRingBuf.BLOCK_SIZE + self.state.rxbuf_info = (off, size) + self.create_bufs() + +class DummyAFKEp(AFKEp): + def handle_ipc(self, data, dir=None): + pass + +class EPICEp(AFKEp): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.pending_call = None + self.pending_cmd = None + + def handle_hello(self, hdr, sub, fd): + if sub.type != 0xc0: + return False + + payload = fd.read() + name = payload.split(b"\0")[0].decode("ascii") + self.log(f"Hello! (endpoint {name})") + return True + + def handle_notify(self, hdr, sub, fd): + for calltype in CALLTYPES: + if calltype.matches(hdr, sub): + call = calltype.from_stream(fd) + self.trace_call_early(call) + self.pending_call = call + return True + + return False + + def handle_reply(self, hdr, sub, fd): + if self.pending_call is None: + return False + + call = self.pending_call + call.read_resp(fd) + self.trace_call(call) + self.pending_call = None + return True + + def dispatch_ipc(self, dir, hdr, sub, fd): + if sub.category == EPICCategory.COMMAND: + return self.handle_notify(hdr, sub, fd) + if dir == "<" and sub.category == EPICCategory.REPORT: + return self.handle_hello(hdr, sub, fd) + if dir == ">" and sub.category == EPICCategory.NOTIFY: + return self.handle_notify(hdr, sub, fd) + if dir == "<" and sub.category == EPICCategory.REPLY: + return self.handle_reply(hdr, sub, fd) + + def handle_ipc(self, data, dir=None): + fd = BytesIO(data) + hdr = EPICHeader.parse_stream(fd) + sub = EPICSubHeaderVer2.parse_stream(fd) + + if not getattr(self, 'VERBOSE', False): + return + + self.log(f"{dir} 0x{hdr.channel:x} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") + self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Ts {sub.timestamp:#x}") + self.log(f" Unk1 {sub.unk1:#x} Unk2 {sub.unk2:#x}") + + if self.dispatch_ipc(dir, hdr, sub, fd): + return + + def trace_call_early(self, call): + # called at TX time + if isinstance(call, IndirectCall): + call.read_txbuf(self) + + def trace_call(self, call): + if isinstance(call, IndirectCall): + call.read_rxbuf(self) + call = call.unwrap() + call.dump(self.log) + +class SPUAppEp(EPICEp): + SHORT = "SPUApp" + +class AccelEp(EPICEp): + SHORT = "accel" + +class GyroEp(EPICEp): + SHORT = "gyro" + +class LASEp(EPICEp): + SHORT = "las" + +class WakeHintEp(EPICEp): + SHORT = "wakehint" + +class UNK26Ep(EPICEp): + SHORT = "unk26" + +class AudioEp(EPICEp): + SHORT = "aop-audio" + VERBOSE = True + +class VoiceTriggerEp(EPICEp): + SHORT = "aop-voicetrigger" + VERBOSE = True + + +class AOPTracer(ASCTracer, AOPBase): + ENDPOINTS = { + 0x20: SPUAppEp, + 0x21: AccelEp, + 0x22: GyroEp, + 0x24: LASEp, + 0x25: WakeHintEp, + 0x26: UNK26Ep, + 0x27: AudioEp, + 0x28: VoiceTriggerEp, + } + + def __init__(self, hv, devpath, verbose=False): + self.default_bootargs = None + super().__init__(hv, devpath, verbose) + self.u = hv.u + AOPBase.__init__(self, hv.u, self.dev) + + def start(self, *args): + self.default_bootargs = self.read_bootargs() + super().start(*args) + + def w_CPU_CONTROL(self, val): + if val.RUN: + self.bootargs = self.read_bootargs() + self.log("Bootargs patched by AP:") + self.default_bootargs.dump_diff(self.bootargs, self.log) + self.log("(End of list)") + super().w_CPU_CONTROL(val) + + @classmethod + def replay(cls, f, passthru=False): + epmap = dict() + epcont = EPContainer() + + class FakeASCTracer: + def __init__(self): + self.hv = None + + def log(self, str): + print(str) + asc_tracer = FakeASCTracer() + + for cls in cls.mro(): + eps = getattr(cls, "ENDPOINTS", None) + if eps is None: + break + for k, v in eps.items(): + if k in epmap: + continue + ep = v(asc_tracer, k) + epmap[k] = ep + if getattr(epcont, ep.name, None): + ep.name = f"{ep.name}{k:02x}" + setattr(epcont, ep.name, ep) + ep.start() + + def readdump(firstline, hdr, f): + l = firstline + assert hdr in l + postscribe = l[l.index(hdr) + len(hdr):] + annotation = dict([s.split("=") for s \ + in postscribe.strip().split(" ")]) + + dump = [] + for l in f: + if "===END DATA===" in l: + break + dump.append(l) + return chexundump("".join(dump)), annotation + + def read_txbuf(icall, ep): + hdr = "===COMMAND TX DATA===" + for l in f: + if hdr in l: + break + data, annot = readdump(l, hdr, f) + assert int(annot["addr"], 16) == icall.args.txbuf + icall.txbuf = data + def read_rxbuf(icall, ep): + hdr = "===COMMAND RX DATA===" + for l in f: + if hdr in l: + break + data, annot = readdump(l, hdr, f) + assert int(annot["addr"], 16) == icall.rets.rxbuf + icall.rxbuf = data + IndirectCall.read_rxbuf = read_rxbuf + IndirectCall.read_txbuf = read_txbuf + + for l in f: + if (rxhdr := "===RX DATA===") in l: + dir = "<" + hdr = rxhdr + elif (txhdr := "===TX DATA===") in l: + dir = ">" + hdr = txhdr + else: + if passthru: + print(l, end="") + continue + data, annot = readdump(l, hdr, f) + epid = int(annot["epid"]) + epmap[epid].handle_ipc(data, dir) + + +if __name__ == "__main__": + # We can replay traces by saving the textual output of live tracing + # and then passing it to this script. + with open(sys.argv[1]) as f: + AOPTracer.replay(f) + sys.exit(0) + +dart_aop_tracer = DARTTracer(hv, "/arm-io/dart-aop", verbose=4) +dart_aop_tracer.start() + +dart_aop_base = u.adt["/arm-io/dart-aop"].get_reg(0)[0] + +#hv.trace_range(irange(*u.adt["/arm-io/dart-aop"].get_reg(1))) +#hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(1))) +#hv.trace_range(irange(*u.adt["/arm-io/aop"].get_reg(3))) +#hv.trace_range(irange(*u.adt["/arm-io/admac-aop-audio"].get_reg(0))) + +aop_tracer = AOPTracer(hv, "/arm-io/aop", verbose=1) +aop_tracer.start(dart_aop_tracer.dart) diff --git a/tools/proxyclient/hv/trace_atc.py b/tools/proxyclient/hv/trace_atc.py new file mode 100644 index 0000000..f301803 --- /dev/null +++ b/tools/proxyclient/hv/trace_atc.py @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: MIT + +from m1n1.hv import TraceMode +from m1n1.hw.dwc3 import XhciRegs, Dwc3CoreRegs, PipehandlerRegs +from m1n1.hw.atc import Usb2PhyRegs, AtcPhyRegs +from m1n1.trace import ADTDevTracer +from m1n1.utils import * + + +class PhyTracer(ADTDevTracer): + REGMAPS = [ + Usb2PhyRegs, + None, + (AtcPhyRegs, 0x20000), + (AtcPhyRegs, 0x0), + (AtcPhyRegs, 0x2000), + (AtcPhyRegs, 0x2200), + (AtcPhyRegs, 0x2800), + (AtcPhyRegs, 0x2A00), + (AtcPhyRegs, 0x7000), + (AtcPhyRegs, 0xA00), + (AtcPhyRegs, 0x800), + (AtcPhyRegs, 0xD000), + (AtcPhyRegs, 0x14000), + (AtcPhyRegs, 0xC000), + (AtcPhyRegs, 0x13000), + (AtcPhyRegs, 0xB000), + (AtcPhyRegs, 0x12000), + (AtcPhyRegs, 0x9000), + (AtcPhyRegs, 0x10000), + (AtcPhyRegs, 0x1000), + (AtcPhyRegs, 0x50000), + (AtcPhyRegs, 0x50200), + (AtcPhyRegs, 0x54000), + None, + None, + None, + (AtcPhyRegs, 0xA000), + (AtcPhyRegs, 0x11000), + ] + + +class Dwc3VerboseTracer(ADTDevTracer): + REGMAPS = [XhciRegs, None, Dwc3CoreRegs, PipehandlerRegs] + NAMES = ["xhci", None, "dwc-core", "pipehandler"] + + +class Dwc3Tracer(ADTDevTracer): + REGMAPS = [None, None, Dwc3CoreRegs, PipehandlerRegs] + NAMES = [None, None, "dwc-core", "pipehandler"] + + +PhyTracer = PhyTracer._reloadcls() +Dwc3Tracer = Dwc3Tracer._reloadcls() +Dwc3VerboseTracer = Dwc3VerboseTracer._reloadcls() + +phy_tracer = PhyTracer(hv, "/arm-io/atc-phy1", verbose=2) +dwc3_tracer = Dwc3Tracer(hv, "/arm-io/usb-drd1", verbose=2) + +phy_tracer.start() +dwc3_tracer.start() diff --git a/tools/proxyclient/hv/trace_cd3217.py b/tools/proxyclient/hv/trace_cd3217.py new file mode 100644 index 0000000..cea79f3 --- /dev/null +++ b/tools/proxyclient/hv/trace_cd3217.py @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: MIT + +from enum import Enum + +from m1n1.trace.i2c import I2CTracer, I2CDevTracer + +class HpmTracer(I2CDevTracer): + class State(Enum): + UNKNOWN = 0 + REQUEST = 1 + WRITE = 2 + READ = 3 + + def __init__(self, addr=128, name=None, verbose=True): + print(f"CD3217Tracer.__init__(addr={addr}, name={name}, verbose={verbose})") + super().__init__(addr, name, verbose) + self.reset() + + def reset(self): + self.reg = None + self.state = CD3217Tracer.State.UNKNOWN + self.length = None + self.data = [] + + def start(self, addr, read): + if addr != self.addr: + return + + if self.state == CD3217Tracer.State.UNKNOWN: + if read: + self.state = CD3217Tracer.State.READ + else: + self.state = CD3217Tracer.State.REQUEST + elif self.state == CD3217Tracer.State.REQUEST and read: + pass + else: + self.log(f"unexpected state in start(read={read}): state:{self.state} reg:{self.reg} data:{self.data}") + + def stop(self): + if self.state == CD3217Tracer.State.REQUEST and len(self.data) == 0: + return + + msg = f"Txn: {self.addr:02x}." + if self.state == CD3217Tracer.State.REQUEST: + msg += f"r [{self.reg:02x}]" + elif self.state == CD3217Tracer.State.WRITE: + msg += f"w [{self.reg:02x}]" + elif self.state == CD3217Tracer.State.READ: + msg += f"r [xx]" + else: + self.log(f"unexpected state in stop(): state:{self.state} reg:{self.reg} data:{self.data}") + self.reset() + return + + # only for debugging as some mismatches are expected as + # cd3217 seems to report the register size and not the number + # of requested bytes (or I2CDevTracer truncates reads). + #if self.length is not None and self.length > len(self.data): + # self.log(f"length {self.length:02x} mismatch received data: {len(self.data):02x}") + + for data in self.data: + msg += f" {data:02x}" + + self.log(msg) + self.reset() + + def read(self, data): + self.data.append(data) + + def write(self, data): + if self.reg is None: + self.reg = data + elif self.length is None: + self.length = data + self.state = CD3217Tracer.State.WRITE + else: + self.data.append(data) + +class CD3217Tracer(HpmTracer): + def read(self, data): + if self.length is None: + self.length = data - 1 + else: + self.data.append(data) + + +i2c_tracers = {} + +for node in hv.adt["/arm-io"]: + if not node.name.startswith("i2c"): + continue + + n = int(node.name[3:]) + bus = I2CTracer(hv, f"/arm-io/{node.name}") + + for mngr_node in node: + if "compatible" not in mngr_node._properties: # thanks Apple + continue + + if mngr_node.compatible[0] != "usbc,manager": + continue + + addr = mngr_node.reg[0] & 0xff + bus.add_device(addr, HpmTracer(addr=addr, name=mngr_node.name)) + + for devnode in mngr_node: + + dcls = { + "usbc,cd3217": CD3217Tracer, + }.get(devnode.compatible[0], None) + if dcls: + addr = devnode.hpm_iic_addr & 0xff + bus.add_device(addr, dcls(addr=addr, name=devnode.name)) + + if len(bus.state.devices) > 1: + i2c_tracers[n] = bus + +for bus in i2c_tracers.values(): + bus.start() diff --git a/tools/proxyclient/hv/trace_codecs.py b/tools/proxyclient/hv/trace_codecs.py new file mode 100644 index 0000000..4cd9730 --- /dev/null +++ b/tools/proxyclient/hv/trace_codecs.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: MIT + +from m1n1.utils import RegMap +from m1n1.trace.i2c import I2CTracer, I2CRegMapTracer +from m1n1.hw.codecs import * + +hv.p.hv_set_time_stealing(0, 1) + +class SN012776Tracer(I2CRegMapTracer): + REGMAP = SN012776Regs + ADDRESSING = (1, 1) + +class TAS5770Tracer(I2CRegMapTracer): + REGMAP = TAS5770Regs + ADDRESSING = (1, 1) + +class CS42L84Tracer(I2CRegMapTracer): + REGMAP = CS42L84Regs + ADDRESSING = (0, 2) + +i2c_tracers = {} + +for node in hv.adt["/arm-io"]: + if not node.name.startswith("i2c"): + continue + + n = int(node.name[3:]) + i2c_tracers[n] = bus = I2CTracer(hv, f"/arm-io/{node.name}") + + for devnode in node: + if "compatible" not in devnode._properties: # thanks Apple + continue + + dcls = { + "audio-control,tas5770": TAS5770Tracer, + "audio-control,sn012776": SN012776Tracer, + "audio-control,cs42l84": CS42L84Tracer, + }.get(devnode.compatible[0], None) + if dcls: + bus.add_device( + devnode.reg[0] & 0xff, + dcls(name=devnode.name) + ) + + bus.start() diff --git a/tools/proxyclient/hv/trace_dart.py b/tools/proxyclient/hv/trace_dart.py new file mode 100644 index 0000000..d983145 --- /dev/null +++ b/tools/proxyclient/hv/trace_dart.py @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: MIT + +from m1n1.trace.dart import DARTTracer + +DARTTracer = DARTTracer._reloadcls() + +DEVICES = [ + "/arm-io/dart-pmp", + "/arm-io/dart-sep", + "/arm-io/dart-sio", + "/arm-io/dart-usb1", + "/arm-io/dart-disp0", + "/arm-io/dart-dcp", + "/arm-io/dart-dispext0", + "/arm-io/dart-dcpext", + "/arm-io/dart-scaler", +] + +dart_tracers = {} + +for i in DEVICES: + tracer = DARTTracer(hv, i, verbose=3) + tracer.start() + + dart_tracers[i.split("-")[-1]] = tracer + +del tracer diff --git a/tools/proxyclient/hv/trace_dcp.py b/tools/proxyclient/hv/trace_dcp.py new file mode 100644 index 0000000..367a990 --- /dev/null +++ b/tools/proxyclient/hv/trace_dcp.py @@ -0,0 +1,1113 @@ +# SPDX-License-Identifier: MIT + +import struct +from io import BytesIO + +from enum import IntEnum + +from m1n1.proxyutils import RegMonitor +from m1n1.utils import * +from m1n1.trace.dart import DARTTracer +from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR +from m1n1.fw.afk.rbep import * +from m1n1.fw.afk.epic import * + +if True: + dcp_adt_path = "/arm-io/dcp" + dcp_dart_adt_path = "/arm-io/dart-dcp" + dcp_dart_mapper_adt_path = "/arm-io/dart-dcp/mapper-dcp" + disp0_dart_adt_path = "/arm-io/dart-disp0" +else: + dcp_adt_path = "/arm-io/dcpext" + dcp_dart_adt_path = "/arm-io/dart-dcpext" + dcp_dart_mapper_adt_path = "/arm-io/dart-dcpext/mapper-dcpext" + disp0_dart_adt_path = "/arm-io/dart-dispext0" + +trace_device(dcp_adt_path, True, ranges=[1]) + +DARTTracer = DARTTracer._reloadcls() +ASCTracer = ASCTracer._reloadcls() + +iomon = RegMonitor(hv.u, ascii=True) + +class AFKRingBufSniffer(AFKRingBuf): + def __init__(self, ep, state, base, size): + super().__init__(ep, base, size) + self.state = state + self.rptr = getattr(state, "rptr", 0) + + def update_rptr(self, rptr): + self.state.rptr = rptr + + def update_wptr(self): + raise NotImplementedError() + + def get_wptr(self): + return struct.unpack("<I", self.read_buf(2 * self.BLOCK_SIZE, 4))[0] + + def read_buf(self, off, size): + return self.ep.dart.ioread(self.ep.stream, self.base + off, size) + +class AFKEp(EP): + BASE_MESSAGE = AFKEPMessage + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.txbuf = None + self.rxbuf = None + self.state.txbuf = EPState() + self.state.rxbuf = EPState() + self.state.shmem_iova = None + self.state.txbuf_info = None + self.state.rxbuf_info = None + self.state.verbose = 1 + + def start(self): + #self.add_mon() + self.create_bufs() + + def create_bufs(self): + if not self.state.shmem_iova: + return + if not self.txbuf and self.state.txbuf_info: + off, size = self.state.txbuf_info + self.txbuf = AFKRingBufSniffer(self, self.state.txbuf, + self.state.shmem_iova + off, size) + if not self.rxbuf and self.state.rxbuf_info: + off, size = self.state.rxbuf_info + self.rxbuf = AFKRingBufSniffer(self, self.state.rxbuf, + self.state.shmem_iova + off, size) + + def add_mon(self): + if self.state.shmem_iova: + iomon.add(self.state.shmem_iova, 32768, + name=f"{self.name}.shmem@{self.state.shmem_iova:08x}", offset=0) + + Init = msg_log(0x80, DIR.TX) + Init_Ack = msg_log(0xa0, DIR.RX) + + GetBuf = msg_log(0x89, DIR.RX) + + Shutdown = msg_log(0xc0, DIR.TX) + Shutdown_Ack = msg_log(0xc1, DIR.RX) + + @msg(0xa1, DIR.TX, AFKEP_GetBuf_Ack) + def GetBuf_Ack(self, msg): + self.state.shmem_iova = msg.DVA + self.txbuf = None + self.rxbuf = None + self.state.txbuf = EPState() + self.state.rxbuf = EPState() + self.state.txbuf_info = None + self.state.rxbuf_info = None + #self.add_mon() + + @msg(0xa2, DIR.TX, AFKEP_Send) + def Send(self, msg): + for data in self.txbuf.read(): + if self.state.verbose >= 3: + self.log(f">TX rptr={self.txbuf.state.rptr:#x}") + chexdump(data, print_fn=self.log) + self.handle_ipc(data, dir=">") + return True + + Hello = msg_log(0xa3, DIR.TX) + + @msg(0x85, DIR.RX, AFKEPMessage) + def Recv(self, msg): + for data in self.rxbuf.read(): + if self.state.verbose >= 3: + self.log(f"<RX rptr={self.rxbuf.state.rptr:#x}") + chexdump(data, print_fn=self.log) + self.handle_ipc(data, dir="<") + return True + + def handle_ipc(self, data, dir=None): + pass + + @msg(0x8a, DIR.RX, AFKEP_InitRB) + def InitTX(self, msg): + off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE + size = msg.SIZE * AFKRingBuf.BLOCK_SIZE + self.state.txbuf_info = (off, size) + self.create_bufs() + + @msg(0x8b, DIR.RX, AFKEP_InitRB) + def InitRX(self, msg): + off = msg.OFFSET * AFKRingBuf.BLOCK_SIZE + size = msg.SIZE * AFKRingBuf.BLOCK_SIZE + self.state.rxbuf_info = (off, size) + self.create_bufs() + +class SilentEp(AFKEp): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.state.verbose = 0 + + def log(self, msg): + pass + +def epic_service_cmd(group, cmd): + def f(x): + x.is_cmd = True + x.group = group + x.cmd = cmd + return x + return f + +def epic_service_reply(group, cmd): + def f(x): + x.is_reply = True + x.group = group + x.cmd = cmd + return x + return f + +class EPICServiceTracer(Reloadable): + def __init__(self, tracer, ep, key): + self.tracer = tracer + self.ep = ep + self.key = key + + self.cmdmap = {} + self.replymap = {} + for name in dir(self): + i = getattr(self, name) + if not callable(i): + continue + if getattr(i, "is_cmd", False): + self.cmdmap[i.group, i.cmd] = getattr(self, name) + if getattr(i, "is_reply", False): + self.replymap[i.group, i.cmd] = getattr(self, name) + + def log(self, msg): + self.ep.log(f"[{self.key}] {msg}") + + def init(self, props): + pass + + def handle_cmd(self, sgroup, scmd, sdata): + cmdfn = self.cmdmap.get((sgroup, scmd), None) + if cmdfn: + cmdfn(sdata) + else: + self.log(f"> unknown group {sgroup}; command {scmd}") + if sdata: + chexdump(sdata, print_fn=self.log) + + def handle_reply(self, sgroup, scmd, sdata): + replyfn = self.replymap.get((sgroup, scmd), None) + if replyfn: + replyfn(sdata) + else: + self.log(f"< unknown group {sgroup}; command {scmd}") + if sdata: + chexdump(sdata, print_fn=self.log) + + @epic_service_cmd(4, 4) + def getLocation(self, data): + self.log("> getLocation") + @epic_service_reply(4, 4) + def getLocation_reply(self, data): + self.log("< getLocation") + + @epic_service_cmd(4, 5) + def getUnit(self, data): + self.log("> getUnit") + @epic_service_reply(4, 5) + def getUnit_reply(self, data): + self.log("< getUnit") + + @epic_service_cmd(4, 6) + def open(self, data): + self.log("> open") + @epic_service_reply(4, 6) + def open_reply(self, data): + self.log("< open") + + @epic_service_cmd(4, 7) + def close(self, data): + self.log("> close") + @epic_service_reply(4, 7) + def close_reply(self, data): + self.log("< close") + +class EPICEp(AFKEp): + SERVICES = [] + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + + self.serv_map = {} + self.chan_map = {} + self.serv_names = {} + for i in self.SERVICES: + self.serv_names[i.NAME] = i + + def handle_ipc(self, data, dir=None): + fd = BytesIO(data) + hdr = EPICHeader.parse_stream(fd) + sub = EPICSubHeader.parse_stream(fd) + + if sub.category == EPICCategory.REPORT: + self.handle_report(hdr, sub, fd) + if sub.category == EPICCategory.NOTIFY: + self.handle_notify(hdr, sub, fd) + elif sub.category == EPICCategory.REPLY: + self.handle_reply(hdr, sub, fd) + elif sub.category == EPICCategory.COMMAND: + self.handle_cmd(hdr, sub, fd) + else: + self.log(f"{dir}Ch {hdr.channel} Type {hdr.type} Ver {hdr.version} Tag {hdr.seq}") + self.log(f" Len {sub.length} Ver {sub.version} Cat {sub.category} Type {sub.type:#x} Seq {sub.seq}") + chexdump(data, print_fn=self.log) + + def handle_report_init(self, hdr, sub, fd): + init = EPICAnnounce.parse_stream(fd) + self.log(f"Init: {init.name}") + self.log(f" Props: {init.props}") + + if not init.props: + init.props = {} + + name = init.props.get("EPICName", init.name) + key = name + str(init.props.get("EPICUnit", "")) + self.log(f"New service: {key} on channel {hdr.channel}") + + srv_cls = self.serv_names.get(name, EPICServiceTracer) + srv = srv_cls(self.tracer, self, key) + srv.init(init.props) + srv.chan = hdr.channel + self.chan_map[hdr.channel] = srv + self.serv_map[key] = srv + + def handle_report(self, hdr, sub, fd): + if sub.type == 0x30: + self.handle_report_init(hdr, sub, fd) + else: + self.log(f"Report {sub.type:#x}") + chexdump(fd.read(), print_fn=self.log) + + def handle_notify(self, hdr, sub, fd): + self.log(f"Notify:") + chexdump(fd.read(), print_fn=self.log) + + def handle_reply(self, hdr, sub, fd): + if sub.inline_len: + payload = fd.read() + self.log("Inline payload:") + chexdump(payload, print_fn=self.log) + else: + cmd = EPICCmd.parse_stream(fd) + if not cmd.rxbuf: + self.log(f"Response {sub.type:#x}: {cmd.retcode:#x}") + return + + data = self.dart.ioread(self.stream, cmd.rxbuf, cmd.rxlen) + rgroup, rcmd, rlen, rmagic = struct.unpack("<2xHIII", data[:16]) + if rmagic != 0x69706378: + self.log("Warning: Invalid EPICStandardService response magic") + + srv = self.chan_map.get(hdr.channel, None) + if srv: + srv.handle_reply(rgroup, rcmd, data[64:64+rlen] if rlen else None) + else: + self.log(f"[???] < group {rgroup} command {rcmd}") + chexdump(data[64:64+rlen], print_fn=lambda msg: self.log(f"[???] {msg}")) + + def handle_cmd(self, hdr, sub, fd): + cmd = EPICCmd.parse_stream(fd) + payload = fd.read() + + if sub.type == 0xc0 and cmd.txbuf: + data = self.dart.ioread(self.stream, cmd.txbuf, cmd.txlen) + sgroup, scmd, slen, sfooter = struct.unpack("<2xHIII48x", data[:64]) + sdata = data[64:64+slen] if slen else None + + srv = self.chan_map.get(hdr.channel, None) + if srv: + srv.handle_cmd(sgroup, scmd, sdata) + else: + self.log(f"[???] > group {sgroup} command {scmd}") + chexdump(data[64:64+slen], print_fn=lambda msg: self.log(f"[???] {msg}")) + else: + self.log(f"Command {sub.type:#x}: {cmd.retcode:#x}") + if payload: + chexdump(payload, print_fn=self.log) + if cmd.txbuf: + self.log(f"TX buf @ {cmd.txbuf:#x} ({cmd.txlen:#x} bytes):") + chexdump(self.dart.ioread(self.stream, cmd.txbuf, cmd.txlen), print_fn=self.log) + +KNOWN_MSGS = { + "A000": "IOMFB::UPPipeAP_H13P::late_init_signal()", + "A001": "IOMFB::UPPipeAP_H13P::init_ipa(unsigned long long, unsigned long)", + "A002": "IOMFB::UPPipeAP_H13P::alss_supported()", + "A003": "IOMFB::UPPipeAP_H13P::reset_dsc()", + "A004": "IOMFB::UPPipeAP_H13P::display_edr_factor_changed(float)", + "A005": "IOMFB::UPPipeAP_H13P::set_contrast(float)", + "A006": "IOMFB::UPPipeAP_H13P::set_csc_mode(IOMFB_CSCMode)", + "A007": "IOMFB::UPPipeAP_H13P::set_op_mode(IOMFB_CSCMode)", + "A008": "IOMFB::UPPipeAP_H13P::set_op_gamma_mode(IOMFB_TFMode)", + "A009": "IOMFB::UPPipeAP_H13P::set_video_out_mode(IOMFB_Output_Mode)", + "A010": "IOMFB::UPPipeAP_H13P::set_meta_allowed(bool)", + "A011": "IOMFB::UPPipeAP_H13P::set_tunneled_color_mode(bool)", + "A012": "IOMFB::UPPipeAP_H13P::set_bwr_line_time_us(double)", + "A013": "IOMFB::UPPipeAP_H13P::performance_feedback(double)", + "A014": "IOMFB::UPPipeAP_H13P::notify_swap_complete(unsigned int)", + "A015": "IOMFB::UPPipeAP_H13P::is_run_mode_change_pending() const", + "A016": "IOMFB::UPPipeAP_H13P::ready_for_run_mode_change(IOMFB::AppleRegisterStream*)", + "A017": "IOMFB::UPPipeAP_H13P::set_thermal_throttle_cap(unsigned int)", + "A018": "IOMFB::UPPipeAP_H13P::emergency_shutdown_normal_mode(IOMFB::AppleRegisterStream*)", + "A019": "IOMFB::UPPipeAP_H13P::set_target_run_mode(IOMFB::AppleRegisterStream*)", + "A020": "IOMFB::UPPipeAP_H13P::rt_bandwidth_setup()", + "A021": "IOMFB::UPPipeAP_H13P::rt_bandwidth_update(IOMFB::AppleRegisterStream*, float, float, bool, bool)", + "A022": "IOMFB::UPPipeAP_H13P::rt_bandwidth_update_downgrade(IOMFB::AppleRegisterStream*)", + "A023": "IOMFB::UPPipeAP_H13P::rt_bandwidth_write_update(IOMFB::AppleRegisterStream*, RealtimeBandwithWritebackBlock, bool)", + "A024": "IOMFB::UPPipeAP_H13P::cif_blending_eco_present()", + "A025": "IOMFB::UPPipeAP_H13P::request_bic_update()", + "A026": "IOMFB::UPPipeAP_H13P::early_power_off_warning(IOMFB::AppleRegisterStream*)", + "A027": "IOMFB::UPPipeAP_H13P::get_max_frame_size(unsigned int*, unsigned int*)", + "A028": "IOMFB::UPPipeAP_H13P::shadow_FIFO_empty(IOMFB::AppleRegisterStream*) const", + "A029": "IOMFB::UPPipeAP_H13P::setup_video_limits()", + "A030": "IOMFB::UPPipeAP_H13P::can_program_swap() const", + "A031": "IOMFB::UPPipeAP_H13P::in_auto_mode() const", + "A032": "IOMFB::UPPipeAP_H13P::push_black_frame(IOMFB::AppleRegisterStream*)", + "A033": "IOMFB::UPPipeAP_H13P::read_crc(Notify_Info_Index, unsigned int)", + "A034": "IOMFB::UPPipeAP_H13P::update_notify_clients_dcp(unsigned int const*)", + "A035": "IOMFB::UPPipeAP_H13P::is_hilo() const", + "A036": "IOMFB::UPPipeAP_H13P::apt_supported() const", + "A037": "IOMFB::UPPipeAP_H13P::get_dfb_info(unsigned int*, unsigned long long*, unsigned int*)", + "A038": "IOMFB::UPPipeAP_H13P::get_dfb_compression_info(unsigned int*)", + "A039": "IOMFB::UPPipeAP_H13P::get_frame_done_time() const", + "A040": "IOMFB::UPPipeAP_H13P::get_performance_headroom() const", + "A041": "IOMFB::UPPipeAP_H13P::are_stats_active() const", + "A042": "IOMFB::UPPipeAP_H13P::supports_odd_h_blanking() const", + "A043": "IOMFB::UPPipeAP_H13P::is_first_hw_version() const", + "A044": "IOMFB::UPPipeAP_H13P::set_blendout_CSC_mode()", + + "A100": "IOMFB::UPPipe2::get_gamma_table_gated(IOMFBGammaTable*)", + "A101": "IOMFB::UPPipe2::set_gamma_table_gated(IOMFBGammaTable const*)", + "A102": "IOMFB::UPPipe2::test_control(IOMFB_TC_Cmd, unsigned int)", + "A103": "IOMFB::UPPipe2::get_config_frame_size(unsigned int*, unsigned int*) const", + "A104": "IOMFB::UPPipe2::set_config_frame_size(unsigned int, unsigned int) const", + "A105": "IOMFB::UPPipe2::program_config_frame_size() const", + "A106": "IOMFB::UPPipe2::read_blend_crc() const", + "A107": "IOMFB::UPPipe2::read_config_crc() const", + "A108": "IOMFB::UPPipe2::disable_wpc_calibration(bool)", + "A109": "IOMFB::UPPipe2::vftg_is_running(IOMFB::AppleRegisterStream*) const", + "A110": "IOMFB::UPPipe2::vftg_debug(IOMFB::AppleRegisterStream*, unsigned int) const", + "A111": "IOMFB::UPPipe2::vftg_set_color_channels(unsigned int, unsigned int, unsigned int)", + "A112": "IOMFB::UPPipe2::set_color_filter_scale(int)", + "A113": "IOMFB::UPPipe2::set_corner_temps(int const*)", + "A114": "IOMFB::UPPipe2::reset_aot_enabled() const", + "A115": "IOMFB::UPPipe2::aot_enabled() const", + "A116": "IOMFB::UPPipe2::aot_active() const", + "A117": "IOMFB::UPPipe2::set_timings_enabled(IOMFB::AppleRegisterStream*, bool)", + "A118": "IOMFB::UPPipe2::get_frame_size(IOMFB::AppleRegisterStream*, unsigned int*, unsigned int*)", + "A119": "IOMFB::UPPipe2::set_block(unsigned long long, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long, bool)", + "A121": "IOMFB::UPPipe2::get_buf_block(unsigned long long, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long, bool)", + "A122": "IOMFB::UPPipe2::get_matrix(IOMFB_MatrixLocation, IOMFBColorFixedMatrix*) const", + "A123": "IOMFB::UPPipe2::set_matrix(IOMFB_MatrixLocation, IOMFBColorFixedMatrix const*)", + "A124": "IOMFB::UPPipe2::get_internal_timing_attributes_gated(IOMFB::RefreshTimingAttributes*) const", + "A125": "IOMFB::UPPipe2::display_edr_factor_changed(float)", + "A126": "IOMFB::UPPipe2::set_contrast(float)", + "A127": "IOMFB::UPPipe2::p3_to_disp_cs(float const*, float const (*) [2])", + "A128": "IOMFB::UPPipe2::max_panel_brightness() const", + "A129": "IOMFB::UPPipe2::swap_flush_stream_replay(IOMFB::AppleRegisterStream*)", + "A130": "IOMFB::UPPipe2::init_ca_pmu()", + "A131": "IOMFB::UPPipe2::pmu_service_matched()", + "A132": "IOMFB::UPPipe2::backlight_service_matched()", + + "A200": "IOMFB::PropRelay::setBool(IOMFB::RuntimeProperty, bool)", + "A201": "IOMFB::PropRelay::setInt(IOMFB::RuntimeProperty, unsigned int)", + "A202": "IOMFB::PropRelay::setFx(IOMFB::RuntimeProperty, int)", + "A203": "IOMFB::PropRelay::setPropDynamic(IOMFB::RuntimeProperty, unsigned int)", + "A204": "IOMFB::PropRelay::getBool(IOMFB::RuntimeProperty)", + "A205": "IOMFB::PropRelay::getInt(IOMFB::RuntimeProperty)", + "A206": "IOMFB::PropRelay::getFx(IOMFB::RuntimeProperty)", + + "A350": "UnifiedPipeline2::displayHeight()", + "A351": "UnifiedPipeline2::displayWidth()", + "A352": "UnifiedPipeline2::applyProperty(unsigned int, unsigned int)", + "A353": "UnifiedPipeline2::get_system_type() const", + "A354": "UnifiedPipeline2::headless() const", + "A355": "UnifiedPipeline2::export_idle_method(unsigned int)", + "A357": "UnifiedPipeline2::set_create_DFB()", + "A358": "UnifiedPipeline2::vi_set_temperature_hint()", + + "A400": "IOMobileFramebufferAP::free_signal()", + "A401": "IOMobileFramebufferAP::start_signal()", + "A402": "IOMobileFramebufferAP::stop_signal()", + "A403": "IOMobileFramebufferAP::systemWillShutdown()", + "A404": "IOMobileFramebufferAP::swap_begin()", + "A405": "IOMobileFramebufferAP::rotate_surface(unsigned int, unsigned int, unsigned int)", + "A406": "IOMobileFramebufferAP::get_framebuffer_id()", + "A407": "IOMobileFramebufferAP::swap_start(unsigned int*, IOUserClient*)", + "A408": "IOMobileFramebufferAP::swap_submit_dcp(IOMFBSwapRec const*, IOSurface**, unsigned int const*, bool, double, unsigned int, bool*)", + "A409": "IOMobileFramebufferAP::swap_signal(unsigned int, unsigned int)", + "A410": "IOMobileFramebufferAP::set_display_device(unsigned int)", + "A411": "IOMobileFramebufferAP::is_main_display() const", + "A412": "IOMobileFramebufferAP::set_digital_out_mode(unsigned int, unsigned int)", + "A413": "IOMobileFramebufferAP::get_digital_out_state(unsigned int*)", + "A414": "IOMobileFramebufferAP::get_display_area(DisplayArea*)", + "A415": "IOMobileFramebufferAP::set_tvout_mode(unsigned int)", + "A416": "IOMobileFramebufferAP::set_tvout_signaltype(unsigned int)", + "A417": "IOMobileFramebufferAP::set_wss_info(unsigned int, unsigned int)", + "A418": "IOMobileFramebufferAP::set_content_flags(unsigned int)", + "A419": "IOMobileFramebufferAP::get_gamma_table(IOMFBGammaTable*)", + "A420": "IOMobileFramebufferAP::set_gamma_table(IOMFBGammaTable*)", + "A421": "IOMobileFramebufferAP::get_matrix(unsigned int, unsigned long long (*) [3][3]) const", + "A422": "IOMobileFramebufferAP::set_matrix(unsigned int, unsigned long long const (*) [3][3])", + "A423": "IOMobileFramebufferAP::set_contrast(float*)", + "A424": "IOMobileFramebufferAP::set_white_on_black_mode(unsigned int)", + "A425": "IOMobileFramebufferAP::set_color_remap_mode(DisplayColorRemapMode)", + "A426": "IOMobileFramebufferAP::get_color_remap_mode(DisplayColorRemapMode*) const", + "A427": "IOMobileFramebufferAP::setBrightnessCorrection(unsigned int)", + "A428": "IOMobileFramebufferAP::temp_queue_swap_cancel(unsigned int)", + "A429": "IOMobileFramebufferAP::swap_cancel(unsigned int)", + "A430": "IOMobileFramebufferAP::swap_cancel_all_dcp(unsigned long long)", + "A431": "IOMobileFramebufferAP::surface_is_replaceable(unsigned int, bool*)", + "A432": "IOMobileFramebufferAP::kernel_tests(IOMFBKernelTestsArguments*)", + "A433": "IOMobileFramebufferAP::splc_set_brightness(unsigned int)", + "A434": "IOMobileFramebufferAP::splc_get_brightness(unsigned int*)", + "A435": "IOMobileFramebufferAP::set_block_dcp(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long)", + "A436": "IOMobileFramebufferAP::get_block_dcp(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char*, unsigned long) const", + "A438": "IOMobileFramebufferAP::swap_set_color_matrix(IOMFBColorFixedMatrix*, IOMFBColorMatrixFunction, unsigned int)", + "A439": "IOMobileFramebufferAP::set_parameter_dcp(IOMFBParameterName, unsigned long long const*, unsigned int)", + "A440": "IOMobileFramebufferAP::display_width() const", + "A441": "IOMobileFramebufferAP::display_height() const", + "A442": "IOMobileFramebufferAP::get_display_size(unsigned int*, unsigned int*) const", + "A443": "IOMobileFramebufferAP::do_create_default_frame_buffer() const", + "A444": "IOMobileFramebufferAP::printRegs()", + "A445": "IOMobileFramebufferAP::enable_disable_dithering(unsigned int)", + "A446": "IOMobileFramebufferAP::set_underrun_color(unsigned int)", + "A447": "IOMobileFramebufferAP::enable_disable_video_power_savings(unsigned int)", + "A448": "IOMobileFramebufferAP::set_video_dac_gain(unsigned int)", + "A449": "IOMobileFramebufferAP::set_line21_data(unsigned int)", + "A450": "IOMobileFramebufferAP::enableInternalToExternalMirroring(bool)", + "A451": "IOMobileFramebufferAP::getExternalMirroringCapability(IOMFBMirroringCapability*)", + "A452": "IOMobileFramebufferAP::setRenderingAngle(float*)", + "A453": "IOMobileFramebufferAP::setOverscanSafeRegion(IOMFBOverscanSafeRect*)", + "A454": "IOMobileFramebufferAP::first_client_open()", + "A455": "IOMobileFramebufferAP::last_client_close_dcp(unsigned int*)", + "A456": "IOMobileFramebufferAP::writeDebugInfo(unsigned long)", + "A457": "IOMobileFramebufferAP::flush_debug_flags(unsigned int)", + "A458": "IOMobileFramebufferAP::io_fence_notify(unsigned int, unsigned int, unsigned long long, IOMFBStatus)", + "A459": "IOMobileFramebufferAP::swap_wait_dcp(bool, unsigned int, unsigned int, unsigned int)", + "A460": "IOMobileFramebufferAP::setDisplayRefreshProperties()", + "A461": "IOMobileFramebufferAP::exportProperty(unsigned int, unsigned int)", + "A462": "IOMobileFramebufferAP::applyProperty(unsigned int, unsigned int)", + "A463": "IOMobileFramebufferAP::flush_supportsPower(bool)", + "A464": "IOMobileFramebufferAP::abort_swaps_dcp(IOMobileFramebufferUserClient*)", + "A465": "IOMobileFramebufferAP::swap_signal_gated(unsigned int, unsigned int)", + "A466": "IOMobileFramebufferAP::update_dfb(IOSurface*, unsigned int, unsigned int, unsigned long long)", + "A467": "IOMobileFramebufferAP::update_dfb(IOSurface*)", + "A468": "IOMobileFramebufferAP::setPowerState(unsigned long, bool, unsigned int*)", + "A469": "IOMobileFramebufferAP::isKeepOnScreen() const", + "A470": "IOMobileFramebufferAP::resetStats()", + "A471": "IOMobileFramebufferAP::set_has_frame_swap_function(bool)", + "A472": "IOMobileFramebufferAP::getPerformanceStats(unsigned int*, unsigned int*)", + + "D000": "bool IOMFB::UPPipeAP_H13P::did_boot_signal()", + "D001": "bool IOMFB::UPPipeAP_H13P::did_power_on_signal()", + "D002": "void IOMFB::UPPipeAP_H13P::will_power_off_signal()", + "D003": "void IOMFB::UPPipeAP_H13P::rt_bandwidth_setup_ap(inout rt_bw_config_t*)", + "D004": "void IOMFB::UPPipeAP_H13P::mcc_report_replay(bool, unsigned int)", + "D005": "void IOMFB::UPPipeAP_H13P::mcc_report_bics(bool, unsigned int)", + + "D100": "void UnifiedPipeline2::match_pmu_service()", + #"D101": "", # get some uint32_t, inlined + "D102": "void UnifiedPipeline2::set_number_property(char const*, unsigned int)", + "D103": "void UnifiedPipeline2::set_boolean_property(char const*, bool)", + "D104": "void UnifiedPipeline2::set_string_property(char const*, char const*)", + "D105": "IOReturn IOService::acknowledgeSetPowerState()", + "D106": "void IORegistryEntry::removeProperty(char const*)", + "D107": "bool UnifiedPipeline2::create_provider_service", + "D108": "bool UnifiedPipeline2::create_product_service()", + "D109": "bool UnifiedPipeline2::create_PMU_service()", + "D110": "bool UnifiedPipeline2::create_iomfb_service()", + "D111": "bool UnifiedPipeline2::create_backlight_service()", + "D112": "void UnifiedPipeline2::set_idle_caching_state_ap(IdleCachingState, unsigned int)", + "D113": "bool UnifiedPipeline2::upload_trace_start(IOMFB::FrameInfoBuffer::FrameInfoConstants const*)", + "D114": "bool UnifiedPipeline2::upload_trace_chunk(IOMFB::FrameInfoBuffer::FrameInfoData const*, unsigned int, unsigned int)", + "D115": "bool UnifiedPipeline2::upload_trace_end(char const*)", + "D116": "bool UnifiedPipeline2::start_hardware_boot()", + "D117": "bool UnifiedPipeline2::is_dark_boot()", + "D118": "bool UnifiedPipeline2::is_waking_from_hibernate()", + "D119": "bool UnifiedPipeline2::detect_fastsim()", + "D120": "bool UnifiedPipeline2::read_edt_data(char const*, unsigned int, unsigned int*) const", + "D121": "bool UnifiedPipeline2::read_edt_string(char const*, char*, unsigned int*) const", + "D122": "bool UnifiedPipeline2::setDCPAVPropStart(unsigned int)", + "D123": "bool UnifiedPipeline2::setDCPAVPropChunk(unsigned char const*, unsigned int, unsigned int)", + "D124": "bool UnifiedPipeline2::setDCPAVPropEnd(char const*)", + + "D200": "uint64_t IOMFB::UPPipe2::get_default_idle_caching_method()", + "D201": "IOMFBStatus IOMFB::UPPipe2::map_buf(IOMFB::BufferDescriptor*, unsigned long*, unsigned long long*, bool)", + "D202": "void IOMFB::UPPipe2::unmap_buf(IOMFB::BufferDescriptor*, unsigned long, unsigned long long, bool)", + "D203": "bool IOMFB::UPPipe2::aot_supported_peek()", + "D204": "uint64_t IOMFB::UPPipe2::get_ideal_screen_space()", + "D205": "bool IOMFB::UPPipe2::read_carveout(unsigned char*, unsigned int, unsigned int) const", + "D206": "bool IOMFB::UPPipe2::match_pmu_service()", + "D207": "bool IOMFB::UPPipe2::match_backlight_service()", + "D208": "uint64_ IOMFB::UPPipe2::get_calendar_time_ms()", + "D209": "void IOMFB::UPPipe2::plc_enable(bool)", + "D210": "void IOMFB::UPPipe2::plc_init()", + "D211": "void IOMFB::UPPipe2::update_backlight_factor_prop(int)", + + "D300": "void IOMFB::PropRelay::publish(IOMFB::RuntimeProperty, unsigned int)", + + "D400": "void IOMFB::ServiceRelay::get_property(unsigned int, in char const[0x40], out unsigned char[0x200], inout unsigned int*)", + "D401": "bool IOMFB::ServiceRelay::get_uint_prop(unsigned int, in char const[0x40], inout unsigned long long*)", + "D402": "void IOMFB::ServiceRelay::set_uint_prop(unsigned int, in char const[0x40], unsigned long long)", + "D403": "bool IOMFB::ServiceRelay::get_uint_prop(unsigned int, in char const[0x40], inout unsigned int*)", + "D404": "void IOMFB::ServiceRelay::set_uint_prop(unsigned int, in char const[0x40], unsigned int)", + "D405": "bool IOMFB::ServiceRelay::get_fx_prop(unsigned int, in char const[0x40], inout int*)", + "D406": "void IOMFB::ServiceRelay::set_fx_prop(unsigned int, in char const[0x40], int)", + "D407": "void IOMFB::ServiceRelay::set_bool_prop(unsigned int, in char const[0x40], bool)", + "D408": "unsigned long long IOMFB::ServiceRelay::getClockFrequency(unsigned int, unsigned int)", + "D409": "IOMFBStatus IOMFB::ServiceRelay::enableDeviceClock(unsigned int, unsigned int, unsigned int)", + "D410": "IOMFBStatus IOMFB::ServiceRelay::enableDevicePower(unsigned int, unsigned int, inout unsigned int*, unsigned int)", + "D411": "IOMFBStatus IOMFB::ServiceRelay::mapDeviceMemoryWithIndex(unsigned int, unsigned int, unsigned int, inout unsigned long*, inout unsigned long long*)", + "D412": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSArray const*)", + "D413": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSDictionary const*)", + "D414": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSNumber const*)", + "D415": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSBoolean const*)", + "D416": "bool IOMFB::ServiceRelay::setProperty(unsigned int, OSString<0x40> const*, OSString const*)", + "D417": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSArray const*)", + "D418": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSDictionary const*)", + "D419": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSNumber const*)", + "D420": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSBoolean const*)", + "D421": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], OSString const*)", + "D422": "bool IOMFB::ServiceRelay::setProperty(unsigned int, char const[0x40], bool)", + "D423": "void IOMFB::ServiceRelay::removeProperty(unsigned int, char const[0x40])", + "D424": "void IOMFB::ServiceRelay::removeProperty(unsigned int, OSString<0x40> const*)", + + "D450": "bool IOMFB::MemDescRelay::from_id(unsigned int, unsigned long*, unsigned long*, unsigned long long*)", + "D451": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::allocate_buffer(unsigned int, unsigned long long, unsigned int, unsigned long*, unsigned long*, unsigned long long*)", + "D452": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::map_physical(unsigned long long, unsigned long long, unsigned int, unsigned long*, unsigned long long*)", + "D453": "MemDescRelay::desc_id_t IOMFB::MemDescRelay::withAddressRange(unsigned long long, unsigned long long, unsigned int, task*, unsigned long*, unsigned long long*)", + "D454": "IOMFBStatus IOMFB::MemDescRelay::prepare(unsigned int, unsigned int)", + "D455": "IOMFBStatus IOMFB::MemDescRelay::complete(unsigned int, unsigned int)", + "D456": "bool IOMFB::MemDescRelay::release_descriptor(unsigned int)", + + "D500": "IOMFBStatus IOMFB::PlatformFunctionRelay::allocate_record(unsigned int, char const*, unsigned int, bool)", + "D501": "IOMFBStatus IOMFB::PlatformFunctionRelay::release_record(unsigned int)", + "D502": "IOMFBStatus IOMFB::PlatformFunctionRelay::callFunctionLink(unsigned int, unsigned long, unsigned long, unsigned long)", + + "D550": "bool IORegistryEntry::setProperty(OSString *, OSArray *)", + "D551": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKArray *)", + "D552": "bool IORegistryEntry::setProperty(OSString *, OSDictionary *)", + "D553": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKDictionary *)", + "D554": "bool IORegistryEntry::setProperty(OSString *, OSNumber *)", + "D555": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKNumber *)", + "D556": "bool IORegistryEntry::setProperty(OSString *, OSBoolean *)", + "D557": "bool IORegistryEntry::setProperty(OSString *, OSString *)", + "D558": "bool IORegistryEntry::setProperty(OSString *, IOMFB::AFKString *)", + "D559": "bool IORegistryEntry::setProperty(char const*, OSArray *)", + "D560": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKArray *)", + "D561": "bool IORegistryEntry::setProperty(char const*, OSDictionary *)", + "D562": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKDictionary *)", + "D563": "bool IORegistryEntry::setProperty(char const*, OSNumber *)", + "D564": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKNumber *)", + "D565": "bool IORegistryEntry::setProperty(char const*, OSBoolean *)", + "D566": "bool IORegistryEntry::setProperty(char const*, OSString *)", + "D567": "bool IORegistryEntry::setProperty(char const*, IOMFB::AFKString *)", + "D568": "bool IORegistryEntry::setProperty(char const*, char const*)", + "D569": "bool IORegistryEntry::setProperty(char const*, bool)", + "D570": "IOMFBStatus IOMobileFramebufferAP::setProperties(OSDictionary*)", + "D571": "void IOMobileFramebufferAP::swapping_client_did_start(IOMobileFramebufferUserClient*)", + "D572": "void IOMobileFramebufferAP::swapping_client_will_stop(IOMobileFramebufferUserClient*)", + "D573": "IOMFBStatus IOMobileFramebufferAP::set_canvas_size(unsigned int, unsigned int)", + "D574": "IOMFBStatus IOMobileFramebufferAP::powerUpDART(bool)", + "D575": "IOMFBStatus IOMobileFramebufferAP::get_dot_pitch(unsigned int*)", + "D576": "void IOMobileFramebufferAP::hotPlug_notify_gated(unsigned long long)", + "D577": "void IOMobileFramebufferAP::powerstate_notify(bool, bool)", + "D578": "bool IOMobileFramebufferAP::idle_fence_create(IdleCachingState)", + "D579": "void IOMobileFramebufferAP::idle_fence_complete()", + "D580": "void IOMobileFramebufferAP::idle_surface_release_ap()", + "D581": "void IOMobileFramebufferAP::swap_complete_head_of_line(unsigned int, bool, unsigned int, bool)", + "D582": "bool IOMobileFramebufferAP::create_default_fb_surface(unsigned int, unsigned int)", + "D583": "bool IOMobileFramebufferAP::serializeDebugInfoCb(unsigned long, IOMFB::BufferDescriptor const*, unsigned int)", + "D584": "void IOMobileFramebufferAP::clear_default_surface()", + "D585": "void IOMobileFramebufferAP::swap_notify_gated(unsigned long long, unsigned long long, unsigned long long)", + "D586": "void IOMobileFramebufferAP::swap_info_notify_dispatch(SwapInfoBlob const*)", + "D587": "void IOMFBStatus IOMobileFramebufferAP::updateBufferMappingCount_gated(bool)", + "D588": "void IOMobileFramebufferAP::resize_default_fb_surface_gated()", + "D589": "void IOMobileFramebufferAP::swap_complete_ap_gated(unsigned int, bool, SwapCompleteData const*, SwapInfoBlob const*, unsigned int)", + "D590": "void IOMobileFramebufferAP::batched_swap_complete_ap_gated(unsigned int*, unsigned int, bool, bool const*, SwapCompleteData const*)", + "D591": "void IOMobileFramebufferAP::swap_complete_intent_gated(unsigned int, bool, IOMFBSwapRequestIntent, unsigned int, unsigned int)", + "D592": "void IOMobileFramebufferAP::abort_swap_ap_gated(unsigned int)", + "D593": "void IOMobileFramebufferAP::enable_backlight_message_ap_gated(bool)", + "D594": "void IOMobileFramebufferAP::setSystemConsoleMode(bool)", + "D595": "void IOMobileFramebufferAP::setSystemConsoleMode_gated(bool)", + "D596": "bool IOMobileFramebufferAP::isDFBAllocated()", + "D597": "bool IOMobileFramebufferAP::preserveContents()", + "D598": "void IOMobileFramebufferAP::find_swap_function_gated()", + + "D700": "int IOMFB::DCPPowerManager::set_kernel_power_assert(bool, bool)", +} + +# iboot interface +""" +0: setResource +1: setSurface +2: setPower +3: getHpdStatus +4: getTimingModes +5: getColorModes +6: setMode +7: setBrightness +8: rwBCONRegsRequest +9: setParameter +10: setMatrix +11: setProperty +12: getProperty +13: setBlock +14: getBlock +15: swapBegin +16: setSwapLayer +17: setSwapTimestamp +18: setSwapEnd +19: setSwapWait +20: setBrightnessCfg +21: setNamedProperty +22: getNamedProperty +""" + +from m1n1.fw.dcp.dcpep import DCPMessage, DCPEp_SetShmem, CallContext, DCPEp_Msg + +class DCPCallState: + pass + +class DCPCallChannel(Reloadable): + def __init__(self, dcpep, name, buf, bufsize): + self.dcpep = dcpep + self.name = name + self.buf = buf + self.bufsize = bufsize + self.log = self.dcpep.log + self.state = self.dcpep.state + + def call(self, msg, dir): + ident = f"{self.name}.{msg.OFF:x}" + + if any(msg.OFF == s.off for s in self.state.ch.get(self.name, [])): + self.log(f"{dir}{self.name}.{msg.OFF:x} !!! Overlapping call ({msg})") + assert False + + state = DCPCallState() + + data = self.dcpep.dart.ioread(self.dcpep.stream, self.state.shmem_iova + self.buf + msg.OFF, msg.LEN) + tag = data[:4][::-1].decode("ascii") + in_len, out_len = struct.unpack("<II", data[4:12]) + data_in = data[12:12 + in_len] + + state.off = msg.OFF + state.tag = tag + state.in_len = in_len + state.out_addr = self.buf + msg.OFF + 12 + in_len + state.out_len = out_len + + verb = self.dcpep.get_verbosity(tag) + if verb >= 1: + self.log(f"{dir}{self.name}.{msg.OFF:x} {tag}:{KNOWN_MSGS.get(tag, 'unk')} ({msg})") + if verb >= 2: + print(f"Message: {tag} ({KNOWN_MSGS.get(tag, 'unk')}): (in {in_len:#x}, out {out_len:#x})") + if data_in: + print(f"{dir} Input ({len(data_in):#x} bytes):") + chexdump(data_in[:self.state.max_len]) + + #if tag not in KNOWN_MSGS: + #hv.run_shell() + + if self.state.dumpfile: + dump = f"CALL {dir} {msg.value:#018x} {self.name} {state.off:#x} {state.tag} {in_len:#x} {out_len:#x} {data_in.hex()}\n" + self.state.dumpfile.write(dump) + self.state.dumpfile.flush() + + self.state.ch.setdefault(self.name, []).append(state) + + def ack(self, msg, dir): + assert msg.LEN == 0 + + states = self.state.ch.get(self.name, None) + if not states: + self.log(f"{dir}ACK {self.name}.{msg.OFF:x} !!! ACK without call ({msg})") + return + + state = states[-1] + + if self.state.show_acks: + self.log(f"{dir}ACK {self.name}.{msg.OFF:x} ({msg})") + + data_out = self.dcpep.dart.ioread(self.dcpep.stream, self.state.shmem_iova + state.out_addr, state.out_len) + + verb = self.dcpep.get_verbosity(state.tag) + if verb >= 3 and state.out_len > 0: + print(f"{dir}{self.name}.{msg.OFF:x} Output buffer ({len(data_out):#x} bytes):") + chexdump(data_out[:self.state.max_len]) + + if self.state.dumpfile: + dump = f"ACK {dir} {msg.value:#018x} {self.name} {state.off:#x} {data_out.hex()}\n" + self.state.dumpfile.write(dump) + self.state.dumpfile.flush() + + states.pop() + +class DCPEp(EP): + BASE_MESSAGE = DCPMessage + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.state.shmem_iova = None + self.state.show_globals = True + self.state.show_acks = True + self.state.max_len = 1024 * 1024 + self.state.verbosity = 3 + self.state.op_verb = {} + self.state.ch = {} + self.state.dumpfile = None + + self.ch_cb = DCPCallChannel(self, "CB", 0x60000, 0x8000) + self.ch_cmd = DCPCallChannel(self, "CMD", 0, 0x8000) + self.ch_async = DCPCallChannel(self, "ASYNC", 0x40000, 0x20000) + self.ch_oobcb = DCPCallChannel(self, "OOBCB", 0x68000, 0x8000) + self.ch_oobcmd = DCPCallChannel(self, "OOBCMD", 0x8000, 0x8000) + + self.cmd_ch = { + CallContext.CB: self.ch_cmd, + CallContext.CMD: self.ch_cmd, + CallContext.ASYNC: None, # unknown + CallContext.OOBCB: self.ch_oobcmd, + CallContext.OOBCMD: self.ch_oobcmd, + } + + self.cb_ch = { + CallContext.CB: self.ch_cb, + CallContext.CMD: None, + CallContext.ASYNC: self.ch_async, + CallContext.OOBCB: self.ch_oobcb, + CallContext.OOBCMD: None, + } + + def start(self): + self.add_mon() + + def add_mon(self): + if self.state.shmem_iova and self.state.show_globals: + addr = self.state.shmem_iova + 0x80000 + iomon.add(addr, 128, + name=f"{self.name}.shmem@{addr:08x}", offset=addr) + + #addr = self.state.shmem_iova + #iomon.add(addr, 0x80080, + #name=f"{self.name}.shmem@{addr:08x}", offset=addr) + + InitComplete = msg_log(1, DIR.RX) + + @msg(0, DIR.TX, DCPEp_SetShmem) + def SetShmem(self, msg): + self.log(f"Shared memory DVA: {msg.DVA:#x}") + self.state.shmem_iova = msg.DVA & 0xfffffffff + self.add_mon() + + @msg(2, DIR.TX, DCPEp_Msg) + def Tx(self, msg): + if msg.ACK: + self.cb_ch[msg.CTX].ack(msg, ">") + else: + self.cmd_ch[msg.CTX].call(msg, ">") + + if self.state.show_globals: + iomon.poll() + + return True + + @msg(2, DIR.RX, DCPEp_Msg) + def Rx(self, msg): + self.log(msg) + if msg.ACK: + self.cmd_ch[msg.CTX].ack(msg, "<") + else: + self.cb_ch[msg.CTX].call(msg, "<") + + if self.state.show_globals: + iomon.poll() + + return True + + def get_verbosity(self, tag): + return self.state.op_verb.get(tag, self.state.verbosity) + + def set_verb_known(self, verb): + for i in KNOWN_MSGS: + if verb is None: + self.state.op_verb.pop(i, None) + else: + self.state.op_verb[i] = verb + +class SystemService(EPICEp): + NAME = "system" + +class TestService(EPICEp): + NAME = "test" + +class DCPExpertService(EPICEp): + NAME = "dcpexpert" + +class Disp0Service(EPICEp): + NAME = "disp0" + +class DCPAVControllerEpicTracer(EPICServiceTracer): + NAME = "dcpav-controller-epic" + + @epic_service_cmd(0, 14) + def getParticipatesPowerManagement(self, data): + self.log("> getParticipatesPowerManagement") + @epic_service_reply(0, 14) + def getParticipatesPowerManagement_reply(self, data): + self.log("< getParticipatesPowerManagement") + chexdump(data, print_fn=self.log) + +class DPAVController(EPICEp): + NAME = "dpavctrl" + SERVICES = [ + DCPAVControllerEpicTracer + ] + +class DPSACService(EPICEp): + NAME = "dpsac" + + +class DCPDPDeviceEpicTracer(EPICServiceTracer): + NAME = "dcpdp-device-epic" + + @epic_service_cmd(0, 15) + def getDeviceMatchingData(self, data): + self.log("> getDeviceMatchingData") + @epic_service_reply(0, 15) + def getDeviceMatchingData_reply(self, data): + self.log("< getDeviceMatchingData") + chexdump(data, print_fn=self.log) + +class DPDevService(EPICEp): + NAME = "dpdev" + SERVICES = [ + DCPDPDeviceEpicTracer + ] + +class DPAVService(EPICEp): + NAME = "dpavserv" + +class DCPAVAudioInterfaceEpicTracer(EPICServiceTracer): + NAME = "dcpav-audio-interface-epic" + + # usually 4, 6 but apparently also 0, 6 here? + # or maybe a different open? + @epic_service_cmd(0, 6) + def open2(self, data): + self.log("> open") + @epic_service_reply(0, 6) + def open2_reply(self, data): + self.log("< open") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(0, 8) + def prepareLink(self, data): + self.log("> prepareLink") + @epic_service_reply(0, 8) + def prepareLink_reply(self, data): + self.log("< prepareLink") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(0, 9) + def startLink(self, data): + self.log("> startLink") + @epic_service_reply(0, 9) + def startLink_reply(self, data): + self.log("< startLink") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(0, 15) + def getLinkStatus(self, data): + self.log("> getLinkStatus") + @epic_service_reply(0, 15) + def getLinkStatus_reply(self, data): + self.log("< getLinkStatus") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(0, 16) + def getTransport(self, data): + self.log("> getTransport") + @epic_service_reply(0, 16) + def getTransport_reply(self, data): + self.log("< getTransport") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(0, 17) + def getPortID(self, data): + self.log("> getPortID") + @epic_service_reply(0, 17) + def getPortID_reply(self, data): + self.log("< getPortID") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(1, 18) + def getElements(self, data): + self.log("> getElements") + @epic_service_reply(1, 18) + def getElements_reply(self, data): + self.log("< getElements") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(1, 20) + def getProductAttributes(self, data): + self.log("> getProductAttributes") + @epic_service_reply(1, 20) + def getProductAttributes_reply(self, data): + self.log("< getProductAttributes") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(1, 21) + def getEDIDUUID(self, data): + self.log("> getEDIDUUID") + @epic_service_reply(1, 21) + def getEDIDUUID_reply(self, data): + self.log("< getEDIDUUID") + chexdump(data, print_fn=self.log) + + @epic_service_cmd(0, 22) + def getDataLatency(self, data): + self.log("> getDataLatency") + @epic_service_reply(0, 22) + def getDataLatency_reply(self, data): + self.log("< getDataLatency") + chexdump(data, print_fn=self.log) + + +class AVService(EPICEp): + NAME = "av" + SERVICES = [ + DCPAVAudioInterfaceEpicTracer + ] + +class DCPDPTXHDCPAuthSessionTracer(EPICServiceTracer): + NAME = "dcpdptx-hdcp-auth-session" + + @epic_service_cmd(4, 8) + def getProtocol(self, data): + self.log("> getProtocol") + @epic_service_reply(4, 8) + def getProtocol_reply(self, data): + self.log("< getProtocol") + chexdump(data, print_fn=self.log) + +class HDCPService(EPICEp): + NAME = "hdcp" + SERVICES = [ + DCPDPTXHDCPAuthSessionTracer + ] + +class RemoteAllocService(EPICEp): + NAME = "remotealloc" + +class DCPDPTXRemotePortTarget(Register32): + CORE = 3, 0 + ATC = 7, 4 + DIE = 11, 8 + CONNECTED = 15, 15 + +class DCPDPTXPortEpicTracer(EPICServiceTracer): + NAME = "dcpdptx-port-epic" + + @epic_service_cmd(0, 8) + def setPowerState(self, data): + self.log("> setPowerState") + @epic_service_reply(0, 8) + def setPowerState_reply(self, data): + self.log("< setPowerState") + + @epic_service_cmd(0, 13) + def connectTo(self, data): + unk1, target = struct.unpack("<II24x", data) + target = DCPDPTXRemotePortTarget(target) + self.log(f"> connectTo(target={target}, unk1=0x{unk1:x})") + @epic_service_reply(0, 13) + def connectTo_reply(self, data): + unk1, target = struct.unpack("<II24x", data) + target = DCPDPTXRemotePortTarget(target) + self.log(f"< connectTo(target={target}, unk1=0x{unk1:x})") + + @epic_service_cmd(0, 14) + def validateConnection(self, data): + unk1, target = struct.unpack("<II40x", data) + target = DCPDPTXRemotePortTarget(target) + self.log(f"> validateConnection(target={target}, unk1=0x{unk1:x})") + @epic_service_reply(0, 14) + def validateConnection_reply(self, data): + unk1, target = struct.unpack("<II40x", data) + target = DCPDPTXRemotePortTarget(target) + self.log(f"< validateConnection(target={target}, unk1=0x{unk1:x})") + + @epic_service_cmd(8, 10) + def hotPlugDetectChangeOccurred(self, data): + unk = struct.unpack("<16x?15x", data)[0] + self.log(f"> hotPlugDetectChangeOccurred(unk={unk})") + @epic_service_reply(8, 10) + def hotPlugDetectChangeOccurred_reply(self, data): + unk = struct.unpack("<16x?15x", data)[0] + self.log(f"< hotPlugDetectChangeOccurred(unk={unk})") + +class DPTXPortService(EPICEp): + NAME = "dptxport" + SERVICES = [ + DCPDPTXPortEpicTracer + ] + +class DCPTracer(ASCTracer): + ENDPOINTS = { + 0x20: SystemService, + 0x21: TestService, + 0x22: DCPExpertService, + # Disp0 / DCP iboot as used by m1n1 is incompatible with the generic + # EPICEp tracer, disable it for now + #0x23: Disp0Service, + 0x24: DPAVController, + 0x25: EPICEp, # dcpav-power-ep + 0x26: DPSACService, + 0x27: DPDevService, + 0x28: DPAVService, + 0x29: AVService, + 0x2a: DPTXPortService, # dcpdptx-port-ep + 0x2b: HDCPService, + 0x2c: EPICEp, # cb-ap-to-dcp-service-ep + 0x2d: RemoteAllocService, + 0x37: DCPEp, # iomfb-link + } + + def handle_msg(self, direction, r0, r1): + super().handle_msg(direction, r0, r1) + #iomon.poll() + + +dcp_sid = u.adt[dcp_dart_mapper_adt_path].reg + +dart_dcp_tracer = DARTTracer(hv, dcp_dart_adt_path) +dart_dcp_tracer.start() + +dart_disp0_tracer = DARTTracer(hv, disp0_dart_adt_path) +dart_disp0_tracer.start() + +def readmem_iova(addr, size, readfn): + try: + return dart_dcp_tracer.dart.ioread(dcp_sid, addr, size) + except Exception as e: + print(e) + return None + +iomon.readmem = readmem_iova + +dcp_tracer = DCPTracer(hv, dcp_adt_path, verbose=1) +dcp_tracer.start(dart_dcp_tracer.dart, stream=dcp_sid) + +#dcp_tracer.ep.dcpep.state.dumpfile = open("dcp.log", "a") diff --git a/tools/proxyclient/hv/trace_gpio.py b/tools/proxyclient/hv/trace_gpio.py new file mode 100644 index 0000000..4560bf0 --- /dev/null +++ b/tools/proxyclient/hv/trace_gpio.py @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: MIT + +from m1n1.trace.gpio import GPIOTracer + +#trace_device("/arm-io/gpio", True) + +# trace gpio interrups, useful to follow the cascaded interrupts +aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") +try: + node = hv.adt["/arm-io/gpio0"] + path = "/arm-io/gpio0" +except: + node = hv.adt["/arm-io/gpio"] + path = "/arm-io/gpio" + +if getattr(node, "interrupt-parent") == aic_phandle: + for irq in getattr(node, "interrupts"): + hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) + +PIN_NAMES_j274 = { + 0xC0: "i2c0:scl", + 0xBC: "i2c0:sda", + 0xC9: "i2c1:scl", + 0xC7: "i2c1:sda", + 0xA3: "i2c2:scl", + 0xA2: "i2c2:sda", + 106: "hpm:irq", + 136: "bluetooth:irq", + 196: "wlan:irq", + 183: "cs42l83:irq", + 182: "tas5770:irq", + 152: "pci@0,0", + 153: "pci@1,0", + 33: "pci@2,0", + #0x2D: "spi_nor:CS", +} + +GPIOTracer = GPIOTracer._reloadcls() +gpio_tracer = GPIOTracer(hv, path, PIN_NAMES_j274, verbose=0) +gpio_tracer.start() diff --git a/tools/proxyclient/hv/trace_i2c.py b/tools/proxyclient/hv/trace_i2c.py new file mode 100644 index 0000000..f64fd24 --- /dev/null +++ b/tools/proxyclient/hv/trace_i2c.py @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: MIT + +from m1n1.trace.i2c import I2CTracer + +I2CTracer = I2CTracer._reloadcls() + +i2c_tracers = {} + +for node in hv.adt["/arm-io"]: + if node.name.startswith("i2c"): + n = int(node.name[3:]) + i2c_tracers[n] = I2CTracer(hv, f"/arm-io/i2c{n}", verbose=0) + i2c_tracers[n].stop() + i2c_tracers[n].start() + if hv.ctx: + for irq in getattr(node, "interrupts"): + hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) + +from m1n1.gpiola import GPIOLogicAnalyzer + +if not hv.started: + for cpu in list(hv.adt["cpus"]): + if cpu.name == "cpu3": + print(f"Removing ADT node {cpu._path}") + del hv.adt["cpus"][cpu.name] + +if not hv.started or hv.ctx is not None: + m = GPIOLogicAnalyzer(u, "arm-io/gpio", + pins={"scl": 0xc9, "sda": 0xc7}, + div=1, on_pin_change=True, cpu=3) + + m.load_regmap(list(i2c_tracers[1].regmaps.values())[0], + regs={"SMSTA", "XFSTA"}) + +def start_la(): + m.start(1000000, bufsize=0x80000) + hv.cont() + +def stop_la(): + m.complete() + m.show() diff --git a/tools/proxyclient/hv/trace_isp.py b/tools/proxyclient/hv/trace_isp.py new file mode 100644 index 0000000..b930095 --- /dev/null +++ b/tools/proxyclient/hv/trace_isp.py @@ -0,0 +1,5 @@ +from m1n1.trace.isp import ISPTracer + +hv.log('ISP: Registering ISP ASC tracer...') +isp_asc_tracer = ISPTracer(hv, "/arm-io/isp", "/arm-io/dart-isp", verbose=4) +isp_asc_tracer.start()
\ No newline at end of file diff --git a/tools/proxyclient/hv/trace_keyboard.py b/tools/proxyclient/hv/trace_keyboard.py new file mode 100644 index 0000000..d8cae66 --- /dev/null +++ b/tools/proxyclient/hv/trace_keyboard.py @@ -0,0 +1,206 @@ +# SPDX-License-Identifier: MIT + +import struct +from construct import * + +from m1n1.hv import TraceMode +from m1n1.proxyutils import RegMonitor +from m1n1.utils import * + +from m1n1.trace import ADTDevTracer +from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR +from m1n1.trace.dart import DARTTracer +from m1n1.trace.gpio import GPIOTracer +from m1n1.trace.spi import SPITracer + +DARTTracer = DARTTracer._reloadcls() +ASCTracer = ASCTracer._reloadcls() +GPIOTracer = GPIOTracer._reloadcls() +SPITracer = SPITracer._reloadcls() + +# SPI HID transport tracer for 2021 macbook models + +kbd_node = None +for node in hv.adt.walk_tree(): + try: + if node.compatible[0] == "spi-1,spimc": + for c in node: + try: + if c.compatible[0] == "hid-transport,spi": + kbd_node = c + break + except AttributeError: + continue + except AttributeError: + continue + if kbd_node is not None: + break + +# trace interrupts +aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") +spi_node = kbd_node._parent + +if getattr(spi_node, "interrupt-parent") == aic_phandle: + for irq in getattr(spi_node, "interrupts"): + hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) + +spi_pins = { + 0x37: "spi3_cs", + 0xc2: "ipd_en", +} +spi_pins_nub = { + 0x6: "ipd_irq", +} + +SPI_HID_PKT = Struct( + "flags" / Int8ub, + "dev" / Int8ub, + "offset" / Int16ul, + "remain" / Int16ul, + "length" / Int16ul, + "data" / Bytes(246), + "crc16" / Int16ul +) + +#gpio_tracer = GPIOTracer(hv, "/arm-io/gpio0", spi_pins, verbose=0) +#gpio_tracer.start() + +#gpio_tracer_nub = GPIOTracer(hv, "/arm-io/nub-gpio0", spi_pins_nub, verbose=0) +#gpio_tracer_nub.start() + +dart_sio_tracer = DARTTracer(hv, "/arm-io/dart-sio", verbose=1) +dart_sio_tracer.start() + +iomon = RegMonitor(hv.u, ascii=True) + +def readmem_iova(addr, size): + try: + return dart_sio_tracer.dart.ioread(0, addr, size) + except Exception as e: + print(e) + return None + +iomon.readmem = readmem_iova + +class SIOMessage(Register64): + EP = 7, 0 # matches device's ADT dma-channels, j314c spi3 0x1a and 0x1b + TAG = 13, 8 # counts for ipd spi transfers from 0x02 to 0x20 + TYPE = 23, 16 # OP + PARAM = 31, 24 + DATA = 63, 32 + +class SIOStart(SIOMessage): + TYPE = 23, 16, Constant(2) + +class SIOSetup(SIOMessage): + TYPE = 23, 16, Constant(3) + +class SIOConfig(SIOMessage): #??? + TYPE = 23, 16, Constant(5) + +class SIOAck(SIOMessage): + TYPE = 23, 16, Constant(0x65) + +class SIOSetupIO(SIOMessage): + TYPE = 23, 16, Constant(6) + +class SIOCompleteIO(SIOMessage): + TYPE = 23, 16, Constant(0x68) + +class SIOEp(EP): + BASE_MESSAGE = SIOMessage + SHORT = "sioep" + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.state.iova = None + self.state.iova_cfg = None + self.state.dumpfile = None + + @msg(2, DIR.TX, SIOStart) + def Start(self, msg): + self.log("Start SIO") + + @msg(3, DIR.TX, SIOSetup) + def m_Setup(self, msg): + iomon.poll() + if msg.EP == 0 and msg.PARAM == 0x1: + self.state.iova = msg.DATA << 12 + elif msg.EP == 0 and msg.PARAM == 0x2: + # size for PARAM == 0x1? + if self.state.iova is not None and self.tracer.verbose > 1: + iomon.add(self.state.iova, msg.DATA * 8, name=f"sio.shmem@{self.state.iova:08x}", + offset=self.state.iova) + elif msg.EP == 0 and msg.PARAM == 0xb: + # second iova block, maybe config + self.state.iova_cfg = msg.DATA << 12 + elif msg.EP == 0 and msg.PARAM == 0xc: + # size for PARAM == 0xb? + if self.state.iova is not None and self.tracer.verbose > 1: + iomon.add(self.state.iova_cfg, msg.DATA * 8, + name=f"sio.shmem@{self.state.iova_cfg:08x}", offset=self.state.iova_cfg) + + @msg(0x65, DIR.RX, SIOAck) + def m_Ack(self, msg): + iomon.poll() + + @msg(6, DIR.TX, SIOSetupIO) + def m_SetupIO(self, msg): + iomon.poll() + if self.state.iova is None: + return + if msg.EP == 0x1a: + buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xa8, 4))[0] + size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xb0, 4))[0] + self.log_spihid_pkt("SPI3 TX", self.tracer.ioread(buf, size)) + + @msg(0x68, DIR.RX, SIOCompleteIO) + def m_CompleteIO(self, msg): + iomon.poll() + if self.state.iova is None: + return + if msg.EP == 0x1b: + buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x48, 4))[0] + size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x50, 4))[0] + self.log_spihid_pkt("SPI3 RX", self.tracer.ioread(buf, size)) + + def log_spihid_pkt(self, label, data): + if len(data) != 256: + self.log(f"{label}: unexpected data length: {len(data):d}") + chexdump(data) + return + + crc16 = crc16USB(0, data[:254]) + pkt = SPI_HID_PKT.parse(data) + #self.log(f"pkt.crc16:{pkt.crc16:#04x} crc16:{crc16:#04x}") + if pkt.length == 0: + return + + if pkt.flags == 0x80 and pkt.dev == 0x11 and pkt.length == 849 and pkt.offset == 256 and pkt.remain == 834 and crc16 == 0x1489: + return + if pkt.crc16 != crc16: + self.log(f"{label}: CRC mismatch: pkt.crc16:{pkt.crc16:#04x} crc16:{crc16:#04x}") + chexdump(data) + return + self.log(f"{label}: flags:{pkt.flags:#2x} dev:{pkt.dev:#2x} length:{pkt.length:4d} offset:{pkt.offset:3d} remain:{pkt.remain:3d}") + chexdump(pkt.data[:min(246, pkt.length)]) + + if self.state.dumpfile: + dump = f"{label}: flags:{pkt.flags:#2x} dev:{pkt.dev:#2x} length:{pkt.length:4d} {pkt.data[:min(246, pkt.length)].hex()}\n" + self.state.dumpfile.write(dump) + self.state.dumpfile.flush() + + +class SIOTracer(ASCTracer): + ENDPOINTS = { + 0x20: SIOEp + } + + +sio_tracer = SIOTracer(hv, "/arm-io/sio", verbose=False) +sio_tracer.start(dart_sio_tracer.dart) + +sio_tracer.ep.sioep.state.dumpfile = open("spi_hid.log", "a") + +spi_tracer = SPITracer(hv, "/arm-io/" + spi_node.name, verbose=1) +spi_tracer.start() diff --git a/tools/proxyclient/hv/trace_mesa.py b/tools/proxyclient/hv/trace_mesa.py new file mode 100644 index 0000000..37fb32f --- /dev/null +++ b/tools/proxyclient/hv/trace_mesa.py @@ -0,0 +1,212 @@ +# SPDX-License-Identifier: MIT +""" +Things to note: + The command buffer is encrypted after the poweron sequence, and I + can't find the key in the SEP using sven's old SEP tracer. +""" +import struct +from construct import * + +from m1n1.hv import TraceMode +from m1n1.proxyutils import RegMonitor +from m1n1.utils import * + +from m1n1.trace import ADTDevTracer +from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR +from m1n1.trace.dart import DARTTracer +from m1n1.trace.gpio import GPIOTracer +from m1n1.trace.spi import SPITracer + +DARTTracer = DARTTracer._reloadcls() +ASCTracer = ASCTracer._reloadcls() +GPIOTracer = GPIOTracer._reloadcls() +SPITracer = SPITracer._reloadcls() + +mesa_node = None +for node in hv.adt.walk_tree(): + try: + if node.compatible[0] == "spi-1,spimc": + for c in node: + try: + if c.compatible[0] == "biosensor,mesa": + mesa_node = c + break + except AttributeError: + continue + except AttributeError: + continue + if mesa_node is not None: + break + +# trace interrupts +aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") +spi_node = mesa_node._parent + +if getattr(spi_node, "interrupt-parent") == aic_phandle: + for irq in getattr(spi_node, "interrupts"): + hv.trace_irq("/arm-io/" + spi_node.name, irq, 1, hv.IRQTRACE_IRQ) + +if getattr(mesa_node, "interrupt-parent") == aic_phandle: + for irq in getattr(mesa_node, "interrupts"): + hv.trace_irq("/arm-io/" + mesa_node.name, irq, 1, hv.IRQTRACE_IRQ) + +mesa_pins = { + 0xc4: "mesa_pwr", + +} + + +gpio_tracer = GPIOTracer(hv, "/arm-io/gpio0", mesa_pins, verbose=1) +gpio_tracer.start() + +dart_sio_tracer = DARTTracer(hv, "/arm-io/dart-sio", verbose=1) +dart_sio_tracer.start() + +iomon = RegMonitor(hv.u, ascii=True) + +def readmem_iova(addr, size, readfn=None): + try: + return dart_sio_tracer.dart.ioread(0, addr, size) + except Exception as e: + print(e) + return None + +iomon.readmem = readmem_iova + + + +class SIOMessage(Register64): + EP = 7, 0 # SPI2 DMA channels 0x18, 0x19 + TAG = 13, 8 # counts up, message ID? + TYPE = 23, 16 # SIO message type + PARAM = 31, 24 + DATA = 63, 32 + +class SIOStart(SIOMessage): + TYPE = 23, 16, Constant(2) + +class SIOSetup(SIOMessage): + TYPE = 23, 16, Constant(3) + +class SIOConfig(SIOMessage): #??? + TYPE = 23, 16, Constant(5) + +class SIOAck(SIOMessage): + TYPE = 23, 16, Constant(0x65) + +class SIOSetupIO(SIOMessage): + TYPE = 23, 16, Constant(6) + +class SIOCompleteIO(SIOMessage): + TYPE = 23, 16, Constant(0x68) + +class SIOEp(EP): + BASE_MESSAGE = SIOMessage + SHORT = "sioep" + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.state.iova = None + self.state.iova_cfg = None + self.state.iova_unk = None + self.state.dumpfile = None + + @msg(2, DIR.TX, SIOStart) + def Start(self, msg): + self.log("Start SIO") + + @msg(3, DIR.TX, SIOSetup) + def m_Setup(self, msg): + if msg.EP == 0 and msg.PARAM == 0x1: + self.state.iova = msg.DATA << 12 + + elif msg.EP == 0 and msg.PARAM == 0x2: + # size for PARAM == 0x1? + iomon.add(self.state.iova, msg.DATA * 8, + name=f"SIO IOVA region at 0x{self.state.iova:08x}", + offset=self.state.iova) + + #elif msg.EP == 0 and msg.PARAM == 0xb: + ## second iova block, maybe config + #self.state.iova_cfg = msg.DATA << 12 + + #elif msg.EP == 0 and msg.PARAM == 0xc: + ## size for PARAM == 0xb? + #iomon.add(self.state.iova_cfg, msg.DATA * 8, + #name=f"SIO IOVA CFG region at 0x{self.state.iova_cfg:08x}", + #offset=self.state.iova_cfg) + + if msg.EP == 0 and msg.PARAM == 0xd: + # possible fingerprint sensor IOVA region + self.state.iova_unk = msg.DATA << 12 + + elif msg.EP == 0 and msg.PARAM == 0xe: + iomon.add(self.state.iova_unk, msg.DATA * 8, + name=f"SIO IOVA UNK region at {self.state.iova_unk:08x}", + offset=self.state.iova_unk) + + @msg(5, DIR.TX, SIOConfig) + def m_Config(self, msg): + return + + @msg(0x65, DIR.RX, SIOAck) + def m_Ack(self, msg): + return + + @msg(6, DIR.TX, SIOSetupIO) + def m_SetupIO(self, msg): + if msg.EP == 0x18 or 0x19: + iomon.poll() + return + + @msg(0x68, DIR.RX, SIOCompleteIO) + def m_CompleteIO(self, msg): + if msg.EP == 0x18: + if self.state.iova is None: + return + + buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xa8, 4))[0] + size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0xb0, 4))[0] + self.log(f"SetupIO 0x18: buf {buf:#x}, size {size:#x}") + # XXX: Do not try to log messages going to 0x2 + if buf == 0x2: + self.log("Mesa command interrupted!") + return + self.log_mesa("EP 0x18", self.tracer.ioread(buf, size)) + return + if msg.EP == 0x19: + if self.state.iova is None: + return + + buf = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x48, 4))[0] + size = struct.unpack("<I", self.tracer.ioread(self.state.iova + 0x50, 4))[0] + self.log(f"CompleteIO 0x19: buf {buf:#x}, size {size:#x}") + # XXX: Do not try to log messages going to 0x2 + if buf == 0x2: + self.log("Mesa command interrupted!") + return + if size >= 0x7200: + with open("large_message.bin", "wb") as fd: + fd.write(self.tracer.ioread(buf, size)) + print("Fingerprint record message dumped.") + return + self.log_mesa("EP 0x19", self.tracer.ioread(buf, size)) + return + + def log_mesa(self, label, data): + self.log(f"{label}: {len(data):d} byte message: ") + chexdump(data) + print("\n") + + +class SIOTracer(ASCTracer): + ENDPOINTS = { + 0x20: SIOEp + } + + +sio_tracer = SIOTracer(hv, "/arm-io/sio", verbose=1) +sio_tracer.start(dart=dart_sio_tracer.dart) + +spi_tracer = SPITracer(hv, "/arm-io/spi2", verbose=2) +spi_tracer.start() diff --git a/tools/proxyclient/hv/trace_nvme.py b/tools/proxyclient/hv/trace_nvme.py new file mode 100644 index 0000000..c10b3bc --- /dev/null +++ b/tools/proxyclient/hv/trace_nvme.py @@ -0,0 +1,275 @@ +# SPDX-License-Identifier: MIT + +from construct import * +from construct.core import Int16ul, Int32ul, Int64ul, Int8ul + +from m1n1.hv import TraceMode +from m1n1.utils import * +from m1n1.trace import ADTDevTracer +from m1n1.trace.asc import ASCRegs +from m1n1.trace.asc import ASCTracer + +ASCTracer = ASCTracer._reloadcls() + +class NVMERegs(RegMap): + APPLE_NVMMU_NUM = 0x28100, Register32 + APPLE_NVMMU_BASE_ASQ = 0x28108, Register32 + APPLE_NVMMU_BASE_ASQ1 = 0x2810C, Register32 + APPLE_NVMMU_BASE_IOSQ = 0x28110, Register32 + APPLE_NVMMU_BASE_IOSQ1 = 0x28114, Register32 + APPLE_NVMMU_TCB_INVAL = 0x28118, Register32 + APPLE_NVMMU_TCB_STAT = 0x28120, Register32 + APPLE_ANS2_LINEAR_SQ_CTRL = 0x24908, Register32 + APPLE_ANS2_UNKNOWN_CTRL = 0x24008, Register32 + APPLE_ANS2_BOOT_STATUS = 0x1300, Register32 + APPLE_ANS2_MAX_PEND_CMDS_CTRL = 0x1210, Register32 + APPLE_ANS2_LINEAR_ASQ_DB = 0x2490C, Register32 + APPLE_ANS2_LINEAR_IOSQ_DB = 0x24910, Register32 + + NVME_REG_CAP = 0x0000, Register32 + NVME_REG_VS = 0x0008, Register32 + NVME_REG_INTMS = 0x000C, Register32 + NVME_REG_INTMC = 0x0010, Register32 + NVME_REG_CC = 0x0014, Register32 + NVME_REG_CSTS = 0x001C, Register32 + NVME_REG_NSSR = 0x0020, Register32 + NVME_REG_AQA = 0x0024, Register32 + NVME_REG_ASQ = 0x0028, Register32 + NVME_REG_ASQ1 = 0x002C, Register32 + NVME_REG_ACQ = 0x0030, Register32 + NVME_REG_CMBLOC = 0x0038, Register32 + NVME_REG_CMBSZ = 0x003C, Register32 + NVME_REG_BPINFO = 0x0040, Register32 + NVME_REG_BPRSEL = 0x0044, Register32 + NVME_REG_BPMBL = 0x0048, Register32 + NVME_REG_CMBMSC = 0x0050, Register32 + NVME_REG_PMRCAP = 0x0E00, Register32 + NVME_REG_PMRCTL = 0x0E04, Register32 + NVME_REG_PMRSTS = 0x0E08, Register32 + NVME_REG_PMREBS = 0x0E0C, Register32 + NVME_REG_PMRSWTP = 0x0E10, Register32 + NVME_REG_DBS = 0x1000, Register32 + NVME_REG_DBS_ASQ = 0x1004, Register32 + NVME_REG_DBS_IOSQ = 0x100C, Register32 + + +AppleTunnelSetTime = Struct( + "unk" / Int32ul, + "unix_timestamp" / Int32ul, + "time_0" / Int64ul, + "time_1" / Int64ul, +) + +NVMECommand = Struct( + "opcode" / Int8ul, + "flags" / Int8ul, + "command_id" / Int16ul, + "nsid" / Int32ul, + "cdw0" / Int32ul, + "cdw1" / Int32ul, + "metadata" / Int64ul, + "prp1" / Int64ul, + "prp2" / Int64ul, + "cdw10" / Int32ul, + "cdw11" / Int32ul, + "cdw12" / Int32ul, + "cdw13" / Int32ul, + "cdw14" / Int32ul, + "cdw16" / Int32ul, +) + +NVME_IO_COMMANDS = { + 0x00: "nvme_cmd_flush", + 0x01: "nvme_cmd_write", + 0x02: "nvme_cmd_read", + 0x04: "nvme_cmd_write_uncor", + 0x05: "nvme_cmd_compare", + 0x08: "nvme_cmd_write_zeroes", + 0x09: "nvme_cmd_dsm", + 0x0C: "nvme_cmd_verify", + 0x0D: "nvme_cmd_resv_register", + 0x0E: "nvme_cmd_resv_report", + 0x11: "nvme_cmd_resv_acquire", + 0x15: "nvme_cmd_resv_release", + 0x79: "nvme_cmd_zone_mgmt_send", + 0x7A: "nvme_cmd_zone_mgmt_recv", + 0x7D: "nvme_cmd_zone_append", +} + +NVME_ADMIN_COMMANDS = { + 0x00: "nvme_admin_delete_sq", + 0x01: "nvme_admin_create_sq", + 0x02: "nvme_admin_get_log_page", + 0x04: "nvme_admin_delete_cq", + 0x05: "nvme_admin_create_cq", + 0x06: "nvme_admin_identify", + 0x08: "nvme_admin_abort_cmd", + 0x09: "nvme_admin_set_features", + 0x0A: "nvme_admin_get_features", + 0x0C: "nvme_admin_async_event", + 0x0D: "nvme_admin_ns_mgmt", + 0x10: "nvme_admin_activate_fw", + 0x11: "nvme_admin_download_fw", + 0x14: "nvme_admin_dev_self_test", + 0x15: "nvme_admin_ns_attach", + 0x18: "nvme_admin_keep_alive", + 0x19: "nvme_admin_directive_send", + 0x1A: "nvme_admin_directive_recv", + 0x1C: "nvme_admin_virtual_mgmt", + 0x1D: "nvme_admin_nvme_mi_send", + 0x1E: "nvme_admin_nvme_mi_recv", + 0x7C: "nvme_admin_dbbuf", + 0x80: "nvme_admin_format_nvm", + 0x81: "nvme_admin_security_send", + 0x82: "nvme_admin_security_recv", + 0x84: "nvme_admin_sanitize_nvm", + 0x86: "nvme_admin_get_lba_status", + 0xC0: "nvme_admin_vendor_start", +} + +APPLE_TUNNEL_CMDS = {0x06: "set_time", 0x38: "get_nand_id", 0xBA: "get_nand_geometry"} + +NVMMUTcb = Struct( + "opcode" / Int8ul, + "dma_flags" / Int8ul, + "command_id" / Int8ul, + "unk0" / Int8ul, + "length" / Int32ul, + "unk1a" / Int64ul, + "unk1b" / Int64ul, + "prp0" / Int64ul, + "prp1" / Int64ul, + "unk2a" / Int64ul, + "unk2b" / Int64ul, + # aes_iv, u8[8] + # aes_data, u8[64] +) + + +class NVMETracer(ASCTracer): + DEFAULT_MODE = TraceMode.SYNC + + REGMAPS = [ASCRegs, None, None, NVMERegs] + NAMES = ["asc", None, None, "nvme"] + + ENDPOINTS = {} + + def init_state(self): + self.state.ep = {} + self.state.cmd_cache = {} + self.state.nvmmu_asq_base = None + self.state.nvmmu_iosq_base = None + self.state.asq = None + + def r_APPLE_NVMMU_TCB_STAT(self, r): + pass + + def w_APPLE_NVMMU_BASE_ASQ(self, r): + self.state.nvmmu_asq_base = r.value + + def w_APPLE_NVMMU_BASE_ASQ1(self, r): + self.state.nvmmu_asq_base |= r.value << 32 + + def w_APPLE_NVMMU_BASE_IOSQ(self, r): + self.state.nvmmu_iosq_base = r.value + + def w_APPLE_NVMMU_BASE_IOSQ1(self, r): + self.state.nvmmu_iosq_base |= r.value << 32 + + def w_NVME_REG_ASQ(self, r): + self.state.asq = r.value + + def w_NVME_REG_ASQ1(self, r): + self.state.asq |= r.value << 32 + + def w_APPLE_ANS2_LINEAR_ASQ_DB(self, r): + tag = r.value + cmd = NVMECommand.parse(self.hv.iface.readmem(self.state.asq + 64 * tag, 0x40)) + tcb = NVMMUTcb.parse( + self.hv.iface.readmem(self.state.nvmmu_asq_base + 0x80 * tag, 0x80) + ) + + self.state.cmd_cache[tag] = (True, cmd, tcb) + + if cmd.opcode == 0xD8: + self.log("apple_tunnel_cmd:") + self.parse_apple_tunnel_cmd(cmd, False) + return + + cmdname = NVME_ADMIN_COMMANDS.get(cmd.opcode, "unknown") + self.log(f"{cmdname}:") + self.log(f" {repr(cmd)}") + self.log(f" {repr(tcb)}") + + if cmd.opcode == 1: + self.state.iosq = cmd.prp1 + + def w_APPLE_ANS2_LINEAR_IOSQ_DB(self, r): + tag = r.value + cmd = NVMECommand.parse(self.hv.iface.readmem(self.state.iosq + 64 * tag, 0x40)) + tcb = NVMMUTcb.parse( + self.hv.iface.readmem(self.state.nvmmu_iosq_base + 0x80 * tag, 0x80) + ) + cmdname = NVME_IO_COMMANDS.get(cmd.opcode, "unknown") + self.log(f"{cmdname}:") + self.log(f" {repr(cmd)}") + self.log(f" {repr(tcb)}") + + self.state.cmd_cache[tag] = (False, cmd, tcb) + + def parse_apple_tunnel_cmd(self, cmd, done): + ptr0 = (cmd.cdw12 << 32) | cmd.cdw11 + ptr1 = (cmd.cdw14 << 32) | cmd.cdw13 + + data = self.hv.iface.readmem(ptr0, 0x4000) + if ptr1 > 0: + data1 = self.hv.iface.readmem(ptr1, 0x4000) + + apple_cmd_opcode = data[12] + apple_cmd = APPLE_TUNNEL_CMDS.get(apple_cmd_opcode, "Unknown") + + if apple_cmd_opcode == 0x06: + self.log( + f" apple_tunnel_cmd: set_time: {repr(AppleTunnelSetTime.parse(data[0x18:0x30]))}" + ) + elif apple_cmd_opcode == 0x38: + self.log(f" apple_tunnel_cmd: get_nand_id") + if done: + self.log(f" manufacturer id: {hexdump(data1[:8])}") + else: + self.log(f" apple_tunnel_cmd: {apple_cmd} ({apple_cmd_opcode})") + chexdump(data, print_fn=self.log) + if ptr1 > 0: + chexdump(self.hv.iface.readmem(ptr1, 0x4000), print_fn=self.log) + + def w_APPLE_NVMMU_TCB_INVAL(self, r): + self.log(f" NVMMU inval for {r.value}") + tag = r.value + if tag not in self.state.cmd_cache: + self.log(" NVMMU tag not found in cmd_cache") + return + + is_admin, cmd, tcb = self.state.cmd_cache[tag] + del self.state.cmd_cache[tag] + + if is_admin: + if cmd.opcode == 0xD8: + self.log(f" done apple_tunnel_cmd") + self.parse_apple_tunnel_cmd(cmd, True) + else: + cmdname = NVME_ADMIN_COMMANDS.get(cmd.opcode, "unknown") + self.log(f" done {cmdname}") + else: + cmdname = NVME_IO_COMMANDS.get(cmd.opcode, "unknown") + self.log(f" done {cmdname}") + + def start(self): + self.state.cmd_cache = {} + super().start() + + +NVMETracer = NVMETracer._reloadcls() +nvme_tracer = NVMETracer(hv, "/arm-io/ans", verbose=1) +nvme_tracer.start() + +trace_device("/arm-io/sart-ans") diff --git a/tools/proxyclient/hv/trace_pmgr.py b/tools/proxyclient/hv/trace_pmgr.py new file mode 100644 index 0000000..f04d8db --- /dev/null +++ b/tools/proxyclient/hv/trace_pmgr.py @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: MIT + +from m1n1 import asm +from m1n1.trace import Tracer +from m1n1.utils import * +from m1n1.proxy import * +from m1n1.sysreg import * +from m1n1.proxyutils import RegMonitor +from m1n1.trace.dart import DARTTracer +from m1n1.trace.asc import ASCTracer, EP, msg, msg_log, DIR +from m1n1.fw.pmp import * + +#trace_device("/arm-io/pmgr", False) +#trace_device("/arm-io/jpeg0") +#trace_device("/arm-io/jpeg1") + +#for reg in (0, 1, 2, 3, 4, 23): + #addr, size = hv.adt["/arm-io/pmgr"].get_reg(reg) + #hv.trace_range(irange(addr, 0x20000)) + +#hv.trace_range(irange(0x210e00000, 0x80000), read=False) +#hv.trace_range(irange(0x211e00000, 0x80000), read=False) + +#hv.trace_range(irange(0x23b040000, 0x1000)) +#hv.trace_range(irange(0x23b044000, 0x14000)) + +Tracer = Tracer._reloadcls() +ASCTracer = ASCTracer._reloadcls() + +iomon = RegMonitor(hv.u, ascii=True) + +def readmem_iova(addr, size): + try: + return dart_tracer.dart.ioread(0, addr, size) + except Exception as e: + print(e) + return None + +iomon.readmem = readmem_iova + +class PMPEpTracer(EP): + BASE_MESSAGE = PMPMessage + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.state.shmem_iova = None + self.state.verbose = 1 + + def start(self): + self.add_mon() + + def add_mon(self): + if self.state.shmem_iova: + iomon.add(self.state.shmem_iova, 0x10000, + name=f"{self.name}.shmem@{self.state.shmem_iova:08x}", offset=0) + + @msg(1, DIR.TX, PMP_Configure) + def Configure(self, msg): + self.state.shmem_iova = msg.DVA + self.add_mon() + +class PMPTracer(ASCTracer): + ENDPOINTS = { + 0x20: PMPEpTracer + } + + def handle_msg(self, direction, r0, r1): + super().handle_msg(direction, r0, r1) + iomon.poll() + + def start(self, dart=None): + super().start() + # noisy doorbell + self.trace(0x23bc34000, 4, TraceMode.OFF) + +#dart_tracer = DARTTracer(hv, "/arm-io/dart-pmp", verbose=2) +#dart_tracer.start() + +#pmp_tracer = PMPTracer(hv, "/arm-io/pmp", verbose=1) +#pmp_tracer.start(dart_tracer.dart) + +class PMGRTracer(Tracer): + IGNORED = set(["SPI1", "I2C2"]) + def __init__(self, hv): + super().__init__(hv) + self.dev = hv.adt["/arm-io/pmgr"] + self.ignored_ranges = [ + (0x23b738004, 4), # ecpu state report + (0x23b738008, 4), # pcpu state report + (0x23d2b9000, 0x30), + (0x23d2dc100, 4), + ] + self.build_table(hv) + self.reg_cache = {} + + def hook_w(self, addr, val, width, **kwargs): + self.hv.log(f"PMGR: W {addr:#x} <- {val:#x}") + #print("-> ignored") + super().hook_w(addr, val, width, **kwargs) + + def hook_r(self, addr, width, **kwargs): + val = super().hook_r(addr, width, **kwargs) + self.hv.log(f"PMGR: R {addr:#x} = {val:#x}") + return val + + def evt_rw(self, evt): + if not evt.flags.WRITE: + self.reg_cache[evt.addr] = evt.data + cb = self.ranges.lookup(evt.addr) + cb[0](evt, *cb[1:]) + self.reg_cache[evt.addr] = evt.data + + def event_default(self, evt, start, name): + t = "W" if evt.flags.WRITE else "R" + m = "+" if evt.flags.MULTI else " " + data = f"{evt.data:#x}" + if evt.flags.WRITE: + data = f"{evt.data:#x}" + old = self.reg_cache.get(evt.addr, None) + if old is not None: + data = f"{old:#x} -> {evt.data:#x}" + self.hv.log(f"[cpu{evt.flags.CPU}][0x{evt.pc:016x}] PMGR: {t}.{1<<evt.flags.WIDTH:<2}{m} " + + f"0x{evt.addr:x} ({name} + {evt.addr - start:#04x}) = {data}", show_cpu=False) + + def build_table(self, hv): + self.ranges = ScalarRangeMap() + self.state_regs = {} + + starts = {} + for reg in (0, 1): + addr, size = self.dev.get_reg(reg) + self.ranges[addr:addr + size] = self.event_default, addr, f"reg[{reg}]" + + for i, ps in enumerate(self.dev.ps_regs): + addr = self.dev.get_reg(ps.reg)[0] + ps.offset + for idx in range(32): + ps_addr = addr + idx * 8 + self.ranges[ps_addr:ps_addr + 8] = self.event_default, ps_addr, f"ps[{i}][{idx}]" + + for i, dev in enumerate(self.dev.devices): + ps = self.dev.ps_regs[dev.psreg] + if dev.psidx or dev.psreg: + addr = self.dev.get_reg(ps.reg)[0] + ps.offset + dev.psidx * 8 + self.state_regs[addr] = dev.name + if dev.name in self.IGNORED: + self.ignored_ranges.append((addr, 8)) + self.ranges[addr:addr + 8] = self.event_default, addr, f"{dev.name}.pstate" + + def start(self): + self.hv.clear_tracers(self.ident) + + for reg in (0, 1): + addr, size = self.dev.get_reg(reg) + self.trace(addr, size, TraceMode.WSYNC, read=False) + + for ps in self.dev.ps_regs: + addr = self.dev.get_reg(ps.reg)[0] + ps.offset + self.trace(addr, 0x100, TraceMode.WSYNC) + + for lane in range(8): + addr = 0x200200000 + 0x40000 * lane + self.trace(addr, 0x40000, TraceMode.HOOK) + #for reg in (23,): + #addr, size = self.dev.get_reg(reg) + #self.trace(addr, 0x20000, TraceMode.SYNC) + for addr, size in self.ignored_ranges: + self.trace(addr, size, TraceMode.OFF) + +pmgr_tracer = PMGRTracer(hv) +pmgr_tracer.start() diff --git a/tools/proxyclient/hv/trace_prores.py b/tools/proxyclient/hv/trace_prores.py new file mode 100644 index 0000000..05acb4a --- /dev/null +++ b/tools/proxyclient/hv/trace_prores.py @@ -0,0 +1,88 @@ +from m1n1.trace import ADTDevTracer +from m1n1.trace.dart8110 import DART8110Tracer +from m1n1.hw.prores import * +from m1n1.utils import * +import struct + +p.pmgr_adt_clocks_enable('/arm-io/dart-apr0') +p.pmgr_adt_clocks_enable('/arm-io/dart-apr1') + +dart0_tracer = DART8110Tracer(hv, "/arm-io/dart-apr0", verbose=1) +dart0_tracer.start() +print(dart0_tracer) +dart1_tracer = DART8110Tracer(hv, "/arm-io/dart-apr1", verbose=1) +dart1_tracer.start() +print(dart1_tracer) + + +class ProResTracer(ADTDevTracer): + DEFAULT_MODE = TraceMode.SYNC + REGMAPS = [ProResRegs] + NAMES = ['prores'] + + def __init__(self, hv, devpath, dart_tracer): + super().__init__(hv, devpath, verbose=3) + self.dart_tracer = dart_tracer + + def w_DR_SIZE(self, val): + self._dr_size = val + + def w_DR_ADDR_HI(self, val): + self._dr_addr_hi = val + + def w_DR_ADDR_LO(self, val): + self._dr_addr_lo = val + + def w_DR_TAIL(self, val): + self.log(f"DR_TAIL = {val}") + self._dr_tail = val + + def w_DR_HEAD(self, val): + self.log(f"DR_HEAD = {val}") + self.dart_tracer.dart.dump_all() + + dr_addr = int(self._dr_addr_hi) << 32 | int(self._dr_addr_lo) + dr_size = int(self._dr_size) + self.log(f"desc ring @ {dr_addr:016X} sz {dr_size:08X}") + + dr = self.dart_tracer.dart.ioread(0, dr_addr, dr_size) + chexdump(dr) + + # FIXME there are other descriptor types + # also, what if there are multiple in the ring? + dr_head = int(val) + dr_tail = int(self._dr_tail) + + if dr_head - dr_tail == 0x180: + desc = EncodeNotRawDescriptor._make(struct.unpack(ENCODE_NOT_RAW_STRUCT, dr[dr_tail:dr_head])) + print(desc) + + p0_iova = desc.luma_iova + p1_iova = desc.chroma_iova + p2_iova = desc.alpha_iova + + if p0_iova: + print(f"P0 iova {p0_iova:016X}") + data = self.dart_tracer.dart.ioread(0, p0_iova, 0x1000) + chexdump(data) + if p1_iova: + print(f"P1 iova {p1_iova:016X}") + data = self.dart_tracer.dart.ioread(0, p1_iova, 0x1000) + chexdump(data) + if p2_iova: + print(f"P2 iova {p2_iova:016X}") + data = self.dart_tracer.dart.ioread(0, p2_iova, 0x1000) + chexdump(data) + + +ProResTracer = ProResTracer._reloadcls() + +p.pmgr_adt_clocks_enable('/arm-io/apr0') +p.pmgr_adt_clocks_enable('/arm-io/apr1') + +tracer0 = ProResTracer(hv, '/arm-io/apr0', dart0_tracer) +tracer0.start() +print(tracer0) +tracer1 = ProResTracer(hv, '/arm-io/apr1', dart1_tracer) +tracer1.start() +print(tracer1) diff --git a/tools/proxyclient/hv/trace_smc.py b/tools/proxyclient/hv/trace_smc.py new file mode 100644 index 0000000..2a1efa6 --- /dev/null +++ b/tools/proxyclient/hv/trace_smc.py @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: MIT + +import struct + +from enum import IntEnum + +from m1n1.proxyutils import RegMonitor +from m1n1.utils import * +from m1n1.trace.dart import DARTTracer +from m1n1.trace.asc import ASCTracer, EP, EPState, msg, msg_log, DIR +from m1n1.fw.smc import * + +ASCTracer = ASCTracer._reloadcls() + +class SMCEpTracer(EP): + BASE_MESSAGE = SMCMessage + + def __init__(self, tracer, epid): + super().__init__(tracer, epid) + self.state.sram_addr = None + self.state.verbose = 1 + self.state.rb = {} + + Initialize = msg_log(SMC_INITIALIZE, DIR.TX, SMCInitialize) + Notification = msg_log(SMC_NOTIFICATION, DIR.RX) + + @msg(SMC_WRITE_KEY, DIR.TX, SMCWriteKey) + def WriteKey(self, msg): + key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") + self.state.rb[msg.ID] = msg.TYPE, key, msg.SIZE + data = self.hv.iface.readmem(self.state.sram_addr, msg.SIZE) + self.log(f"[{msg.ID:x}] >W: <{key}> = {data.hex()} ({msg.SIZE})") + return True + + @msg(SMC_READ_KEY, DIR.TX, SMCReadKey) + def ReadKey(self, msg): + key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") + self.state.rb[msg.ID] = msg.TYPE, key, msg.SIZE + self.log(f"[{msg.ID:x}] >R: <{key}> = ... ({msg.SIZE})") + return True + + @msg(SMC_RW_KEY, DIR.TX, SMCReadWriteKey) + def ReadKeyPayload(self, msg): + key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") + self.state.rb[msg.ID] = msg.TYPE, key, msg.RSIZE + data = self.hv.iface.readmem(self.state.sram_addr, msg.WSIZE) + self.log(f"[{msg.ID:x}] >RP: <{key}> = {data.hex()} ({msg.WSIZE, msg.RSIZE})") + return True + + @msg(SMC_GET_KEY_INFO, DIR.TX, SMCGetKeyInfo) + def GetInfo(self, msg): + key = msg.KEY.to_bytes(4, byteorder="big").decode("ascii") + self.state.rb[msg.ID] = msg.TYPE, key, None + self.log(f"[{msg.ID:x}] >KInfo: <{key}>") + return True + + @msg(SMC_GET_KEY_BY_INDEX, DIR.TX, SMCGetKeyByIndex) + def GetKeyByIndex(self, msg): + self.state.rb[msg.ID] = msg.TYPE, msg.INDEX, None + self.log(f"[{msg.ID:x}] >KIdx: <{msg.INDEX}>") + return True + + @msg(None, DIR.RX, Register64) + def RXMsg(self, msg): + if self.state.sram_addr is None: + self.log(f"SRAM address: {msg.value:#x}") + self.state.sram_addr = msg.value + return True + + msg = SMCResult(msg.value) + if msg.RESULT != 0: + self.log(f"[{msg.ID:x}] <Err: 0x{msg.RESULT:02x}") + return True + + if msg.ID in self.state.rb: + msgtype, key, size = self.state.rb.pop(msg.ID) + if msgtype in (SMC_READ_KEY, SMC_RW_KEY): + if size <= 4: + data = hex(msg.VALUE) + else: + data = self.hv.iface.readmem(self.state.sram_addr, msg.SIZE).hex() + self.log(f"[{msg.ID:x}] <R: <{key}> = {data}") + return True + + elif msgtype == SMC_GET_KEY_INFO: + data = self.hv.iface.readmem(self.state.sram_addr, 6) + size, type, flags = struct.unpack("B4sB", data) + self.log(f"[{msg.ID:x}] <Info: <{key}>: size={size} type={type.decode('ascii')} flags={flags:#x}") + return True + + elif msgtype == SMC_GET_KEY_BY_INDEX: + kname = msg.VALUE.to_bytes(4, byteorder="little").decode("ascii") + self.log(f"[{msg.ID:x}] <Key @{key}: <{kname}>") + return True + + self.log(f"[{msg.ID:x}] <OK {msg!r}") + return True + +class SMCTracer(ASCTracer): + ENDPOINTS = { + 0x20: SMCEpTracer + } + + def handle_msg(self, direction, r0, r1): + super().handle_msg(direction, r0, r1) + +smc_tracer = SMCTracer(hv, "/arm-io/smc", verbose=1) +smc_tracer.start() diff --git a/tools/proxyclient/hv/trace_wlan.py b/tools/proxyclient/hv/trace_wlan.py new file mode 100644 index 0000000..1e4c4b1 --- /dev/null +++ b/tools/proxyclient/hv/trace_wlan.py @@ -0,0 +1,446 @@ +import struct +from construct import * + +from m1n1.utils import irange +from m1n1.hw.dart import DART +from m1n1.utils import chexdump +from m1n1.proxyutils import RegMonitor +from m1n1.constructutils import * + +from m1n1.trace.pcie import * + +PCIeDevTracer = PCIeDevTracer._reloadcls() + +mon = RegMonitor(hv.u) + +class WLANCfgSpace(PCICfgSpace): + BAR0_WINDOW = 0x80, Register32 + WRAPPERBASE = 0x70, Register32 + INTSTATUS = 0x90, Register32 + INTMASK = 0x94, Register32 + SBMBX = 0x98, Register32 + LINK_STATUS_CTRL = 0xbc, Register32 + +class WLANBAR0(RegMap): + INTMASK = 0x2024, Register32 + MAILBOXINT = 0x2048, Register32 + MAILBOXMASK = 0x204c, Register32 + CONFIGADDR = 0x2120, Register32 + CONFIGDATA = 0x2124, Register32 + H2D_MAILBOX_0 = 0x2140, Register32 + H2D_MAILBOX_1 = 0x2144, Register32 + + # Linux uses these, via offset 0 instead of 0x2000 + H2D_MAILBOX_0_ALT = 0x140, Register32 + H2D_MAILBOX_1_ALT = 0x144, Register32 + H2D_MAILBOX_0_64 = 0xa20, Register32 + H2D_MAILBOX_1_64 = 0xa24, Register32 + INTMASK_64 = 0xc14, Register32 + MAILBOXINT_64 = 0xc30, Register32 + MAILBOXMASK_64 = 0xc34, Register32 + +class WLANSRAMEnd(RegMap): + PAD = 0x00, Register32 + SHARED_BASE = 0x04, Register32 + +class WLANSRAMShared(RegMap): + FLAGS = 0, Register32 + CONSOLE_ADDR = 20, Register32 + FWID = 28, Register32 + MAX_RXBUFPOST = 34, Register16 + RX_DATAOFFSET = 36, Register32 + HTOD_MB_DATA_ADDR = 40, Register32 + DTOH_MB_DATA_ADDR = 44, Register32 + RING_INFO_ADDR = 48, Register32 + DMA_SCRATCH_LEN = 52, Register32 + DMA_SCRATCH_ADDR = 56, Register64 + HOST_SCB_ADDR = 64, Register64 + HOST_SCB_SIZE = 72, Register32 + BUZZ_DBG_PTR = 76, Register32 + FLAGS2 = 80, Register32 + HOST_CAP = 84, Register32 + HOST_TRAP_ADDR = 88, Register64 + DEVICE_FATAL_LOGBUF_START = 96, Register32 + HOFFLOAD_ADDR = 100, Register64 + FLAGS3 = 108, Register32 + HOST_CAP2 = 112, Register32 + HOST_CAP3 = 116, Register32 + +class WLANSRAMRingInfo(RegMap): + RINGMEM = 0x00, Register32 + H2D_W_IDX_PTR = 0x04, Register32 + H2D_R_IDX_PTR = 0x08, Register32 + D2H_W_IDX_PTR = 0x0c, Register32 + D2H_R_IDX_PTR = 0x10, Register32 + H2D_W_IDX_HOSTADDR = 0x14, Register64 + H2D_R_IDX_HOSTADDR = 0x1c, Register64 + D2H_W_IDX_HOSTADDR = 0x24, Register64 + D2H_R_IDX_HOSTADDR = 0x2c, Register64 + MAX_FLOWRINGS = 0x34, Register32 + MAX_SUBMISSIONRINGS = 0x38, Register32 + MAX_COMPLETIONRINGS = 0x3c, Register32 + +COMMON_RING_CNT = 5 + +class WLANSRAMRingMem(RegMap): + MAX_ITEM = irange(0x04, COMMON_RING_CNT, 0x10), Register16 + LEN_ITEMS = irange(0x06, COMMON_RING_CNT, 0x10), Register16 + BASE_ADDR = irange(0x08, COMMON_RING_CNT, 0x10), Register32 + +class MsgHeader(ConstructClass): + subcon = Struct( + "msg_type" / Int8ul, + "if_id" / Int8sl, + "flags" / Int8ul, + "epoch" / Int8ul, + "request_id" / Int32ul, + ) + +class ComplHeader(ConstructClass): + subcon = Struct( + "status" / Int16ul, + "ring_id" / Int16ul, + ) + +class IOCtlPtrReq(ConstructClass): + subcon = Struct( + "cmd" / Int32ul, + "trans_id" / Int16ul, + "input_buf_len" / Int16ul, + "output_buf_len" / Int16ul, + "rsvd" / Array(3, Int16ul), + "host_input_buf_addr" / Int64ul, + ) + +class IOCtlResp(ConstructClass): + subcon = Struct( + "compl" / ComplHeader, + "resp_len" / Int16ul, + "trans_id" / Int16ul, + "cmd" / Int32ul, + ) + +class H2DMailboxData(ConstructClass): + subcon = Struct( + "data" / Int32ul + ) + +class D2HMailboxData(ConstructClass): + subcon = Struct( + "compl" / ComplHeader, + "data" / Int32ul, + ) + +class RingMessage(ConstructClass): + subcon = Struct( + "hdr" / MsgHeader, + "payload" / Switch(this.hdr.msg_type, { + 0x09: IOCtlPtrReq, + 0x0c: IOCtlResp, + 0x23: H2DMailboxData, + 0x24: D2HMailboxData, + + }, default=HexDump(GreedyBytes)) + ) + +class RingState: + pass + +class WLANRingTracer(PCIeDevTracer): + def __init__(self, wlan, info): + self.wlan = wlan + self.hv = wlan.hv + self.p = wlan.hv.p + self.u = wlan.hv.u + self.ringid = self.RX, self.PTR_IDX + if self.ringid in wlan.state.rings: + self.state = wlan.state.rings[self.ringid] + else: + self.state = wlan.state.rings[self.ringid] = RingState() + self.state.rptr = 0 + + self.info = info + assert info.item_size == self.ITEM_SIZE + self.base_addr = info.base_addr + self.count = info.count + + if self.RX: + d2h_paddr = self.wlan.iotranslate(self.wlan.state.d2h_w_idx_ha + 4 * self.PTR_IDX, 4)[0][0] + assert d2h_paddr is not None + self.hv.add_tracer(irange(d2h_paddr, 4), self.wlan.ident, TraceMode.SYNC, + read=self.d2h_w_idx_readhook) + + def d2h_w_idx_readhook(self, evt): + self.log("W idx read") + self.poll() + + def poll(self): + if self.RX: + wptr = self.wlan.ioread(self.wlan.state.d2h_w_idx_ha + 4 * self.PTR_IDX, 4) + else: + wptr = self.wlan.ioread(self.wlan.state.h2d_w_idx_ha + 4 * self.PTR_IDX, 4) + + wptr = struct.unpack("<I", wptr)[0] + + while wptr != self.state.rptr: + off = self.state.rptr * self.ITEM_SIZE + addr = self.base_addr + off + data = self.wlan.ioread(addr, self.ITEM_SIZE) + self.pkt(data) + self.state.rptr = (self.state.rptr + 1) % self.count + + def pkt(self, data): + self.log("Got packet:") + pkt = RingMessage.parse(data) + self.log(pkt) + if pkt.hdr.msg_type == 0x09: + self.wlan.ioctlptr_req(pkt) + if pkt.hdr.msg_type == 0x0c: + self.wlan.ioctlresp(pkt) + + def log(self, msg): + self.wlan.log(f"[{self.NAME}]{msg!s}") + +class WLANControlSubmitRingTracer(WLANRingTracer): + NAME = "CTLSubmit" + PTR_IDX = 0 + RX = False + ITEM_SIZE = 0x28 + +class WLANControlCompleteRingTracer(WLANRingTracer): + NAME = "CTLCompl" + PTR_IDX = 0 + RX = True + ITEM_SIZE = 0x18 + +class RingInfo: + def __init__(self): + self.count = None + self.item_size = None + self.base_addr = None + + def ready(self): + return self.count is not None and self.item_size is not None and self.base_addr is not None + +class WLANTracer(PCIeDevTracer): + DEFAULT_MODE = TraceMode.SYNC + + SRAM_BASE = 0x740000 + SRAM_SIZE = 0x1f9000 + + BARMAPS = [WLANBAR0, None, None] + CFGMAP = WLANCfgSpace + + RINGS = [ + WLANControlSubmitRingTracer, + None, # RXPost + WLANControlCompleteRingTracer, + None, # TX complete + None, # RX complete + ] + + CMDS = { + 1: "GET_VERSION", + 2: "UP", + 3: "DOWN", + 262: "GET_VAR", + 263: "SET_VAR", + } + + def __init__(self, hv, apcie, bus, dev, fn, dart_path=None, verbose=False): + super().__init__(hv, apcie, bus, dev, fn, verbose=verbose) + self.u = hv.u + self.p = hv.p + self.dart_path = dart_path + self.dart_dev = None + self.dart = None + self.rings = {} + + def init_state(self): + super().init_state() + self.state.shared_base = None + self.state.ring_info_base = None + self.state.ring_mem_base = None + self.state.tcm_base = None + self.state.tcm_size = None + self.state.ring_info = None + self.state.ring_mem = None + self.state.ring_info_data = {} + self.state.rings = {} + self.state.ioctls = {} + self.state.h2d_w_idx_ha = None + self.state.h2d_r_idx_ha = None + self.state.d2h_w_idx_ha = None + self.state.d2h_r_idx_ha = None + + def config_dart(self): + # Ugly... + if self.dart_dev is None: + for i in range (16): + ttbr = self.dart.regs.TTBR[i, 0].reg + if ttbr.VALID: + self.log(f"DART device: {i}") + self.dart_dev = i + break + else: + raise Exception("Failed to find DART device") + + def ioread(self, addr, size): + self.config_dart() + return self.dart.ioread(self.dart_dev, addr, size) + + def iotranslate(self, addr, size): + self.config_dart() + return self.dart.iotranslate(self.dart_dev, addr, size) + + def r_SHARED_BASE(self, base): + if base.value & 0xffff == (base.value >> 16) ^ 0xffff: + return + + self.state.shared_base = base.value + self.update_shared() + + def w_H2D_W_IDX_HOSTADDR(self, addr): + self.state.h2d_w_idx_ha = addr.value + + def w_H2D_R_IDX_HOSTADDR(self, addr): + self.state.h2d_r_idx_ha = addr.value + + def w_D2H_W_IDX_HOSTADDR(self, addr): + self.state.d2h_w_idx_ha = addr.value + + def w_D2H_R_IDX_HOSTADDR(self, addr): + self.state.d2h_r_idx_ha = addr.value + + def w_MAX_ITEM(self, val, index): + info = self.state.ring_info_data.setdefault(index, RingInfo()) + info.count = val.value + self.update_ring(index) + + def w_LEN_ITEMS(self, val, index): + info = self.state.ring_info_data.setdefault(index, RingInfo()) + info.item_size = val.value + self.update_ring(index) + + def w_BASE_ADDR(self, val, index): + info = self.state.ring_info_data.setdefault(index, RingInfo()) + info.base_addr = val.value + self.update_ring(index) + + def update_ring(self, idx): + if idx not in self.state.ring_info_data: + return + info = self.state.ring_info_data[idx] + if not info.ready(): + return + + if idx in self.rings: + return + + if idx > len(self.RINGS): + return + + ringcls = self.RINGS[idx] + + if ringcls is None: + return + + self.rings[idx] = ringcls(self, info) + + def w_H2D_MAILBOX_0(self, val): + ring = self.rings.get(2, None) + if ring is not None: + ring.poll() + + ring = self.rings.get(0, None) + if ring is not None: + ring.poll() + + w_H2D_MAILBOX_0_64 = w_H2D_MAILBOX_0 + w_H2D_MAILBOX_0_ALT = w_H2D_MAILBOX_0 + + def ioctlptr_req(self, pkt): + data = self.ioread(pkt.payload.host_input_buf_addr, pkt.payload.input_buf_len) + cmd = self.CMDS.get(pkt.payload.cmd, "unk") + self.log(f"IOCTL request ({cmd}):") + chexdump(data, print_fn = self.log) + self.state.ioctls[pkt.payload.trans_id] = pkt + + def ioctlresp(self, pkt): + req = self.state.ioctls.get(pkt.payload.trans_id, None) + if req is None: + self.log(f"ERROR: unknown transaction ID {pkt.payload.trans_id:#x}") + return + + data = self.ioread(req.payload.host_input_buf_addr, req.payload.output_buf_len) + cmd = self.CMDS.get(pkt.payload.cmd, "unk") + self.log(f"IOCTL response ({cmd}):") + chexdump(data, print_fn = self.log) + del self.state.ioctls[pkt.payload.trans_id] + + def trace_bar(self, idx, start, size): + if idx != 2: + return super().trace_bar(idx, start, size) + + self.state.tcm_base = start + self.state.tcm_size = size + + self.update_tcm_tracers() + + def update_tcm_tracers(self): + if self.state.tcm_base is None: + return + + if self.dart is None: + self.dart = DART.from_adt(self.u, self.dart_path) + + self.trace_regmap(self.state.tcm_base + self.SRAM_BASE + self.SRAM_SIZE - 8, 8, + WLANSRAMEnd, name="sram") + + def update_shared(self): + base = self.state.shared_base + if base is None: + return + + if self.state.ring_info_base is None: + self.shared = WLANSRAMShared(self.hv.u, self.state.tcm_base + base) + + self.log("Reading shared info") + self.shared.dump_regs() + + self.state.ring_info_base = self.shared.RING_INFO_ADDR.val + + if self.state.ring_mem_base is None: + self.ring_info = WLANSRAMRingInfo(self.hv.u, + self.state.tcm_base + self.state.ring_info_base) + self.log("Reading ring info") + self.ring_info.dump_regs() + + self.state.ring_mem_base = self.ring_info.RINGMEM.val + + self.trace_regmap(self.state.tcm_base + base, 0x100, + WLANSRAMShared, name="shared") + + self.trace_regmap(self.state.tcm_base + self.state.ring_info_base, 0x40, + WLANSRAMRingInfo, name="ringinfo") + + self.ring_mem = WLANSRAMRingMem(self.hv.u, + self.state.tcm_base + self.state.ring_mem_base) + self.log("Reading ring mem") + self.ring_mem.dump_regs() + + self.trace_regmap(self.state.tcm_base + self.state.ring_mem_base, + COMMON_RING_CNT * 0x10, WLANSRAMRingMem, name="ringmem") + + def start(self): + super().start() + + self.update_tcm_tracers() + self.update_shared() + for i in range(len(self.RINGS)): + self.update_ring(i) + +wlan_tracer = WLANTracer(hv, "/arm-io/apcie", + 4, 0, 0, "/arm-io/dart-apcie0") + +wlan_tracer.start() diff --git a/tools/proxyclient/hv/trace_z2.py b/tools/proxyclient/hv/trace_z2.py new file mode 100644 index 0000000..c52e80e --- /dev/null +++ b/tools/proxyclient/hv/trace_z2.py @@ -0,0 +1,221 @@ +# SPDX-License-Identifier: MIT + +import struct +from construct import * + +from m1n1.hv import TraceMode +from m1n1.proxyutils import RegMonitor +from m1n1.utils import * + +from m1n1.trace import ADTDevTracer +from m1n1.trace.asc import ASCTracer, ASCRegs, EP, EPState, msg, msg_log, DIR +from m1n1.trace.dart import DARTTracer +from m1n1.trace.gpio import GPIOTracer +from m1n1.trace.spi import SPITracer + +DARTTracer = DARTTracer._reloadcls() +ASCTracer = ASCTracer._reloadcls() +SPITracer = SPITracer._reloadcls() + +# SPI HID transport tracer for 2021 macbook models + +kbd_node = None +for node in hv.adt.walk_tree(): + try: + if node.compatible[0] == "spi-1,spimc": + for c in node: + try: + if c.compatible[0] == "hid-transport,k1": + kbd_node = c + break + except AttributeError: + continue + except AttributeError: + continue + if kbd_node is not None: + break + + +class Z2Tracer(SPITracer): + def start(self): + super().start() + self.txbuffer = [] + self.rxbuffer = [] + self.want_bytes = 0 + self.state = Z2Tracer.preboot + def w_TXDATA(self, data): + self.txbuffer.append(data.value) + self.check_msg_finished() + def r_RXDATA(self, data): + self.rxbuffer.append(data.value) + self.check_msg_finished() + def check_msg_finished(self): + if min(len(self.txbuffer), len(self.rxbuffer)) < self.want_bytes: + return + self.state(self) + def bad_state(self): + pass + def error(self): + self.log(f"RXBUF {' '.join(hex(x) for x in self.rxbuffer)}") + self.log(f"TXBUF {' '.join(hex(x) for x in self.txbuffer)}") + self.log(f"state: {self.state}") + self.log("Tracer desynchronized, shutting down") + self.state = Z2Tracer.bad_state + def consume_bytes(self, n): + self.txbuffer = self.txbuffer[n:] + self.rxbuffer = self.rxbuffer[n:] + def preboot(self): + if self.txbuffer[0] == 0: + self.want_bytes = 4 + self.state = Z2Tracer.init_zeros + elif self.txbuffer[0] == 0x1e: + self.want_bytes = 16 + self.state = Z2Tracer.processing_init_data + else: + self.error() + def init_zeros(self): + self.log("sent 4 zeroes") + self.consume_bytes(4) + self.state = Z2Tracer.preboot + def processing_init_data(self): + self.log("Sent init data") + self.want_bytes = 2 + self.consume_bytes(16) + self.state = Z2Tracer.main_hbpp + def main_hbpp(self): + if self.txbuffer[0] == 0x1a and self.txbuffer[1] == 0xa1: + self.log("Sent int ack") + self.consume_bytes(2) + elif self.txbuffer[0] == 0x18 and self.txbuffer[1] == 0xe1: + self.log("Sent nop") + self.consume_bytes(2) + elif self.txbuffer[0] == 0x1f and self.txbuffer[1] == 0x01: + self.log("Sent request cal") + self.consume_bytes(2) + elif self.txbuffer[0] == 0x30 and self.txbuffer[1] == 0x01: + self.state = Z2Tracer.send_blob_cmd + self.want_bytes = 10 + elif self.txbuffer[0] == 0x1e and self.txbuffer[1] == 0x33: + self.state = Z2Tracer.send_rmw_cmd + self.want_bytes = 16 + elif self.txbuffer[0] == 0xee and self.txbuffer[1] == 0x00: + self.state = Z2Tracer.main_z2 + self.want_bytes = 16 + else: + self.error() + def send_blob_cmd(self): + length = (self.txbuffer[2] << 8 | self.txbuffer[3]) * 4 + self.consume_bytes(10) + self.want_bytes = length + 4 + self.log(f"Sending blob of length {length}") + self.state = Z2Tracer.send_blob_tail + def send_blob_tail(self): + self.log("Finished sendind blob") + self.consume_bytes(self.want_bytes) + self.want_bytes = 2 + self.state = Z2Tracer.main_hbpp + def send_rmw_cmd(self): + self.log('Sent RMW command') + self.want_bytes = 2 + self.consume_bytes(16) + self.state = Z2Tracer.main_hbpp + def main_z2(self): + if self.txbuffer[0] == 0xee: + self.log("sent wake cmd") + self.consume_bytes(16) + elif self.txbuffer[0] == 0xe2: + self.log("sent get device info cmd") + self.consume_bytes(16) + self.state = Z2Tracer.read_device_info_reply + elif self.txbuffer[0] == 0xeb: + length = (self.rxbuffer[1] | (self.rxbuffer[2] << 8)) + 5 + length = (length + 3) & (-4) + self.consume_bytes(16) + self.want_bytes = length + self.state = Z2Tracer.read_interrupt_data + elif self.txbuffer[0] == 0xe3: + self.log(f"got report info for {self.txbuffer[1]}, len is {self.rxbuffer[3]}") + self.consume_bytes(16) + elif self.txbuffer[0] == 0xe7: + self.want_bytes = self.txbuffer[3] + 5 + self.consume_bytes(16) + self.state = Z2Tracer.reading_report_long + elif self.txbuffer[0] == 0xe6: + self.consume_bytes(16) + self.state = Z2Tracer.read_report_reply + else: + self.error() + def reading_report_long(self): + self.log(f"got report {' '.join(hex(x) for x in self.rxbuffer)}") + self.consume_bytes(self.want_bytes) + self.want_bytes = 16 + self.state = Z2Tracer.main_z2 + def read_interrupt_data(self): + data = self.rxbuffer[5:] + tstamp2 = data[4] | (data[5] << 8) | (data[6] << 16) + tx = [f"TS1 {hex(data[1])} TS2 {tstamp2} UNK1: {mxformat(data[7:16])} UNK2: {mxformat(data[17:24])}"] + if len(data) >= 16: + ntouch = data[16] + for i in range(ntouch): + ptr = 24 + 30 * i + finger = data[ptr] + state = data[ptr + 1] + x = data[ptr + 4] | (data[ptr + 5] << 8) + y = data[ptr + 6] | (data[ptr + 7] << 8) + wj = data[ptr + 12] | (data[ptr + 13] << 8) + wn = data[ptr + 14] | (data[ptr + 15] << 8) + dg = data[ptr + 16] | (data[ptr + 17] << 8) + prs = data[ptr + 18] | (data[ptr + 19] << 8) + tx.append(f"F: {hex(finger)} S: {hex(state)} X: {x} Y: {y} MAJ: {wj} MIN: {wn} ANG: {dg} PRS: {prs} UNK1: {mxformat(data[ptr + 2:ptr+4])} UNK2: {mxformat(data[ptr + 8:ptr+12])} UNK3: {mxformat(data[ptr + 20:ptr+30])}") + self.log(';'.join(tx)) + else: + self.log(f"??? {mxformat(data)}") + self.consume_bytes(self.want_bytes) + self.want_bytes = 16 + self.state = Z2Tracer.main_z2 + def read_device_info_reply(self): + self.log(f"got device info {' '.join(hex(x) for x in self.rxbuffer[:16])}") + self.consume_bytes(16) + self.state = Z2Tracer.main_z2 + def read_report_reply(self): + self.log(f"got report {' '.join(hex(x) for x in self.rxbuffer[:16])}") + self.consume_bytes(16) + self.state = Z2Tracer.main_z2 + +def mxformat(ls): + return ''.join(xformat(x) for x in ls) +def xformat(x): + x = hex(x)[2:] + if len(x) == 1: + x = '0' + x + return x + + + +# trace interrupts +aic_phandle = getattr(hv.adt["/arm-io/aic"], "AAPL,phandle") +spi_node = kbd_node._parent + +#if getattr(spi_node, "interrupt-parent") == aic_phandle: +# for irq in getattr(spi_node, "interrupts"): +# hv.trace_irq(node.name, irq, 1, hv.IRQTRACE_IRQ) +#for irq in hv.adt['/arm-io/gpio'].interrupts: +# hv.trace_irq('/arm-io/gpio', irq, 1, hv.IRQTRACE_IRQ) + +spi_tracer = Z2Tracer(hv, "/arm-io/" + spi_node.name) +spi_tracer.start() + +spi_pins_nub = { + 0x0: "clock32khz", +} + +#gpio_tracer_nub = GPIOTracer(hv, "/arm-io/nub-gpio", spi_pins_nub, verbose=0) +#gpio_tracer_nub.start() + +spi_pins = { + 0x6d: "enable_cs", + 0x8b: "reset" +} + +#gpio_tracer = GPIOTracer(hv, "/arm-io/gpio", spi_pins, verbose=0) +#gpio_tracer.start() |
