diff options
| author | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
|---|---|---|
| committer | magh <magh@maghmogh.com> | 2023-03-06 18:44:55 -0600 |
| commit | e80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch) | |
| tree | 79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/m1n1/trace | |
Diffstat (limited to 'tools/proxyclient/m1n1/trace')
| -rw-r--r-- | tools/proxyclient/m1n1/trace/__init__.py | 220 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/agx.py | 1148 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/asc.py | 271 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/dart.py | 69 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/dockchannel.py | 37 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/gpio.py | 67 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/i2c.py | 225 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/isp.py | 118 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/pcie.py | 108 | ||||
| -rw-r--r-- | tools/proxyclient/m1n1/trace/spi.py | 10 |
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"] |
