summaryrefslogtreecommitdiff
path: root/tools/proxyclient/hv
diff options
context:
space:
mode:
authormagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
committermagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
commite80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch)
tree79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/hv
add m1n1HEADmaster
Diffstat (limited to 'tools/proxyclient/hv')
-rw-r--r--tools/proxyclient/hv/README.md4
-rw-r--r--tools/proxyclient/hv/trace_agx.py107
-rw-r--r--tools/proxyclient/hv/trace_agx_defer.py35
-rw-r--r--tools/proxyclient/hv/trace_agx_pwr.py158
-rw-r--r--tools/proxyclient/hv/trace_all.py25
-rw-r--r--tools/proxyclient/hv/trace_all_more.py23
-rw-r--r--tools/proxyclient/hv/trace_aop.py349
-rw-r--r--tools/proxyclient/hv/trace_atc.py61
-rw-r--r--tools/proxyclient/hv/trace_cd3217.py119
-rw-r--r--tools/proxyclient/hv/trace_codecs.py45
-rw-r--r--tools/proxyclient/hv/trace_dart.py27
-rw-r--r--tools/proxyclient/hv/trace_dcp.py1113
-rw-r--r--tools/proxyclient/hv/trace_gpio.py40
-rw-r--r--tools/proxyclient/hv/trace_i2c.py41
-rw-r--r--tools/proxyclient/hv/trace_isp.py5
-rw-r--r--tools/proxyclient/hv/trace_keyboard.py206
-rw-r--r--tools/proxyclient/hv/trace_mesa.py212
-rw-r--r--tools/proxyclient/hv/trace_nvme.py275
-rw-r--r--tools/proxyclient/hv/trace_pmgr.py170
-rw-r--r--tools/proxyclient/hv/trace_prores.py88
-rw-r--r--tools/proxyclient/hv/trace_smc.py108
-rw-r--r--tools/proxyclient/hv/trace_wlan.py446
-rw-r--r--tools/proxyclient/hv/trace_z2.py221
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()