summaryrefslogtreecommitdiff
path: root/tools/proxyclient/m1n1/trace
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/m1n1/trace
add m1n1HEADmaster
Diffstat (limited to 'tools/proxyclient/m1n1/trace')
-rw-r--r--tools/proxyclient/m1n1/trace/__init__.py220
-rw-r--r--tools/proxyclient/m1n1/trace/agx.py1148
-rw-r--r--tools/proxyclient/m1n1/trace/asc.py271
-rw-r--r--tools/proxyclient/m1n1/trace/dart.py69
-rw-r--r--tools/proxyclient/m1n1/trace/dockchannel.py37
-rw-r--r--tools/proxyclient/m1n1/trace/gpio.py67
-rw-r--r--tools/proxyclient/m1n1/trace/i2c.py225
-rw-r--r--tools/proxyclient/m1n1/trace/isp.py118
-rw-r--r--tools/proxyclient/m1n1/trace/pcie.py108
-rw-r--r--tools/proxyclient/m1n1/trace/spi.py10
10 files changed, 2273 insertions, 0 deletions
diff --git a/tools/proxyclient/m1n1/trace/__init__.py b/tools/proxyclient/m1n1/trace/__init__.py
new file mode 100644
index 0000000..73d1a10
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/__init__.py
@@ -0,0 +1,220 @@
+# SPDX-License-Identifier: MIT
+
+from ..hv import TraceMode
+from ..utils import *
+
+__all__ = []
+
+class RegCacheAlwaysCached(Reloadable):
+ def __init__(self, parent):
+ self.parent = parent
+
+ def read(self, addr, width):
+ return self.parent.read_cached(addr, width)
+
+ def write(self, addr, data, width):
+ raise Exception("Trying to write a register to the cache")
+
+class RegCache(Reloadable):
+ def __init__(self, hv):
+ self.hv = hv
+ self.u = hv.u
+ self.cache = {}
+
+ self.cached = RegCacheAlwaysCached(self)
+
+ def update(self, addr, data):
+ self.cache[addr] = data
+
+ def read(self, addr, width):
+ if self.hv.ctx or not self.hv.started:
+ data = self.u.read(addr, width)
+ self.cache[addr] = data
+ return data
+ else:
+ return self.read_cached(addr, width)
+
+ def read_cached(self, addr, width):
+ data = self.cache.get(addr, None)
+ if data is None:
+ print(f"RegCache: no cache for {addr:#x}")
+ return data
+
+ def write(self, addr, data, width):
+ if self.hv.ctx:
+ self.u.write(addr, data, width)
+ self.cache[addr] = data
+ else:
+ raise Exception("Cannot write register in asynchronous context")
+
+class TracerState:
+ pass
+
+class Tracer(Reloadable):
+ DEFAULT_MODE = TraceMode.ASYNC
+
+ def __init__(self, hv, verbose=False, ident=None):
+ self.hv = hv
+ self.ident = ident or type(self).__name__
+ self.regmaps = {}
+ self.verbose = verbose
+ self.state = TracerState()
+ self.init_state()
+ self._cache = RegCache(hv)
+ cache = hv.tracer_caches.get(self.ident, None)
+ if cache is not None:
+ self._cache.cache.update(cache.get("regcache", {}))
+ self.state.__dict__.update(cache.get("state", {}))
+ hv.tracer_caches[self.ident] = {
+ "regcache": self._cache.cache,
+ "state": self.state.__dict__
+ }
+
+ def init_state(self):
+ pass
+
+ def hook_w(self, addr, val, width, **kwargs):
+ self.hv.u.write(addr, val, width)
+
+ def hook_r(self, addr, width, **kwargs):
+ return self.hv.u.read(addr, width)
+
+ def evt_rw(self, evt, regmap=None, prefix=None):
+ self._cache.update(evt.addr, evt.data)
+ reg = rcls = None
+ value = evt.data
+
+ t = "w" if evt.flags.WRITE else "r"
+
+ if regmap is not None:
+ reg, index, rcls = regmap.lookup_addr(evt.addr)
+ if rcls is not None:
+ value = rcls(evt.data)
+
+ if self.verbose >= 3 or (reg is None and self.verbose >= 1):
+ if reg is None:
+ s = f"{evt.addr:#x} = {value:#x}"
+ else:
+ s = f"{regmap.get_name(evt.addr)} = {value!s}"
+ m = "+" if evt.flags.MULTI else " "
+ self.log(f"MMIO: {t.upper()}.{1<<evt.flags.WIDTH:<2}{m} " + s)
+
+ if reg is not None:
+ if prefix is not None:
+ attr = f"{t}_{prefix}_{reg}"
+ else:
+ attr = f"{t}_{reg}"
+ handler = getattr(self, attr, None)
+ if handler:
+ if index is not None:
+ handler(value, index)
+ else:
+ handler(value)
+ elif self.verbose == 2:
+ s = f"{regmap.get_name(evt.addr)} = {value!s}"
+ m = "+" if evt.flags.MULTI else " "
+ self.log(f"MMIO: {t.upper()}.{1<<evt.flags.WIDTH:<2}{m} " + s)
+
+ def trace(self, start, size, mode, read=True, write=True, **kwargs):
+ zone = irange(start, size)
+ if mode == TraceMode.HOOK:
+ self.hv.add_tracer(zone, self.ident, mode, self.hook_r if read else None,
+ self.hook_w if write else None, **kwargs)
+ else:
+ self.hv.add_tracer(zone, self.ident, mode, self.evt_rw if read else None,
+ self.evt_rw if write else None, **kwargs)
+
+ def trace_regmap(self, start, size, cls, mode=None, name=None, prefix=None, regmap_offset=0):
+ if mode is None:
+ mode = self.DEFAULT_MODE
+ if name is None:
+ name = cls.__name__
+
+ regmap = self.regmaps.get(start - regmap_offset, None)
+ if regmap is None:
+ regmap = cls(self._cache, start - regmap_offset)
+ regmap.cached = cls(self._cache.cached, start - regmap_offset)
+ self.regmaps[start - regmap_offset] = regmap
+ else:
+ assert isinstance(regmap, cls)
+
+ setattr(self, name, regmap)
+ self.trace(start, size, mode=mode, regmap=regmap, prefix=prefix)
+
+ def start(self):
+ pass
+
+ def stop(self):
+ self.hv.clear_tracers(self.ident)
+
+ def log(self, msg, show_cpu=True):
+ self.hv.log(f"[{self.ident}] {msg}", show_cpu=show_cpu)
+
+class PrintTracer(Tracer):
+ def __init__(self, hv, device_addr_tbl):
+ super().__init__(hv)
+ self.device_addr_tbl = device_addr_tbl
+ self.log_file = None
+
+ def event_mmio(self, evt, name=None, start=None):
+ dev, zone2 = self.device_addr_tbl.lookup(evt.addr)
+ if name is None:
+ name = dev
+ start = zone2.start
+ t = "W" if evt.flags.WRITE else "R"
+ m = "+" if evt.flags.MULTI else " "
+ logline = (f"[cpu{evt.flags.CPU}] [0x{evt.pc:016x}] MMIO: {t}.{1<<evt.flags.WIDTH:<2}{m} " +
+ f"0x{evt.addr:x} ({name}, offset {evt.addr - start:#04x}) = 0x{evt.data:x}")
+ print(logline)
+ if self.log_file:
+ self.log_file.write(f"# {logline}\n")
+ width = 8 << evt.flags.WIDTH
+ if evt.flags.WRITE:
+ stmt = f"p.write{width}({start:#x} + {evt.addr - start:#x}, {evt.data:#x})\n"
+ else:
+ stmt = f"p.read{width}({start:#x} + {evt.addr - start:#x})\n"
+ self.log_file.write(stmt)
+
+class ADTDevTracer(Tracer):
+ REGMAPS = []
+ NAMES = []
+ PREFIXES = []
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, verbose=verbose, ident=type(self).__name__ + "@" + devpath)
+ self.dev = hv.adt[devpath]
+
+ @classmethod
+ def _reloadcls(cls, force=False):
+ regmaps = []
+ for i in cls.REGMAPS:
+ if i is None:
+ reloaded = None
+ elif isinstance(i, tuple):
+ reloaded = (i[0]._reloadcls(force), i[1])
+ else:
+ reloaded = i._reloadcls(force)
+ regmaps.append(reloaded)
+ cls.REGMAPS = regmaps
+
+ return super()._reloadcls(force)
+
+ def start(self):
+ for i in range(len(self.dev.reg)):
+ if i >= len(self.REGMAPS) or (regmap := self.REGMAPS[i]) is None:
+ continue
+ if isinstance(regmap, tuple):
+ regmap, regmap_offset = regmap
+ else:
+ regmap_offset = 0
+ prefix = name = None
+ if i < len(self.NAMES):
+ name = self.NAMES[i]
+ if i < len(self.PREFIXES):
+ prefix = self.PREFIXES[i]
+
+ start, size = self.dev.get_reg(i)
+ self.trace_regmap(start, size, regmap, name=name, prefix=prefix, regmap_offset=regmap_offset)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__.startswith(__name__))
diff --git a/tools/proxyclient/m1n1/trace/agx.py b/tools/proxyclient/m1n1/trace/agx.py
new file mode 100644
index 0000000..f46a063
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/agx.py
@@ -0,0 +1,1148 @@
+# SPDX-License-Identifier: MIT
+
+import textwrap
+from .asc import *
+from ..hw.uat import UAT, MemoryAttr, PTE, Page_PTE, TTBR
+from ..hw.agx import *
+
+from ..fw.agx.initdata import InitData
+from ..fw.agx.channels import *
+from ..fw.agx.cmdqueue import *
+from ..fw.agx.microsequence import *
+from ..fw.agx.handoff import *
+
+from m1n1.proxyutils import RegMonitor
+from m1n1.constructutils import *
+from m1n1.trace import Tracer
+
+from construct import *
+
+class ChannelTraceState(object):
+ pass
+
+class CommandQueueState(object):
+ pass
+
+class GpuMsg(Register64):
+ TYPE = 55, 48
+
+class PongMsg(GpuMsg):
+ TYPE = 59, 52
+ UNK = 47, 0
+
+class PongEp(EP):
+ # This endpoint recives pongs. The cpu code reads some status registers after receiving one
+ # Might be a "work done" message.
+ BASE_MESSAGE = GpuMsg
+
+ @msg(0x42, DIR.RX, PongMsg)
+ def pong_rx(self, msg):
+ if self.tracer.state.active:
+ self.log(f" Pong {msg!s}")
+ if msg.UNK != 0:
+ self.log(f" Pong had unexpected value{msg.UNK:x}")
+ self.hv.run_shell()
+
+ self.tracer.pong()
+ return True
+
+ @msg(0x81, DIR.TX, PongMsg)
+ def init_ep(self, msg):
+ self.log(f" Init {msg.UNK:x}")
+
+ self.tracer.pong_init(msg.UNK)
+ return True
+
+class KickMsg(GpuMsg):
+ TYPE = 59, 52
+ KICK = 7, 0 # Seen: 17, 16 (common), 9, 8, 1 (common), 0 (common)
+
+class KickEp(EP):
+ BASE_MESSAGE = GpuMsg
+
+ @msg(0x83, DIR.TX, KickMsg)
+ def kick(self, msg):
+ if self.tracer.state.active:
+ self.log(f" Kick {msg}")
+ self.tracer.kick(msg.KICK)
+
+ return True
+
+ @msg(0x84, DIR.TX, KickMsg)
+ def fwkick(self, msg):
+ if self.tracer.state.active:
+ self.log(f" FWRing Kick {msg}")
+ self.tracer.fwkick(msg.KICK)
+ return True
+
+class ChannelTracer(Reloadable):
+ STATE_FIELDS = ChannelStateFields
+ WPTR = 0x20
+ RPTR = 0x00
+
+ def __init__(self, tracer, info, index):
+ self.tracer = tracer
+ self.uat = tracer.uat
+ self.hv = tracer.hv
+ self.u = self.hv.u
+ self.ring_count = len(channelRings[index])
+ self.verbose = False
+
+ if index not in tracer.state.channels:
+ self.state = ChannelTraceState()
+ self.state.active = True
+ self.state.tail = [0] * self.ring_count
+ tracer.state.channels[index] = self.state
+ else:
+ self.state = tracer.state.channels[index]
+
+ self.index = index
+ self.name = channelNames[index]
+ self.info = info
+ base = None
+
+ if self.name == "FWLog":
+ base = self.tracer.state.fwlog_ring2
+
+ self.channel = Channel(self.u, self.uat, self.info, channelRings[index], base=base,
+ state_fields=self.STATE_FIELDS)
+ for i in range(self.ring_count):
+ for addr, size in self.channel.rb_maps[i]:
+ self.log(f"rb_map[{i}] {addr:#x} ({size:#x})")
+
+ self.set_active(self.state.active)
+
+ def state_read(self, evt, regmap=None, prefix=None, off=None):
+ ring = off // 0x30
+ off = off % 0x30
+
+ msgcls, size, count = self.channel.ring_defs[ring]
+
+ if off == self.WPTR:
+ if self.verbose:
+ self.log(f"RD [{evt.addr:#x}] WPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ elif off == self.RPTR:
+ if self.verbose:
+ self.log(f"RD [{evt.addr:#x}] RPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ else:
+ if self.verbose:
+ self.log(f"RD [{evt.addr:#x}] UNK[{ring}] {off:#x} = {evt.data:#x}")
+
+ def state_write(self, evt, regmap=None, prefix=None, off=None):
+ ring = off // 0x30
+ off = off % 0x30
+
+ msgcls, size, count = self.channel.ring_defs[ring]
+
+ if off == self.WPTR:
+ if self.verbose:
+ self.log(f"WR [{evt.addr:#x}] WPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ elif off == self.RPTR:
+ if self.verbose:
+ self.log(f"WR [{evt.addr:#x}] RPTR[{ring}] = {evt.data:#x}")
+ self.poll_ring(ring)
+ # Clear message with test pattern
+ idx = (evt.data - 1) % count
+ self.channel.clear_message(ring, idx)
+ else:
+ if self.verbose:
+ self.log(f"WR [{evt.addr:#x}] UNK[{ring}] {off:#x} = {evt.data:#x}")
+
+ def log(self, msg):
+ self.tracer.log(f"[{self.index}:{self.name}] {msg}")
+
+ def poll(self):
+ for i in range(self.ring_count):
+ self.poll_ring(i)
+
+ def poll_ring(self, ring):
+ msgcls, size, count = self.channel.ring_defs[ring]
+
+ cur = self.state.tail[ring]
+ tail = self.channel.state[ring].WRITE_PTR.val
+ if tail >= count:
+ raise Exception(f"Message index {tail:#x} >= {count:#x}")
+ if cur != tail:
+ #self.log(f"{cur:#x} -> {tail:#x}")
+ while cur != tail:
+ msg = self.channel.get_message(ring, cur, self.tracer.meta_gpuvm)
+ self.log(f"Message @{ring}.{cur}:\n{msg!s}")
+ self.tracer.handle_ringmsg(msg)
+ #if self.index < 12:
+ #self.hv.run_shell()
+ cur = (cur + 1) % count
+ self.state.tail[ring] = cur
+
+ def set_active(self, active=True):
+ if active:
+ if not self.state.active:
+ for ring in range(self.ring_count):
+ self.state.tail[ring] = self.channel.state[ring].WRITE_PTR.val
+
+ for base in range(0, 0x30 * self.ring_count, 0x30):
+ p = self.uat.iotranslate(0, self.channel.state_addr + base + self.RPTR, 4)[0][0]
+ self.hv.add_tracer(irange(p, 4),
+ f"ChannelTracer/{self.name}",
+ mode=TraceMode.SYNC,
+ read=self.state_read,
+ write=self.state_write,
+ off=base + self.RPTR)
+ p = self.uat.iotranslate(0, self.channel.state_addr + base + self.WPTR, 4)[0][0]
+ self.hv.add_tracer(irange(p, 4),
+ f"ChannelTracer/{self.name}",
+ mode=TraceMode.SYNC,
+ read=self.state_read,
+ write=self.state_write,
+ off=base + self.WPTR)
+ else:
+ self.hv.clear_tracers(f"ChannelTracer/{self.name}")
+ self.state.active = active
+
+ChannelTracer = ChannelTracer._reloadcls()
+CommandQueueInfo = CommandQueueInfo._reloadcls()
+
+class FWCtlChannelTracer(ChannelTracer):
+ STATE_FIELDS = FWControlStateFields
+ WPTR = 0x10
+ RPTR = 0x00
+
+class CommandQueueTracer(Reloadable):
+ def __init__(self, tracer, info_addr, new_queue):
+ self.tracer = tracer
+ self.uat = tracer.uat
+ self.hv = tracer.hv
+ self.u = self.hv.u
+ self.verbose = False
+ self.info_addr = info_addr
+
+ if info_addr not in tracer.state.queues:
+ self.state = CommandQueueState()
+ self.state.rptr = None
+ self.state.active = True
+ tracer.state.queues[info_addr] = self.state
+ else:
+ self.state = tracer.state.queues[info_addr]
+
+ if new_queue:
+ self.state.rptr = 0
+
+ self.update_info()
+
+ def update_info(self):
+ self.info = CommandQueueInfo.parse_stream(self.tracer.get_stream(0, self.info_addr))
+
+ def log(self, msg):
+ self.tracer.log(f"[CQ@{self.info_addr:#x}] {msg}")
+
+ @property
+ def rb_size(self):
+ return self.info.pointers.rb_size
+
+ def get_workitems(self, workmsg):
+ self.tracer.uat.invalidate_cache()
+ self.update_info()
+
+ if self.state.rptr is None:
+ self.state.rptr = int(self.info.pointers.gpu_doneptr)
+ self.log(f"Initializing rptr to {self.info.gpu_rptr1:#x}")
+
+ self.log(f"Got workmsg: wptr={workmsg.head:#x} rptr={self.state.rptr:#x}")
+ self.log(f"Queue info: {self.info}")
+
+
+ assert self.state.rptr < self.rb_size
+ assert workmsg.head < self.rb_size
+
+ stream = self.tracer.get_stream(0, self.info.rb_addr)
+
+ count = 0
+ orig_rptr = rptr = self.state.rptr
+ while rptr != workmsg.head:
+ count += 1
+ stream.seek(self.info.rb_addr + rptr * 8, 0)
+ pointer = Int64ul.parse_stream(stream)
+ self.log(f"WI item @{rptr:#x}: {pointer:#x}")
+ if pointer:
+ stream.seek(pointer, 0)
+ yield CmdBufWork.parse_stream(stream)
+ rptr = (rptr + 1) % self.rb_size
+
+ self.state.rptr = rptr
+
+ self.log(f"Parsed {count} items from {orig_rptr:#x} to {workmsg.head:#x}")
+
+ def set_active(self, active=True):
+ if not active:
+ self.state.rptr = None
+ self.state.active = active
+
+CmdBufWork = CmdBufWork._reloadcls()
+CommandQueueTracer = CommandQueueTracer._reloadcls()
+InitData = InitData._reloadcls(True)
+
+class HandoffTracer(Tracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ def __init__(self, hv, agx_tracer, base, verbose=False):
+ super().__init__(hv, verbose=verbose)
+ self.agx_tracer = agx_tracer
+ self.base = base
+
+ def start(self):
+ self.trace_regmap(self.base, 0x4000, GFXHandoffStruct, name="regs")
+
+class SGXTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.HOOK
+
+ REGMAPS = [SGXRegs, SGXInfoRegs]
+ NAMES = ["sgx", "sgx-id"]
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, devpath, verbose=verbose)
+ self.hooks = {}
+
+ def hook_r(self, addr, width, **kwargs):
+ self.log(f"HOOK: {addr:#x}:{width}")
+
+ if addr in self.hooks:
+ val = self.hooks[addr]
+ self.log(f" Returning: {val:#x}")
+ else:
+ xval = val = super().hook_r(addr, width, **kwargs)
+ if isinstance(val, (list, tuple)):
+ xval = list(map(hex, val))
+ else:
+ xval = hex(val)
+ self.log(f" Read: {xval}")
+
+ return val
+
+ def hook_w(self, addr, val, width, **kwargs):
+ if isinstance(val, (list, tuple)):
+ xval = list(map(hex, val))
+ else:
+ xval = hex(val)
+
+ self.log(f"HOOK: {addr:#x}:{width} = {xval}")
+
+ super().hook_w(addr, val, width, **kwargs)
+
+class AGXTracer(ASCTracer):
+ ENDPOINTS = {
+ 0x20: PongEp,
+ 0x21: KickEp
+ }
+
+ REGMAPS = [ASCRegs]
+ NAMES = ["asc"]
+
+ PAGESIZE = 0x4000
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, devpath, verbose)
+ self.channels = []
+ self.uat = UAT(hv.iface, hv.u, hv)
+ self.mon = RegMonitor(hv.u, ascii=True, log=hv.log)
+ self.dev_sgx = hv.u.adt["/arm-io/sgx"]
+ self.sgx = SGXRegs(hv.u, self.dev_sgx.get_reg(0)[0])
+ self.gpu_region = getattr(self.dev_sgx, "gpu-region-base")
+ self.gpu_region_size = getattr(self.dev_sgx, "gpu-region-size")
+ self.gfx_shared_region = getattr(self.dev_sgx, "gfx-shared-region-base")
+ self.gfx_shared_region_size = getattr(self.dev_sgx, "gfx-shared-region-size")
+ self.gfx_handoff = getattr(self.dev_sgx, "gfx-handoff-base")
+ self.gfx_handoff_size = getattr(self.dev_sgx, "gfx-handoff-size")
+
+ self.handoff_tracer = HandoffTracer(hv, self, self.gfx_handoff, verbose=2)
+
+ self.ignorelist = []
+ self.last_msg = None
+
+ # self.mon.add(self.gpu_region, self.gpu_region_size, "contexts")
+ # self.mon.add(self.gfx_shared_region, self.gfx_shared_region_size, "gfx-shared")
+ # self.mon.add(self.gfx_handoff, self.gfx_handoff_size, "gfx-handoff")
+
+ self.trace_kernva = False
+ self.trace_userva = False
+ self.trace_kernmap = True
+ self.trace_usermap = True
+ self.pause_after_init = False
+ self.shell_after_init = False
+ self.after_init_hook = None
+ self.encoder_id_filter = None
+ self.redump = False
+
+ self.vmcnt = 0
+ self.readlog = {}
+ self.writelog = {}
+ self.cmdqueues = {}
+ self.va_to_pa = {}
+
+ self.last_ta = None
+ self.last_3d = None
+
+
+ def get_cmdqueue(self, info_addr, new_queue):
+ if info_addr in self.cmdqueues and not new_queue:
+ return self.cmdqueues[info_addr]
+
+ cmdqueue = CommandQueueTracer(self, info_addr, new_queue)
+ self.cmdqueues[info_addr] = cmdqueue
+
+ return cmdqueue
+
+ def clear_ttbr_tracers(self):
+ self.hv.clear_tracers(f"UATTTBRTracer")
+
+ def add_ttbr_tracers(self):
+ self.hv.add_tracer(irange(self.gpu_region, UAT.NUM_CONTEXTS * 16),
+ f"UATTTBRTracer",
+ mode=TraceMode.WSYNC,
+ write=self.uat_write,
+ iova=0,
+ base=self.gpu_region,
+ level=3)
+
+ def clear_uatmap_tracers(self, ctx=None):
+ if ctx is None:
+ for i in range(UAT.NUM_CONTEXTS):
+ self.clear_uatmap_tracers(i)
+ else:
+ self.hv.clear_tracers(f"UATMapTracer/{ctx}")
+
+ def add_uatmap_tracers(self, ctx=None):
+ self.log(f"add_uatmap_tracers({ctx})")
+ if ctx is None:
+ if self.trace_kernmap:
+ self.add_uatmap_tracers(0)
+ if self.trace_usermap:
+ for i in range(1, UAT.NUM_CONTEXTS):
+ self.add_uatmap_tracers(i)
+ return
+
+ if ctx != 0 and not self.trace_usermap:
+ return
+ if ctx == 0 and not self.trace_kernmap:
+ return
+
+ def trace_pt(start, end, idx, pte, level, sparse):
+ if start >= 0xf8000000000 and (ctx != 0 or not self.trace_kernmap):
+ return
+ if start < 0xf8000000000 and not self.trace_usermap:
+ return
+ self.log(f"Add UATMapTracer/{ctx} {start:#x}")
+ self.hv.add_tracer(irange(pte.offset(), 0x4000),
+ f"UATMapTracer/{ctx}",
+ mode=TraceMode.WSYNC,
+ write=self.uat_write,
+ iova=start,
+ base=pte.offset(),
+ level=2 - level,
+ ctx=ctx)
+
+ self.uat.foreach_table(ctx, trace_pt)
+
+ def clear_gpuvm_tracers(self, ctx=None):
+ if ctx is None:
+ for i in range(UAT.NUM_CONTEXTS):
+ self.clear_gpuvm_tracers(i)
+ else:
+ self.hv.clear_tracers(f"GPUVM/{ctx}")
+
+ def add_gpuvm_tracers(self, ctx=None):
+ self.log(f"add_gpuvm_tracers({ctx})")
+ if ctx is None:
+ self.add_gpuvm_tracers(0)
+ if self.trace_userva:
+ for i in range(1, UAT.NUM_CONTEXTS):
+ self.add_gpuvm_tracers(i)
+ return
+
+ def trace_page(start, end, idx, pte, level, sparse):
+ self.uat_page_mapped(start, pte, ctx)
+
+ self.uat.foreach_page(ctx, trace_page)
+
+ def uat_write(self, evt, level=3, base=0, iova=0, ctx=None):
+ off = (evt.addr - base) // 8
+ sh = ["NS", "??", "OS", "IS"]
+ a = f"{evt.flags.ATTR:02x}:{sh[evt.flags.SH]}"
+ self.log(f"UAT <{a}> write L{level} at {ctx}:{iova:#x} (#{off:#x}) -> {evt.data}")
+
+ if level == 3:
+ ctx = off // 2
+ is_kernel = off & 1
+ if ctx != 0 and is_kernel:
+ return
+
+ if is_kernel:
+ iova += 0xf8000000000
+ pte = TTBR(evt.data)
+ if not pte.valid():
+ self.log(f"Context {ctx} invalidated")
+ self.uat.invalidate_cache()
+ self.clear_uatmap_tracers(ctx)
+ self.clear_gpuvm_tracers(ctx)
+ return
+ self.log(f"Dumping UAT for context {ctx}")
+ self.uat.invalidate_cache()
+ _, pt = self.uat.get_pt(self.uat.gpu_region + ctx * 16, 2)
+ pt[off & 1] = evt.data
+ self.uat.dump(ctx, log=self.log)
+ self.add_uatmap_tracers(ctx)
+ self.add_gpuvm_tracers(ctx)
+ else:
+ is_kernel = iova >= 0xf8000000000
+ iova += off << (level * 11 + 14)
+ if level == 0:
+ pte = Page_PTE(evt.data)
+ self.uat_page_mapped(iova, pte, ctx)
+ return
+ else:
+ pte = PTE(evt.data)
+
+ if not pte.valid():
+ try:
+ paddr = self.va_to_pa[(ctx, level, iova)]
+ except KeyError:
+ return
+ self.hv.del_tracer(irange(paddr, 0x4000),
+ f"UATMapTracer/{ctx}")
+ del self.va_to_pa[(ctx, level, iova)]
+ return
+
+ if ctx != 0 and not self.trace_usermap:
+ return
+ if ctx == 0 and not self.trace_kernmap:
+ return
+
+ self.va_to_pa[(ctx, level, iova)] = pte.offset()
+ level -= 1
+ self.hv.add_tracer(irange(pte.offset(), 0x4000),
+ f"UATMapTracer/{ctx}",
+ mode=TraceMode.WSYNC,
+ write=self.uat_write,
+ iova=iova,
+ base=pte.offset(),
+ level=level,
+ ctx=ctx)
+
+ def uat_page_mapped(self, iova, pte, ctx=0):
+ if iova >= 0xf8000000000 and ctx != 0:
+ return
+ if not pte.valid():
+ self.log(f"UAT unmap {ctx}:{iova:#x} ({pte})")
+ try:
+ paddr = self.va_to_pa[(ctx, iova)]
+ except KeyError:
+ return
+ self.hv.del_tracer(irange(paddr, 0x4000), f"GPUVM/{ctx}")
+ del self.va_to_pa[(ctx, iova)]
+ return
+
+ paddr = pte.offset()
+ self.log(f"UAT map {ctx}:{iova:#x} -> {paddr:#x} ({pte})")
+ if paddr < 0x800000000:
+ return # MMIO, ignore
+
+ if not self.trace_userva and ctx != 0 and iova < 0x80_00000000:
+ return
+ if not self.trace_kernva and ctx == 0:
+ return
+
+ self.va_to_pa[(ctx, iova)] = paddr
+ self.hv.add_tracer(irange(paddr, 0x4000),
+ f"GPUVM/{ctx}",
+ mode=TraceMode.ASYNC,
+ read=self.event_gpuvm,
+ write=self.event_gpuvm,
+ iova=iova,
+ paddr=paddr,
+ ctx=ctx)
+
+ def event_gpuvm(self, evt, iova, paddr, name=None, base=None, ctx=None):
+ off = evt.addr - paddr
+ iova += off
+
+ if evt.flags.WRITE:
+ self.writelog[iova] = (self.vmcnt, evt)
+ else:
+ self.readlog[iova] = (self.vmcnt, evt)
+ t = "W" if evt.flags.WRITE else "R"
+ m = "+" if evt.flags.MULTI else " "
+ sh = ["NS", "??", "OS", "IS"]
+ a = f"{evt.flags.ATTR:02x}:{sh[evt.flags.SH]}"
+ dinfo = ""
+ if name is not None and base is not None:
+ dinfo = f"[{name} + {iova - base:#x}]"
+ logline = (f"[cpu{evt.flags.CPU}] GPUVM[{ctx}/{self.vmcnt:5}]: <{a}>{t}.{1<<evt.flags.WIDTH:<2}{m} " +
+ f"{iova:#x}({evt.addr:#x}){dinfo} = {evt.data:#x}")
+ self.log(logline, show_cpu=False)
+ self.vmcnt += 1
+ #self.mon.poll()
+
+ def meta_gpuvm(self, iova, size):
+ meta = ""
+ iova &= 0xfffffffffff
+ for off in range(size):
+ offva = iova + off
+ if offva in self.readlog:
+ ctr, evt = self.readlog[offva]
+ m = "+" if evt.flags.MULTI else " "
+ meta += f"[R.{1<<evt.flags.WIDTH:<2}{m} @{ctr} +{off:#x}]"
+
+ if offva in self.writelog:
+ ctr, evt = self.writelog[offva]
+ m = "+" if evt.flags.MULTI else " "
+ meta += f"[W.{1<<evt.flags.WIDTH:<2}{m} @{ctr} +{off:#x}]"
+
+ return meta or None
+
+ def get_stream(self, context, off):
+ stream = self.uat.iostream(context, off)
+ stream.meta_fn = self.meta_gpuvm
+ return stream
+
+ def start(self):
+ super().start()
+
+ self.clear_ttbr_tracers()
+ self.clear_uatmap_tracers()
+ self.add_ttbr_tracers()
+ self.add_uatmap_tracers()
+ self.clear_gpuvm_tracers()
+ self.add_mon_regions()
+
+ #self.handoff_tracer.start()
+ self.init_channels()
+ if self.state.active:
+ self.resume()
+ else:
+ self.pause()
+
+ def stop(self):
+ self.pause()
+ self.handoff_tracer.stop()
+ self.clear_ttbr_tracers()
+ self.clear_uatmap_tracers()
+ self.clear_gpuvm_tracers()
+ super().stop()
+
+ def mon_addva(self, ctx, va, size, name=""):
+ self.mon.add(va, size, name, readfn= lambda a, s: self.uat.ioread(ctx, a, s))
+
+ def handle_ringmsg(self, msg):
+ if isinstance(msg, FlagMsg):
+ self.log(f"== Event flag notification ==")
+ self.handle_event(msg)
+ return
+ elif isinstance(msg, RunCmdQueueMsg):
+ self.log(f"== Work notification (type {msg.queue_type})==")
+ queue = self.get_cmdqueue(msg.cmdqueue_addr, msg.new_queue)
+ work_items = list(queue.get_workitems(msg))
+ if self.encoder_id_filter is not None:
+ for wi in work_items:
+ if wi.cmd.magic == 0:
+ # TA
+ if not self.encoder_id_filter(wi.cmd.struct_3.encoder_id):
+ return True
+ if wi.cmd.magic == 1:
+ # 3D
+ if not self.encoder_id_filter(wi.cmd.struct_6.encoder_id):
+ return True
+ for wi in work_items:
+ self.log(str(wi))
+ if msg.queue_type == 2:
+ pass
+ #return self.handle_compute(wi)
+ elif msg.queue_type == 1:
+ self.handle_3d(wi)
+ self.queue_3d = queue
+ elif msg.queue_type == 0:
+ self.handle_ta(wi)
+ self.queue_ta = queue
+ return True
+
+ def handle_event(self, msg):
+ if self.last_ta and self.redump:
+ self.log("Redumping TA...")
+ stream = self.get_stream(0, self.last_ta._addr)
+ last_ta = CmdBufWork.parse_stream(stream)
+ self.log(str(last_ta))
+ self.handle_ta(last_ta)
+ self.queue_ta.update_info()
+ self.log(f"Queue info: {self.queue_ta.info}")
+ self.last_ta = None
+ if self.last_3d and self.redump:
+ self.log("Redumping 3D...")
+ stream = self.get_stream(0, self.last_3d._addr)
+ last_3d = CmdBufWork.parse_stream(stream)
+ self.log(str(last_3d))
+ self.handle_3d(last_3d)
+ self.queue_3d.update_info()
+ self.log(f"Queue info: {self.queue_3d.info}")
+ self.last_3d = None
+
+ def dump_buffer_manager(self, buffer_mgr, kread, read):
+ return
+
+ self.log(f" buffer_mgr @ {buffer_mgr._addr:#x}: {buffer_mgr!s}")
+ self.log(f" page_list @ {buffer_mgr.page_list_addr:#x}:")
+ chexdump(read(buffer_mgr.page_list_addr,
+ buffer_mgr.page_list_size), print_fn=self.log)
+ self.log(f" block_list @ {buffer_mgr.block_list_addr:#x}:")
+ chexdump(read(buffer_mgr.block_list_addr,
+ 0x8000), print_fn=self.log)
+ #self.log(f" unkptr_d8 @ {buffer_mgr.unkptr_d8:#x}:")
+ #chexdump(read(buffer_mgr.unkptr_d8, 0x4000), print_fn=self.log)
+
+
+ def handle_ta(self, wi):
+ self.log(f"Got TA WI{wi.cmd.magic:d}")
+ self.last_ta = wi
+
+ def kread(off, size):
+ return self.uat.ioread(0, off, size)
+
+ if wi.cmd.magic == 6:
+ wi6 = wi.cmd
+ #self.log(f" unkptr_14 @ {wi6.unkptr_14:#x}:")
+ #chexdump(kread(wi6.unkptr_14, 0x100), print_fn=self.log)
+
+ elif wi.cmd.magic == 0:
+ wi0 = wi.cmd
+ context = wi0.context_id
+
+ def read(off, size):
+ return self.uat.ioread(context, off & 0x7fff_ffff_ffff_ffff, size)
+
+ #chexdump(kread(wi0.addr, 0x600), print_fn=self.log)
+ self.log(f" context_id = {context:#x}")
+ self.dump_buffer_manager(wi0.buffer_mgr, kread, read)
+ #self.log(f" unk_emptybuf @ {wi0.unk_emptybuf_addr:#x}:")
+ #chexdump(kread(wi0.unk_emptybuf_addr, 0x1000), print_fn=self.log)
+
+ #self.log(f" unkptr_48 @ {wi0.unkptr_48:#x}:")
+ #chexdump(read(wi0.unkptr_48, 0x1000), print_fn=self.log)
+ #self.log(f" unkptr_58 @ {wi0.unkptr_58:#x}:")
+ #chexdump(read(wi0.unkptr_58, 0x4000), print_fn=self.log)
+ #self.log(f" unkptr_60 @ {wi0.unkptr_60:#x}:")
+ #chexdump(read(wi0.unkptr_60, 0x4000), print_fn=self.log)
+
+ #self.log(f" unkptr_45c @ {wi0.unkptr_45c:#x}:")
+ #chexdump(read(wi0.unkptr_45c, 0x1800), print_fn=self.log)
+
+ for i in wi0.microsequence.value:
+ i = i.cmd
+ if isinstance(i, StartTACmd):
+ self.log(f" # StartTACmd")
+ self.log(f" buf_thing @ {i.buf_thing_addr:#x}: {i.buf_thing!s}")
+ self.log(f" unkptr_18 @ {i.buf_thing.unkptr_18:#x}:")
+ chexdump(read(i.buf_thing.unkptr_18, 0x100), print_fn=self.log)
+ self.log(f" unkptr_24 @ {i.unkptr_24:#x}:")
+ chexdump(read(i.unkptr_24, 0x100), print_fn=self.log)
+ self.log(f" unk_5c @ {i.unkptr_5c:#x}:")
+ chexdump(read(i.unkptr_5c, 0x100), print_fn=self.log)
+
+ elif isinstance(i, FinalizeTACmd):
+ self.log(f" # FinalizeTACmd")
+
+
+ #self.uat.dump(context, self.log)
+
+ def handle_3d(self, wi):
+ self.log(f"Got 3D WI{wi.cmdid:d}")
+ if wi.cmdid != 1:
+ return
+
+ self.last_3d = wi
+
+ def kread(off, size):
+ return self.uat.ioread(0, off, size)
+
+ if wi.cmd.magic == 4:
+ wi4 = wi.cmd
+ #self.log(f" completion_buf @ {wi4.completion_buf_addr:#x}: {wi4.completion_buf!s} ")
+ #chexdump(kread(wi4.completion_buf_addr, 0x1000), print_fn=self.log)
+ elif wi.cmd.magic == 1:
+ wi1 = wi.cmd
+ context = wi1.context_id
+ def read(off, size):
+ return self.uat.ioread(context, off, size)
+
+ self.log(f" context_id = {context:#x}")
+ cmd3d = wi1.microsequence.value[0].cmd
+
+ self.log(f" 3D:")
+ self.log(f" struct1 @ {cmd3d.struct1_addr:#x}: {cmd3d.struct1!s}")
+ self.log(f" struct2 @ {cmd3d.struct2_addr:#x}: {cmd3d.struct2!s}")
+ #self.log(f" tvb_start_addr @ {cmd3d.struct2.tvb_start_addr:#x}:")
+ #if cmd3d.struct2.tvb_start_addr:
+ #chexdump(read(cmd3d.struct2.tvb_start_addr, 0x1000), print_fn=self.log)
+ #self.log(f" tvb_tilemap_addr @ {cmd3d.struct2.tvb_tilemap_addr:#x}:")
+ #if cmd3d.struct2.tvb_tilemap_addr:
+ #chexdump(read(cmd3d.struct2.tvb_tilemap_addr, 0x1000), print_fn=self.log)
+ #self.log(f" aux_fb_ptr @ {cmd3d.struct2.aux_fb_ptr:#x}:")
+ #chexdump(read(cmd3d.struct2.aux_fb_ptr, 0x100), print_fn=self.log)
+ #self.log(f" pipeline_base @ {cmd3d.struct2.pipeline_base:#x}:")
+ #chexdump(read(cmd3d.struct2.pipeline_base, 0x100), print_fn=self.log)
+
+ self.log(f" buf_thing @ {cmd3d.buf_thing_addr:#x}: {cmd3d.buf_thing!s}")
+ #self.log(f" unkptr_18 @ {cmd3d.buf_thing.unkptr_18:#x}:")
+ #chexdump(read(cmd3d.buf_thing.unkptr_18, 0x1000), print_fn=self.log)
+
+ #self.log(f" unk_24 @ {cmd3d.unkptr_24:#x}: {cmd3d.unk_24!s}")
+ self.log(f" struct6 @ {cmd3d.struct6_addr:#x}: {cmd3d.struct6!s}")
+ #self.log(f" unknown_buffer @ {cmd3d.struct6.unknown_buffer:#x}:")
+ #chexdump(read(cmd3d.struct6.unknown_buffer, 0x1000), print_fn=self.log)
+ self.log(f" struct7 @ {cmd3d.struct7_addr:#x}: {cmd3d.struct7!s}")
+ self.log(f" unk_buf_ptr @ {cmd3d.unk_buf_ptr:#x}:")
+ chexdump(kread(cmd3d.unk_buf_ptr, 0x11c), print_fn=self.log)
+ self.log(f" unk_buf2_ptr @ {cmd3d.unk_buf2_ptr:#x}:")
+ chexdump(kread(cmd3d.unk_buf2_ptr, 0x18), print_fn=self.log)
+
+ for i in wi1.microsequence.value:
+ i = i.cmd
+ if not isinstance(i, Finalize3DCmd):
+ continue
+ self.log(f" Finalize:")
+ cmdfin = i
+ #self.log(f" completion:")
+ #chexdump(kread(cmdfin.completion, 0x4), print_fn=self.log)
+ self.log(f" unkptr_1c @ {cmdfin.unkptr_1c:#x}:")
+ chexdump(kread(cmdfin.unkptr_1c, 0x1000), print_fn=self.log)
+ #self.log(f" unkptr_24 @ {cmdfin.unkptr_24:#x}:")
+ #chexdump(kread(cmdfin.unkptr_24, 0x100), print_fn=self.log)
+ self.log(f" unkptr_34 @ {cmdfin.unkptr_34:#x}:")
+ chexdump(kread(cmdfin.unkptr_34, 0x1000), print_fn=self.log)
+ self.log(f" unkptr_3c @ {cmdfin.unkptr_3c:#x}:")
+ chexdump(kread(cmdfin.unkptr_3c, 0x1c0), print_fn=self.log)
+ self.log(f" unkptr_44 @ {cmdfin.unkptr_44:#x}:")
+ chexdump(kread(cmdfin.unkptr_44, 0x40), print_fn=self.log)
+ self.log(f" unkptr_64 @ {cmdfin.unkptr_64:#x}:")
+ chexdump(kread(cmdfin.unkptr_64, 0x118), print_fn=self.log)
+
+ self.log(f" buf_thing @ {wi1.buf_thing_addr:#x}: {wi1.buf_thing!s}")
+ self.log(f" unkptr_18 @ {wi1.buf_thing.unkptr_18:#x}:")
+ chexdump(read(wi1.buf_thing.unkptr_18, 0x1000), print_fn=self.log)
+ self.dump_buffer_manager(wi1.buffer_mgr, kread, read)
+ #self.log(f" unk_emptybuf @ {wi1.unk_emptybuf_addr:#x}:")
+ #chexdump(kread(wi1.unk_emptybuf_addr, 0x1000), print_fn=self.log)
+ #self.log(f" tvb_addr @ {wi1.tvb_addr:#x}:")
+ #chexdump(read(wi1.tvb_addr, 0x1000), print_fn=self.log)
+
+ def handle_compute(self, msg):
+ self.log("Got Compute Work Item")
+
+ try:
+ wi = msg.workItems[0].cmd
+ except:
+ return
+
+ def kread(off, size):
+ return self.uat.ioread(0, off, size)
+
+ context = wi.context_id
+
+ def read(off, size):
+ return self.uat.ioread(context, off, size)
+
+ self.log(f" context_id = {context:#x}")
+ self.log(f" unk_c @ {wi.unkptr_c:#x}: {wi.unk_c!s} ")
+ #chexdump(kread(wi.unkptr_c, 0x100), print_fn=self.log)
+ self.log(f" unkptr_0:")
+ chexdump(kread(wi.unk_c.unkptr_0, 0x1000), print_fn=self.log)
+
+ self.log("StartComputeCmd:")
+ try:
+ ccmd = wi.microsequence.value[0].cmd
+ except:
+ self.log(" MISSING!")
+ return
+ self.log(f" unkptr_4: {ccmd.unkptr_4:#x}")
+ chexdump(kread(ccmd.unkptr_4, 0x54), print_fn=self.log)
+ self.log(f" unkptr_14: {ccmd.unkptr_14:#x}")
+ chexdump(kread(ccmd.unkptr_14, 0x1000), print_fn=self.log)
+ #self.log(f" unkptr_3c: {ccmd.unkptr_3c:#x}")
+ #chexdump(kread(ccmd.unkptr_3c, 0xb4), print_fn=self.log)
+
+ ci = ccmd.computeinfo
+ self.log(f" Compute Info: {ci!s}")
+ self.log(f" args:")
+ u0data = read(ci.args, 0x8000)
+ chexdump(u0data, print_fn=self.log)
+ args = struct.unpack("<8Q", u0data[0x7fa0:0x7fe0])
+ for i, p in enumerate(args):
+ if p:
+ self.log(f" args[{i}] @ {p:#x}")
+ chexdump(read(p, 0x1000), print_fn=self.log)
+ p0, p1 = struct.unpack("<QQ", u0data[0x7fe0:0x7ff0])
+ self.log(f" p0 @ {p0:#x}")
+ chexdump(read(p0, 0x100), print_fn=self.log)
+ self.log(f" p1 @ {p1:#x}")
+ chexdump(read(p1, 0x100), print_fn=self.log)
+ self.log(f" cmdlist:")
+ chexdump(read(ci.cmdlist, 0x8000), print_fn=self.log)
+ self.log(f" unkptr_10:")
+ chexdump(read(ci.unkptr_10, 8), print_fn=self.log)
+ self.log(f" unkptr_18:")
+ chexdump(read(ci.unkptr_18, 8), print_fn=self.log)
+ self.log(f" unkptr_20:")
+ chexdump(read(ci.unkptr_20, 8), print_fn=self.log)
+ self.log(f" unkptr_28:")
+ chexdump(read(ci.unkptr_28, 8), print_fn=self.log)
+ self.log(f" pipeline:")
+ chexdump(read(ci.pipeline_base, 0x1000), print_fn=self.log)
+ self.log(f" unkptr_48:")
+ chexdump(read(ci.unkptr_48, 0x8000), print_fn=self.log)
+
+ ci2 = ccmd.computeinfo2
+ self.log(f" Compute Info 2: {ci2!s}")
+ self.log(f" unknown_buffer:")
+ chexdump(read(ci2.unknown_buffer, 0x8000), print_fn=self.log)
+
+ def ignore(self, addr=None):
+ if addr is None:
+ addr = self.last_msg.cmdqueue_addr
+ self.ignorelist += [addr & 0xfff_ffffffff]
+
+ def kick(self, val):
+ if not self.state.active:
+ return
+
+ self.log(f"kick~! {val:#x}")
+ self.mon.poll()
+
+ if val == 0x10: # Kick Firmware
+ self.log("KickFirmware, polling")
+ self.uat.invalidate_cache()
+ for chan in self.channels:
+ chan.poll()
+ return
+
+ if val == 0x11: # Device Control
+ channel = 12
+ self.uat.invalidate_cache()
+
+ elif val < 0x10:
+ type = val & 3
+ assert type != 3
+ priority = (val >> 2) & 3
+ channel = type + priority * 3
+ self.uat.invalidate_cache()
+
+ else:
+ raise(Exception("Unknown kick type"))
+
+ self.channels[channel].poll()
+
+ ## if val not in [0x0, 0x1, 0x10, 0x11]:
+ #if self.last_msg and isinstance(self.last_msg, (RunCmdQueue, DeviceControl_17)):
+ #self.hv.run_shell()
+
+ #self.last_msg = None
+
+ # check the gfx -> cpu channels
+ for chan in self.channels[13:]:
+ chan.poll()
+
+ def fwkick(self, val):
+ if not self.state.active:
+ return
+
+ self.log(f"FW Kick~! {val:#x}")
+ self.mon.poll()
+
+ if val == 0x00: # Kick FW control
+ channel = len(self.channels) - 1
+ else:
+ raise(Exception("Unknown kick type"))
+
+ self.channels[channel].poll()
+
+ # check the gfx -> cpu channels
+ for chan in self.channels[13:]:
+ chan.poll()
+
+ def pong(self):
+ if not self.state.active:
+ return
+
+ self.log("pong~!");
+ self.mon.poll()
+
+ # check the gfx -> cpu channels
+ for chan in self.channels[13:]:
+ chan.poll()
+
+ def trace_uatrange(self, ctx, start, size, name=None, off=0):
+ start &= 0xfff_ffffffff
+ ranges = self.uat.iotranslate(ctx, start, size)
+ iova = start
+ for range in ranges:
+ pstart, psize = range
+ if pstart:
+ self.log(f"trace {name} {start:#x}/{iova:#x} [{pstart:#x}:{psize:#x}] +{off:#x}")
+ self.hv.add_tracer(irange(pstart, psize), f"GPUVM",
+ mode=TraceMode.ASYNC,
+ read=self.event_gpuvm,
+ write=self.event_gpuvm,
+ iova=iova,
+ paddr=pstart,
+ name=name,
+ base=start - off)
+ iova += psize
+
+ def untrace_uatrange(self, ctx, start, size):
+ ranges = self.uat.iotranslate(ctx, start, size)
+ for range in ranges:
+ start, size = range
+ if start:
+ self.hv.del_tracer(irange(start, size), f"GPUVM")
+
+ def dump_va(self, ctx):
+ data = b''
+ dataStart = 0
+
+ def dump_page(start, end, i, pte, level, sparse):
+ if i == 0 or sparse:
+ if len(data):
+ chexdump32(data, dataStart)
+ data = b''
+ dataStart = 0
+ if MemoryAttr(pte.AttrIndex) != MemoryAttr.Device and pte.OS:
+ if dataStart == 0:
+ dataStart = start
+ data += self.uat.ioread(0, start, 0x4000)
+
+ self.uat.foreach_page(0, dump_page)
+ if len(data):
+ chexdump32(data, dataStart)
+
+ def init_state(self):
+ super().init_state()
+ self.state.active = True
+ self.state.initdata = None
+ self.state.channel_info = []
+ self.state.channels = {}
+ self.state.queues = {}
+
+ def init_channels(self):
+ if self.channels:
+ return
+ #self.channels = []
+ for i, chan_info in enumerate(self.state.channel_info):
+ print(channelNames[i], chan_info)
+ if channelNames[i] == "Stats": # ignore stats
+ continue
+ elif channelNames[i] == "KTrace": # ignore KTrace
+ continue
+ elif channelNames[i] == "FWCtl":
+ channel_chan = FWCtlChannelTracer(self, chan_info, i)
+ else:
+ channel_chan = ChannelTracer(self, chan_info, i)
+ self.channels.append(channel_chan)
+
+ def pause(self):
+ self.clear_gpuvm_tracers()
+ if self.state.initdata is None:
+ return
+ self.clear_uatmap_tracers()
+ self.clear_ttbr_tracers()
+ self.log("Pausing tracing")
+ self.state.active = False
+ for chan in self.channels:
+ chan.set_active(False)
+ for queue in self.cmdqueues.values():
+ queue.set_active(False)
+ for info_addr in self.state.queues:
+ self.state.queues[info_addr].rptr = None
+ self.untrace_uatrange(0, self.state.initdata.regionA_addr, 0x4000)
+ self.untrace_uatrange(0, self.state.initdata.regionB_addr, 0x6bc0)
+ self.untrace_uatrange(0, self.state.initdata.regionC_addr, 0x11d40)
+
+ def resume(self):
+ self.add_gpuvm_tracers()
+ self.add_uatmap_tracers()
+ self.add_ttbr_tracers()
+ if self.state.initdata is None:
+ return
+ self.log("Resuming tracing")
+ self.state.active = True
+ for chan in self.channels:
+ if chan.name == "Stats":
+ continue
+ chan.set_active(True)
+ for queue in self.cmdqueues.values():
+ queue.set_active(True)
+ self.trace_uatrange(0, self.state.initdata.regionA_addr, 0x4000, name="regionA")
+ self.trace_uatrange(0, self.state.initdata.regionB_addr, 0x6bc0, name="regionB")
+ self.trace_uatrange(0, self.state.initdata.regionC_addr, 0x11d40, name="regionC")
+ self.trace_uatrange(0, self.state.initdata.regionB.buffer_mgr_ctl_addr, 0x4000, name="Buffer manager ctl")
+
+ def add_mon_regions(self):
+ return
+ initdata = self.state.initdata
+ if initdata is not None:
+ self.mon_addva(0, initdata.regionA_addr, 0x4000, "RegionA")
+ self.mon_addva(0, initdata.regionB_addr, 0x6bc0, "RegionB")
+ self.mon_addva(0, initdata.regionC_addr, 0x11d40, "RegionC")
+ #self.mon_addva(0, initdata.regionB.unkptr_170, 0xc0, "unkptr_170")
+ #self.mon_addva(0, initdata.regionB.unkptr_178, 0x1c0, "unkptr_178")
+ #self.mon_addva(0, initdata.regionB.unkptr_180, 0x140, "unkptr_180")
+ self.mon_addva(0, initdata.regionB.unkptr_190, 0x80, "unkptr_190")
+ self.mon_addva(0, initdata.regionB.unkptr_198, 0xc0, "unkptr_198")
+ self.mon_addva(0, initdata.regionB.buffer_mgr_ctl_addr, 0x4000, "Buffer manager ctl")
+ self.mon_addva(0, initdata.unkptr_20.unkptr_0, 0x40, "unkptr_20.unkptr_0")
+ self.mon_addva(0, initdata.unkptr_20.unkptr_8, 0x40, "unkptr_20.unkptr_8")
+
+ def pong_init(self, addr):
+ self.log("UAT at init time:")
+ self.uat.invalidate_cache()
+ self.uat.dump(0, log=self.log)
+ addr |= 0xfffff000_00000000
+ initdata = InitData.parse_stream(self.get_stream(0, addr))
+
+ self.log("Initdata:")
+ self.log(initdata)
+
+ self.add_mon_regions()
+
+ #self.initdata.regionB.mon(lambda addr, size, name: self.mon_addva(0, addr, size, name))
+
+ self.state.initdata_addr = addr
+ self.state.initdata = initdata
+ self.state.channel_info = []
+ self.state.fwlog_ring2 = initdata.regionB.fwlog_ring2
+ channels = initdata.regionB.channels
+ for i in channelNames:
+ if i == "FWCtl":
+ chan_info = initdata.fw_status.fwctl_channel
+ else:
+ chan_info = channels[i]
+ self.state.channel_info.append(chan_info)
+
+ self.init_channels()
+ self.mon.poll()
+
+ self.log("Initial commands::")
+ for chan in self.channels:
+ chan.poll()
+ self.log("Init done")
+
+ self.log("Mon regions")
+ self.mon.show_regions(log=self.log)
+
+
+ if self.pause_after_init:
+ self.log("Pausing tracing")
+ self.pause()
+ self.stop()
+ if self.shell_after_init:
+ self.hv.run_shell()
+ if self.after_init_hook:
+ self.after_init_hook()
+
+ChannelTracer = ChannelTracer._reloadcls()
diff --git a/tools/proxyclient/m1n1/trace/asc.py b/tools/proxyclient/m1n1/trace/asc.py
new file mode 100644
index 0000000..6d63a50
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/asc.py
@@ -0,0 +1,271 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+from enum import IntEnum
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+from ..hw.asc import *
+
+class DIR(IntEnum):
+ RX = 0
+ TX = 1
+
+def msg(message, direction=None, regtype=None, name=None):
+ def f(x):
+ x.is_message = True
+ x.direction = direction
+ x.message = message
+ x.regtype = regtype
+ x.name = name
+ return x
+ return f
+
+def msg_log(*args, **kwargs):
+ def x(self, msg):
+ return False
+ return msg(*args, **kwargs)(x)
+
+def msg_ign(*args, **kwargs):
+ def x(self, msg):
+ return True
+ return msg(*args, **kwargs)(x)
+
+class EPState(object):
+ pass
+
+class EP(object):
+ NAME = None
+ BASE_MESSAGE = None
+
+ def __init__(self, tracer, epid):
+ self.tracer = tracer
+ self.epid = epid
+ self.present = False
+ self.started = False
+ self.name = self.NAME or type(self).__name__.lower()
+ self.state = EPState()
+ self.hv = self.tracer.hv
+ self.msgmap = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i) or not getattr(i, "is_message", False):
+ continue
+ self.msgmap[i.direction, i.message] = getattr(self, name), name, i.regtype
+
+ def log(self, msg):
+ self.tracer.log(f"[{self.name}] {msg}")
+
+ def start(self):
+ pass
+
+ def handle_msg(self, direction, r0, r1):
+ msgtype = None
+ if self.BASE_MESSAGE:
+ r0 = self.BASE_MESSAGE(r0.value)
+ msgtype = r0.TYPE
+
+ handler = None
+ name = "<unknown>"
+ regtype = None
+
+ msgids = [
+ (direction, msgtype),
+ (None, msgtype),
+ (direction, None),
+ (None, None),
+ ]
+
+ for msgid in msgids:
+ handler, name, regtype = self.msgmap.get(msgid, (None, None, None))
+ if handler:
+ break
+
+ if regtype is not None:
+ r0 = regtype(r0.value)
+
+ if handler:
+ if handler.name is not None:
+ name = handler.name
+ if handler(r0):
+ return True
+
+ d = ">" if direction == DIR.TX else "<"
+ self.log(f"{d}{msgtype:#x}({name}) {r0.value:016x} ({r0.str_fields()})")
+ return True
+
+class EPContainer(object):
+ pass
+
+class BaseASCTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [ASCRegs, None]
+ NAMES = ["asc", None]
+
+ ENDPOINTS = {}
+
+ def w_OUTBOX_CTRL(self, val):
+ self.log(f"OUTBOX_CTRL = {val!s}")
+
+ def w_INBOX_CTRL(self, val):
+ self.log(f"INBOX_CTRL = {val!s}")
+
+ def w_CPU_CONTROL(self, val):
+ self.log(f"CPU_CONTROL = {val!s}")
+
+ def w_INBOX1(self, inbox1):
+ inbox0 = self.asc.cached.INBOX0.reg
+ if self.verbose >= 2:
+ self.log(f"SEND: {inbox0.value:016x}:{inbox1.value:016x} " +
+ f"{inbox0.str_fields()} | {inbox1.str_fields()}")
+ self.handle_msg(DIR.TX, inbox0, inbox1)
+
+ def r_OUTBOX1(self, outbox1):
+ outbox0 = self.asc.cached.OUTBOX0.reg
+ if self.verbose >= 2:
+ self.log(f"RECV: {outbox0.value:016x}:{outbox1.value:016x} " +
+ f"{outbox0.str_fields()} | {outbox1.str_fields()}")
+ self.handle_msg(DIR.RX, outbox0, outbox1)
+
+ def init_state(self):
+ self.state.ep = {}
+
+ def handle_msg(self, direction, r0, r1):
+ if r1.EP in self.epmap:
+ if self.epmap[r1.EP].handle_msg(direction, r0, r1):
+ return
+
+ d = ">" if direction == DIR.TX else "<"
+ self.log(f"{d}ep:{r1.EP:02x} {r0.value:016x} ({r0.str_fields()})")
+
+ def ioread(self, dva, size):
+ if self.dart:
+ return self.dart.ioread(self.stream, dva & 0xFFFFFFFFF, size)
+ else:
+ return self.hv.iface.readmem(dva, size)
+
+ def iowrite(self, dva, data):
+ if self.dart:
+ return self.dart.iowrite(self.stream, dva & 0xFFFFFFFFF, data)
+ else:
+ return self.hv.iface.writemem(dva, data)
+
+ def start(self, dart=None, stream=0):
+ super().start()
+ self.dart = dart
+ self.stream = stream
+ self.msgmap = {}
+ for name in dir(self):
+ i = getattr(self, name)
+ if not callable(i) or not getattr(i, "is_message", False):
+ continue
+ self.msgmap[i.direction, i.endpoint, i.message] = getattr(self, name), name, i.regtype
+
+ self.epmap = {}
+ self.ep = EPContainer()
+ for cls in type(self).mro():
+ eps = getattr(cls, "ENDPOINTS", None)
+ if eps is None:
+ break
+ for k, v in eps.items():
+ if k in self.epmap:
+ continue
+ ep = v(self, k)
+ ep.dart = dart
+ ep.stream = stream
+ self.epmap[k] = ep
+ if k in self.state.ep:
+ ep.state.__dict__.update(self.state.ep[k])
+ self.state.ep[k] = ep.state.__dict__
+ if getattr(self.ep, ep.name, None):
+ ep.name = f"{ep.name}{k:02x}"
+ setattr(self.ep, ep.name, ep)
+ ep.start()
+
+# System endpoints
+
+## Management endpoint
+
+from ..fw.asc.mgmt import ManagementMessage, Mgmt_EPMap, Mgmt_EPMap_Ack, Mgmt_StartEP, Mgmt_SetAPPower, Mgmt_SetIOPPower, Mgmt_IOPPowerAck
+
+class Management(EP):
+ BASE_MESSAGE = ManagementMessage
+
+ HELLO = msg_log(1, DIR.RX)
+ HELLO_ACK = msg_log(2, DIR.TX)
+
+ @msg(5, DIR.TX, Mgmt_StartEP)
+ def StartEP(self, msg):
+ ep = self.tracer.epmap.get(msg.EP, None)
+ if ep:
+ ep.started = True
+ self.log(f" Starting endpoint #{msg.EP:#02x} ({ep.name})")
+ else:
+ self.log(f" Starting endpoint #{msg.EP:#02x}")
+ #return True
+
+ Init = msg_log(6, DIR.TX)
+
+ @msg(8, DIR.RX, Mgmt_EPMap)
+ def EPMap(self, msg):
+ for i in range(32):
+ if msg.BITMAP & (1 << i):
+ epno = 32 * msg.BASE + i
+ ep = self.tracer.epmap.get(epno, None)
+ if ep:
+ ep.present = True
+ self.log(f" Adding endpoint #{epno:#02x} ({ep.name})")
+ else:
+ self.log(f" Adding endpoint #{epno:#02x}")
+
+ EPMap_Ack = msg_log(8, DIR.TX, Mgmt_EPMap_Ack)
+
+ SetIOPPower = msg_log(6, DIR.TX, Mgmt_SetIOPPower)
+ SetIOPPowerAck = msg_log(7, DIR.TX, Mgmt_IOPPowerAck)
+
+ SetAPPower = msg_log(0x0b, DIR.TX, Mgmt_SetAPPower)
+ SetAPPowerAck = msg_log(0x0b, DIR.RX, Mgmt_SetAPPower)
+
+## Syslog endpoint
+
+from ..fw.asc.syslog import SyslogMessage, Syslog_Init, Syslog_GetBuf, Syslog_Log
+
+class Syslog(EP):
+ BASE_MESSAGE = SyslogMessage
+
+ @msg(8, DIR.RX, Syslog_Init)
+ def Init(self, msg):
+ self.state.count = msg.COUNT
+ self.state.entrysize = msg.ENTRYSIZE
+
+ @msg(1, DIR.RX, Syslog_GetBuf)
+ def GetBuf(self, msg):
+ if msg.DVA:
+ self.state.syslog_buf = msg.DVA
+
+ @msg(1, DIR.TX, Syslog_GetBuf)
+ def GetBuf_Ack(self, msg):
+ self.state.syslog_buf = msg.DVA
+
+ @msg(5, DIR.RX, Syslog_Log)
+ def Log(self, msg):
+ buf = self.state.syslog_buf
+ stride = 0x20 + self.state.entrysize
+ log = self.tracer.ioread(buf + msg.INDEX * stride, stride)
+ hdr, unk, context, logmsg = struct.unpack(f"<II24s{self.state.entrysize}s", log)
+ context = context.split(b"\x00")[0].decode("ascii")
+ logmsg = logmsg.split(b"\x00")[0].decode("ascii").rstrip("\n")
+ self.log(f"* [{context}]{logmsg}")
+ return True
+
+ Log_Ack = msg_ign(5, DIR.TX, Syslog_Log)
+
+class ASCTracer(BaseASCTracer):
+ ENDPOINTS = {
+ 0: Management,
+ #1: CrashLog,
+ 2: Syslog,
+ #3: KDebug,
+ #4: IOReporting,
+ }
diff --git a/tools/proxyclient/m1n1/trace/dart.py b/tools/proxyclient/m1n1/trace/dart.py
new file mode 100644
index 0000000..b1324f7
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/dart.py
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: MIT
+
+from ..hw.dart import *
+from ..hw.dart8020 import *
+from ..hw.dart8110 import *
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class DARTTracer(ADTDevTracer):
+
+ DEFAULT_MODE = TraceMode.ASYNC
+
+ NAMES = ["regs"]
+
+ @classmethod
+ def _reloadcls(cls, force=False):
+ global DART8020
+ global DART8020Regs
+ global DART8110
+ global DART8110Regs
+ DART8020 = DART8020._reloadcls(force)
+ DART8020Regs = DART8020Regs._reloadcls(force)
+ DART8110 = DART8110._reloadcls(force)
+ DART8110Regs = DART8110Regs._reloadcls(force)
+ return super()._reloadcls(force)
+
+ def __init__(self, hv, devpath, **kwargs):
+ compat = hv.adt[devpath].compatible[0]
+ if compat in ["dart,t6000", "dart,t8020"]:
+ self.REGMAPS = [DART8020Regs]
+ elif compat in ["dart,t8110"]:
+ self.REGMAPS = [DART8110Regs]
+
+ return super().__init__(hv, devpath, **kwargs)
+
+ def start(self):
+ super().start()
+ # prime cache
+ if self.dev.compatible[0] == "dart,t8110":
+ for i in range(16):
+ self.regs.TCR[i].val
+ self.regs.TTBR[i].val
+ for _ in range(8):
+ self.regs.ENABLE_STREAMS[_].val
+ else:
+ for i in range(16):
+ self.regs.TCR[i].val
+ for j in range(4):
+ self.regs.TTBR[i, j].val
+ self.regs.ENABLED_STREAMS.val
+
+ self.dart = DART(self.hv.iface, self.regs, compat=self.dev.compatible[0])
+
+
+ def w_STREAM_COMMAND(self, stream_command):
+ if stream_command.INVALIDATE:
+ self.log(f"Invalidate Stream: {self.regs.cached.STREAM_SELECT.reg}")
+ self.dart.invalidate_cache()
+
+ def w_TLB_OP(self, tlb_op):
+ if tlb_op.OP == 0:
+ self.log(f"Invalidate all")
+ self.dart.invalidate_cache()
+ elif tlb_op.OP == 1:
+ self.log(f"Invalidate Stream: {tlb_op.STREAM}")
+ self.dart.invalidate_cache()
+ else:
+ self.log(f"Unknown TLB op {tlb_op}")
diff --git a/tools/proxyclient/m1n1/trace/dockchannel.py b/tools/proxyclient/m1n1/trace/dockchannel.py
new file mode 100644
index 0000000..10ada58
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/dockchannel.py
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..hw.dockchannel import DockChannelIRQRegs, DockChannelConfigRegs, DockChannelDataRegs
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class DockChannelTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [None, DockChannelIRQRegs, DockChannelConfigRegs, DockChannelDataRegs]
+ NAMES = [None, "irq", "config", "data"]
+
+ def w_TX_8(self, d):
+ self.tx(struct.pack("<I", d.value)[0:1])
+ def w_TX_16(self, d):
+ self.tx(struct.pack("<I", d.value)[0:2])
+ def w_TX_24(self, d):
+ self.tx(struct.pack("<I", d.value)[0:3])
+ def w_TX_32(self, d):
+ self.tx(struct.pack("<I", d.value))
+
+ def r_RX_8(self, d):
+ self.rx(struct.pack("<I", d.value)[1:2])
+ def r_RX_16(self, d):
+ self.rx(struct.pack("<I", d.value)[1:3])
+ def r_RX_24(self, d):
+ self.rx(struct.pack("<I", d.value)[1:4])
+ def r_RX_32(self, d):
+ self.rx(struct.pack("<I", d.value))
+
+ def tx(self, d):
+ pass
+
+ def rx(self, d):
+ pass
diff --git a/tools/proxyclient/m1n1/trace/gpio.py b/tools/proxyclient/m1n1/trace/gpio.py
new file mode 100644
index 0000000..386f886
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/gpio.py
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: MIT
+
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class R_PIN(Register32):
+ DRIVE_STRENGTH1 = 23, 22
+ LOCK = 21
+ GROUP = 18, 16
+ SCHMITT = 15
+ DRIVE_STRENGTH0 = 11, 10
+ INPUT_ENABLE = 9
+ PULL = 8, 7
+ PERIPH = 6, 5
+ MODE = 3, 1
+ DATA = 0
+
+class GPIORegs(RegMap):
+ PIN = irange(0x000, 212, 4), R_PIN
+
+ IRQ_GROUP = (irange(0x800, 7, 0x40), irange(0, (212 + 31) // 32, 4)), Register32
+
+def bits32(val, start):
+ return [start + i for i in range(0, 32) if int(val) & (1 << i)]
+
+class GPIOTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.UNBUF
+
+ REGMAPS = [GPIORegs]
+ NAMES = ["gpio"]
+
+ PIN_NAMES = {}
+
+ def __init__(self, hv, devpath, pin_names={}, verbose=False):
+ super().__init__(hv, devpath, verbose)
+ self.PIN_NAMES = pin_names
+
+ def pn(self, pin):
+ return self.PIN_NAMES.get(pin, f"Pin-{pin}")
+
+ def r_PIN(self, val, index):
+ if index not in self.PIN_NAMES and self.verbose < 2:
+ return
+ self.log(f"{self.pn(index):14} R {val!s} ")
+
+ def w_PIN(self, val, index):
+ if index not in self.PIN_NAMES and self.verbose < 2:
+ return
+ self.log(f"{self.pn(index):14} W {val!s} ")
+
+ def r_IRQ_GROUP(self, val, index):
+ (grp, index) = index
+ if int(val) == 0:
+ return
+ pins = [self.pn(x) for x in bits32(val, index * 32) if self.verbose >= 2 or x in self.PIN_NAMES]
+ if len(pins):
+ self.log(f"IRQ[{grp}] ACT {pins}")
+
+ def w_IRQ_GROUP(self, val, index):
+ (grp, index) = index
+ if int(val) == 0:
+ return
+ pins = [self.pn(x) for x in bits32(val, index * 32) if self.verbose >= 2 or x in self.PIN_NAMES]
+ if len(pins):
+ self.log(f"IRQ[{grp}] ACK {pins}")
+
diff --git a/tools/proxyclient/m1n1/trace/i2c.py b/tools/proxyclient/m1n1/trace/i2c.py
new file mode 100644
index 0000000..9c4bbb6
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/i2c.py
@@ -0,0 +1,225 @@
+# SPDX-License-Identifier: MIT
+
+from ..hv import TraceMode
+from ..utils import *
+from ..hw import i2c
+from . import ADTDevTracer
+
+class I2CTracer(ADTDevTracer):
+ DEFAULT_MODE = TraceMode.ASYNC
+ REGMAPS = [i2c.I2CRegs]
+ NAMES = ["i2c"]
+
+ def __init__(self, hv, devpath, verbose=False):
+ super().__init__(hv, devpath, verbose=verbose)
+ self.default_dev = I2CDevTracer()
+ self.default_dev.i2c_tracer = self
+
+ def init_state(self):
+ self.state.txn = []
+ self.state.devices = {}
+
+ def w_MTXFIFO(self, mtxfifo):
+ if self.state.txn is None:
+ self.state.txn = []
+
+ d = mtxfifo.DATA
+ if mtxfifo.START:
+ self.state.txn += ["S"]
+ if mtxfifo.READ:
+ self.state.txn += [None] * d
+ else:
+ self.state.txn.append(d)
+
+ if mtxfifo.STOP:
+ self.state.txn.append("P")
+ self.flush_txn()
+
+ def r_MRXFIFO(self, mrxfifo):
+ if mrxfifo.EMPTY:
+ self.log(f"Read while FIFO empty")
+ return
+
+ if not self.state.txn:
+ self.log(f"Stray read: {mrxfifo}")
+ return
+
+ try:
+ pos = self.state.txn.index(None)
+ self.state.txn[pos] = mrxfifo.DATA
+ except ValueError:
+ self.log(f"Stray read: {mrxfifo}")
+
+ self.flush_txn()
+
+ def flush_txn(self):
+ if not self.state.txn:
+ return
+
+ if self.state.txn[-1] != "P":
+ return
+
+ if not any(i is None for i in self.state.txn):
+ self.handle_txn(self.state.txn)
+ self.state.txn = None
+
+ def handle_txn(self, txn):
+ st = False
+ dev = self.default_dev
+ read = False
+ for i in txn:
+ if i == "S":
+ st = True
+ continue
+ if st:
+ addr = i >> 1
+ dev = self.state.devices.get(addr, self.default_dev)
+ read = bool(i & 1)
+ dev.start(addr, read=read)
+ elif i == "P":
+ dev.stop()
+ elif read:
+ dev.read(i)
+ else:
+ dev.write(i)
+ st = False
+
+ def add_device(self, addr, device):
+ device.hv = self.hv
+ device.i2c_tracer = self
+ self.state.devices[addr] = device
+
+class I2CDevTracer(Reloadable):
+ def __init__(self, addr=None, name=None, verbose=True):
+ self.addr = addr
+ self.name = name
+ self.verbose = verbose
+ self.txn = []
+
+ def log(self, msg, *args, **kwargs):
+ if self.name:
+ msg = f"[{self.name}] {msg}"
+ self.i2c_tracer.log(msg, *args, **kwargs)
+
+ def start(self, addr, read):
+ self.txn.append("S")
+ if read:
+ self.txn.append(f"{addr:02x}.r")
+ else:
+ self.txn.append(f"{addr:02x}.w")
+
+ def stop(self):
+ self.txn.append("P")
+ if self.verbose:
+ self.log(f"Txn: {' '.join(self.txn)}")
+ self.txn = []
+
+ def read(self, data):
+ self.txn.append(f"{data:02x}")
+
+ def write(self, data):
+ self.txn.append(f"{data:02x}")
+
+class I2CRegCache:
+ def __init__(self):
+ self.cache = {}
+
+ def update(self, addr, data):
+ self.cache[addr] = data
+
+ def read(self, addr, width):
+ data = self.cache.get(addr, None)
+ if data is None:
+ print(f"I2CRegCache: no cache for {addr:#x}")
+ return data
+
+ def write(self, addr, data, width):
+ raise NotImplementedError("No write on I2CRegCache")
+
+class I2CRegMapTracer(I2CDevTracer):
+ REGMAP = RegMap
+ ADDRESSING = (0, 1)
+
+ def __init__(self, verbose=False, **kwargs):
+ super().__init__(verbose=verbose, **kwargs)
+ self._cache = I2CRegCache()
+ self.regmap = self.REGMAP(self._cache, 0)
+ self.page = 0x0
+ self.reg = None
+ self.regbytes = []
+
+ self.npagebytes, nimmbytes = self.ADDRESSING
+ self.pageshift = 8 * nimmbytes
+ self.paged = self.npagebytes != 0
+
+ def _reloadme(self):
+ self.regmap._reloadme()
+ return super()._reloadme()
+
+ def start(self, addr, read):
+ if not read:
+ self.reg = None
+ self.regbytes = []
+ super().start(addr, read)
+
+ def stop(self):
+ super().stop()
+
+ def handle_addressing(self, data):
+ if self.reg is not None:
+ return False
+
+ self.regbytes.append(data)
+ if len(self.regbytes)*8 >= self.pageshift:
+ immediate = int.from_bytes(bytes(self.regbytes),
+ byteorder="big")
+ self.reg = self.page << self.pageshift | immediate
+ return True
+
+ @property
+ def reg_imm(self):
+ '''Returns the 'immediate' part of current register address'''
+ return self.reg & ~(~0 << self.pageshift)
+
+ def handle_page_register(self, data):
+ if not self.paged:
+ return False
+
+ if self.reg_imm >= self.npagebytes:
+ return False
+
+ shift = 8 * self.reg_imm
+ self.page &= ~(0xff << shift)
+ self.page |= data << shift
+ return True
+
+ def write(self, data):
+ if self.handle_addressing(data):
+ return
+ elif self.handle_page_register(data):
+ pass
+ else:
+ self.regwrite(self.reg, data)
+
+ self.reg += 1
+ super().write(data)
+
+ def read(self, data):
+ if self.reg_imm >= self.npagebytes:
+ self.regread(self.reg, data)
+ self.reg += 1
+ super().read(data)
+
+ def regwrite(self, reg, val):
+ self.regevent(reg, val, False)
+
+ def regread(self, reg, val):
+ self.regevent(reg, val, True)
+
+ def regevent(self, reg, val, read):
+ self._cache.update(reg, val)
+ r, index, rcls = self.regmap.lookup_addr(reg)
+ val = rcls(val) if rcls is not None else f"{val:#x}"
+ regname = self.regmap.get_name(reg) if r else f"{reg:#x}"
+ t = "R" if read else "W"
+ self.log(f"REG: {t.upper()}.8 {regname} = {val!s}")
diff --git a/tools/proxyclient/m1n1/trace/isp.py b/tools/proxyclient/m1n1/trace/isp.py
new file mode 100644
index 0000000..924bce9
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/isp.py
@@ -0,0 +1,118 @@
+from . import ADTDevTracer
+from .dart import DARTTracer
+from ..hv import TraceMode
+from ..hw.dart import DART, DARTRegs
+from ..hw.isp import *
+
+class ISPTracer(ADTDevTracer):
+
+ DEFAULT_MODE = TraceMode.SYNC
+
+ REGMAPS = [ISPRegs, PSReg, SPMIReg, SPMIReg, SPMIReg]
+ NAMES = ["isp", "ps", "spmi0", "spmi1", "spmi2"]
+
+ ALLOWLISTED_CHANNELS = ["TERMINAL", "IO", "BUF_H2T", "BUF_T2H", "SHAREDMALLOC", "IO_T2H"]
+
+ def __init__(self, hv, dev_path, dart_dev_path, verbose):
+ super().__init__(hv, dev_path, verbose)
+
+ hv.p.pmgr_adt_clocks_enable("/arm-io/dart-isp")
+
+ self.dart_tracer = DARTTracer(hv, "/arm-io/dart-isp")
+ self.dart_tracer.start()
+ self.dart = self.dart_tracer.dart
+
+ self.ignored_ranges = [
+ # -----------------------------------------------------------------
+ # ## System clock counter (24 mhz)
+ (0x23b734004, 4),
+ (0x23b734008, 4),
+ # ## Noisy memory addresses that are always zero
+ (0x23b734868, 4),
+ (0x23b73486c, 4),
+ (0x23b734b38, 4),
+ (0x23b734b3c, 4),
+ (0x23b734b58, 4),
+ (0x23b734b5c, 4),
+ (0x23b734bd8, 4),
+ (0x23b734bdc, 4),
+ (0x23b734c18, 4),
+ (0x23b734c1c, 4),
+ (0x23b778128, 4),
+ (0x23b77812c, 4),
+ (0x23b77c128, 4),
+ (0x23b77c12c, 4),
+ # # Noisy memory addresses that change value
+ (0x23b700248, 4),
+ (0x23b700258, 4),
+ (0x23b7003f8, 4),
+ (0x23b700470, 4),
+ # # ECPU/PCPU state report
+ (0x23b738004, 4), # ecpu state report
+ (0x23b738008, 4), # pcpu state report
+ # -----------------------------------------------------------------
+ ]
+
+ def r_ISP_GPR0(self, val):
+ # I have no idea how many channels may be available in other platforms
+ # but, at least for M1 I know they are seven (7), so using 64 as safe value here
+ if val.value == 0x8042006:
+ self.log(f"ISP_GPR0 = ACK")
+ elif val.value < 64:
+ self.log(f"ISP_IPC_CHANNELS = {val!s}")
+ self.number_of_channels = val.value
+ elif val.value > 0:
+ self.log(f"ISP_IPC_CHANNEL_TABLE_IOVA = {val!s}")
+ self.channel_table = ISPChannelTable(self, self.number_of_channels, val.value)
+ self.log(f"{str(self.channel_table)}")
+
+ def r_ISP_IRQ_INTERRUPT(self, val):
+ pending_irq = int(val.value)
+ self.log(f"======== BEGIN IRQ ========")
+ #self.channel_table.dump()
+ self.channel_table.get_last_read_command(pending_irq)
+ self.log(f"======== END IRQ ========")
+
+ def w_ISP_DOORBELL_RING0(self, val):
+ doorbell_value = int(val.value)
+ self.log(f"======== BEGIN DOORBELL ========")
+ #self.channel_table.dump()
+ self.channel_table.get_last_write_command(doorbell_value)
+ self.log(f"======== END DOORBELL ========")
+
+ def w_ISP_GPR0(self, val):
+ self.log(f"ISP_GPR0 = ({val!s})")
+ if val.value == 0x1812f80:
+ if self.dart:
+ self.init_struct = self.dart.ioread(0, val.value & 0xFFFFFFFF, 0x190)
+
+ def w_ISP_IRQ_INTERRUPT(self, val):
+ self.log(f"IRQ_INTERRUPT = ({val!s}).")
+ if val.value == 0xf:
+ self.log(f"ISP Interrupts enabled")
+
+ def ioread(self, dva, size):
+ if self.dart:
+ return self.dart.ioread(0, dva & 0xFFFFFFFF, size)
+ else:
+ return self.hv.iface.readmem(dva, size)
+
+ def iowrite(self, dva, data):
+ if self.dart:
+ return self.dart.iowrite(0, dva & 0xFFFFFFFF, data)
+ else:
+ return self.hv.iface.writemem(dva, data)
+
+ def start(self):
+ super().start()
+
+ self.msgmap = {}
+ for name in dir(self):
+ arg = getattr(self, name)
+ if not callable(arg) or not getattr(arg, "is_message", False):
+ continue
+ self.msgmap[arg.direction, arg.endpoint, arg.message] = getattr(self, name), name, arg.regtype
+
+ # Disable trace of memory regions
+ for addr, size in self.ignored_ranges:
+ self.trace(addr, size, TraceMode.OFF) \ No newline at end of file
diff --git a/tools/proxyclient/m1n1/trace/pcie.py b/tools/proxyclient/m1n1/trace/pcie.py
new file mode 100644
index 0000000..191da84
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/pcie.py
@@ -0,0 +1,108 @@
+from . import Tracer, TraceMode
+from ..utils import *
+
+class R_BAR(Register32):
+ BASE = 31, 4
+ PREFETCH = 3
+ ADDR64 = 2
+ BELOW_1M = 1
+ SPACE = 0
+
+class PCICfgSpace(RegMap):
+ VENDOR_ID = 0x00, Register16
+ PRODUCT_ID = 0x02, Register16
+ COMMAND = 0x04, Register16
+ STATUS = 0x06, Register16
+
+ BAR = irange(0x10, 6, 4), R_BAR
+ ROMADDR = 0x30, Register32
+
+class PCIeDevTracer(Tracer):
+ CFGMAP = PCICfgSpace
+ BARMAPS = []
+ NAMES = []
+ PREFIXES = []
+
+ def __init__(self, hv, apcie, bus, dev, fn, verbose=False):
+ super().__init__(hv, verbose=verbose, ident=f"{type(self).__name__}@{apcie}/{bus:02x}:{dev:02x}.{fn:1x}")
+ self.busn = bus
+ self.devn = dev
+ self.fn = fn
+ self.ecam_off = (bus << 20) | (dev << 15) | (fn << 12)
+ self.apcie = hv.adt[apcie]
+ self.bars = [0] * 6
+ self.bar_ranges = [None] * 6
+ self.cfg_space = self.CFGMAP(hv.u, self.apcie.get_reg(0)[0] + self.ecam_off)
+ self.verbose = 3
+
+ def init_state(self):
+ self.state.bars = [R_BAR(0) for i in range(6)]
+ self.state.barsize = [None] * 6
+
+ @classmethod
+ def _reloadcls(cls, force=False):
+ cls.BARMAPS = [i._reloadcls(force) if i else None for i in cls.BARMAPS]
+ return super()._reloadcls(force)
+
+ def r_cfg_BAR(self, val, index):
+ if self.state.bars[index].BASE == 0xfffffff:
+ size = (0x10000000 - val.BASE) << 4
+ self.log(f"BAR{index} size = {size:#x}")
+ self.state.barsize[index] = size
+
+ def w_cfg_BAR(self, val, index):
+ self.state.bars[index] = val
+ self.update_tracers(val, index)
+
+ def update_tracers(self, val = None, index = None):
+ self.hv.clear_tracers(self.ident)
+ ecam = self.apcie.get_reg(0)[0]
+ self.trace_regmap(ecam + self.ecam_off, 0x1000, self.CFGMAP, prefix="cfg",
+ mode=TraceMode.WSYNC)
+ i = 0
+ while i < 6:
+ idx = i
+ if i == index:
+ bar = val
+ else:
+ bar = self.cfg_space.BAR[i].reg
+ addr = bar.BASE << 4
+ if bar.ADDR64 and i != 5:
+ if i + 1 == index:
+ barh = val
+ else:
+ barh = self.cfg_space.BAR[i + 1].reg
+ addr |= barh.value << 32
+ i += 2
+ else:
+ i += 1
+
+ if addr in (0, 0xfffffff0, 0xffffffff00000000, 0xfffffffffffffff0):
+ continue
+
+ size = self.state.barsize[idx]
+
+ if not size:
+ self.log(f"BAR{idx} size is unknown!")
+ continue
+
+ # Add in PCIe DT addr flags to get the correct translation
+ start = self.apcie.translate(addr | (0x02 << 88))
+
+ self.log(f"Tracing BAR{idx} : {addr:#x} -> {start:#x}..{start+size-1:#x}")
+ self.bar_ranges[idx] = irange(start, size)
+ self.trace_bar(idx, start, size)
+
+ def trace_bar(self, idx, start, size):
+ if idx >= len(self.BARMAPS) or (regmap := self.BARMAPS[idx]) is None:
+ return
+ prefix = name = None
+ if idx < len(self.NAMES):
+ name = self.NAMES[i]
+ if idx < len(self.PREFIXES):
+ prefix = self.PREFIXES[i]
+
+ self.trace_regmap(start, size, regmap, name=name, prefix=prefix)
+
+ def start(self):
+ self.update_tracers()
diff --git a/tools/proxyclient/m1n1/trace/spi.py b/tools/proxyclient/m1n1/trace/spi.py
new file mode 100644
index 0000000..dd4f967
--- /dev/null
+++ b/tools/proxyclient/m1n1/trace/spi.py
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: MIT
+
+from ..hw.spi import *
+from ..hv import TraceMode
+from ..utils import *
+from . import ADTDevTracer
+
+class SPITracer(ADTDevTracer):
+ REGMAPS = [SPIRegs]
+ NAMES = ["spi"]