diff --git a/proxyclient/hv/trace_all.py b/proxyclient/hv/trace_all.py index 34946d16..908c4106 100644 --- a/proxyclient/hv/trace_all.py +++ b/proxyclient/hv/trace_all.py @@ -1,9 +1,7 @@ # SPDX-License-Identifier: MIT # Map the entire MMIO range as traceable -map_sw(0x2_00000000, - 0x2_00000000 | hv.SPTE_TRACE_READ | hv.SPTE_TRACE_WRITE, - 0x5_00000000) +trace_range(range(0x2_00000000, 0x7_00000000)) # Skip some noisy devices try: @@ -19,6 +17,3 @@ trace_device("/arm-io/error-handler", False) trace_device("/arm-io/aic", False) trace_device("/arm-io/spi1", False) trace_device("/arm-io/pmgr", False) - -# Re-map the vuart, which the first map_sw undid... -map_vuart() diff --git a/proxyclient/m1n1/hv.py b/proxyclient/m1n1/hv.py index 68f12780..d9e3da35 100644 --- a/proxyclient/m1n1/hv.py +++ b/proxyclient/m1n1/hv.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT import sys, traceback, struct, array, bisect, os, signal, runpy from construct import * -from enum import IntEnum, IntFlag +from enum import Enum, IntEnum, IntFlag from .asm import ARMAsm from .tgtypes import * @@ -11,6 +11,7 @@ from .sysreg import * from .macho import MachO from .adt import load_adt from . import xnutools, shell +from . import trace __all__ = ["HV"] @@ -48,6 +49,14 @@ VMProxyHookData = Struct( "data" / Array(2, Hex(Int64ul)), ) +class TraceMode(IntEnum): + OFF = 0 + ASYNC = 1 + UNBUF = 2 + SYNC = 3 + HOOK = 4 + RESERVED = 5 + class HV: PTE_VALID = 1 << 0 @@ -113,7 +122,8 @@ class HV: self._sigint_pending = False self.vm_hooks = [] self.interrupt_map = {} - self.mmio_handlers = AddrLookup() + self.mmio_maps = DictRangeMap() + self.dirty_maps = BoolRangeMap() self.shell_locals = { "hv": self, "iface": self.iface, @@ -179,6 +189,113 @@ class HV: self.interrupt_map.pop(n, None) assert self.p.hv_trace_irq(self.AIC_EVT_TYPE_HW, num, count, flags) > 0 + def add_tracer(self, zone, ident, mode=TraceMode.ASYNC, read=None, write=None, **kwargs): + assert mode in (TraceMode.RESERVED, TraceMode.OFF) or read or write + self.mmio_maps[zone, ident] = (mode, ident, read, write, kwargs) + self.dirty_maps.set(zone) + + def del_tracer(self, zone, ident): + del self.mmio_maps[zone, ident] + self.dirty_maps.set(zone) + + def clear_tracers(self, ident): + for r, v in self.mmio_maps.items(): + if ident in v: + v.pop(ident) + self.dirty_maps.set(r) + + def trace_device(self, path, trace=True): + node = self.adt[path] + for index in range(len(node.reg)): + addr, size = node.get_reg(index) + self.trace_range(irange(addr, size), trace) + + def trace_range(self, zone, trace=True): + if trace: + self.add_tracer(zone, "PrintTracer", TraceMode.ASYNC, self.print_tracer.event_mmio, self.print_tracer.event_mmio) + else: + self.del_tracer(zone, "PrintTracer") + + def pt_update(self): + if not self.dirty_maps: + return + + self.dirty_maps.compact() + self.mmio_maps.compact() + + top = 0 + + for zone in self.dirty_maps: + if zone.stop <= top: + continue + for mzone, maps in self.mmio_maps.overlaps(zone): + if mzone.stop <= top: + continue + top = mzone.stop + if not maps: + continue + maps = sorted(maps.values(), reverse=True) + mode, ident, read, write, kwargs = maps[0] + + need_read = any(m[2] for m in maps) + need_write = any(m[3] for m in maps) + + if mode == TraceMode.RESERVED: + print(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> RESERVED") + continue + elif mode == TraceMode.HOOK: + if need_read and not read: + read = self._mmio_read + if need_write and not write: + write = self._mmio_write + self.map_hook(mzone.start, mzone.stop - mzone.start, read, write, kwargs) + for m2, i2, r2, w2, k2 in maps[1:]: + if m2 == TraceMode.HOOK: + print(f"!! Conflict: HOOK {i2}") + elif mode == TraceMode.SYNC: + read = self._mmio_read if need_read else None + write = self._mmio_write if need_write else None + self.map_hook(mzone.start, mzone.stop - mzone.start, read, write) + elif mode in (TraceMode.UNBUF, TraceMode.ASYNC): + pa = mzone.start + if mode == TraceMode.UNBUF: + pa |= self.SPTE_TRACE_UNBUF + if need_read: + pa |= self.SPTE_TRACE_READ + if need_write: + pa |= self.SPTE_TRACE_WRITE + self.map_sw(mzone.start, pa, mzone.stop - mzone.start) + elif mode == TraceMode.OFF: + self.map_hw(mzone.start, mzone.start, mzone.stop - mzone.start) + print(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> HW") + continue + + rest = [m[1] for m in maps[1:] if m[0] != TraceMode.OFF] + if rest: + rest = " (+ " + ", ".join(rest) + ")" + else: + rest = "" + + print(f"PT[{mzone.start:09x}:{mzone.stop:09x}] -> {mode.name}.{'R' if read else ''}{'W' if read else ''} {ident}{rest}") + + self.u.inst(0xd50c879f) # tlbi alle1 + self.dirty_maps.clear() + + def handle_mmiotrace(self, data): + evt = EvtMMIOTrace.parse(data) + + maps = sorted(self.mmio_maps[evt.addr].values(), reverse=True) + for mode, ident, read, write, kwargs in maps: + if mode > TraceMode.UNBUF: + print(f"ERROR: mmiotrace event but expected {mode.name} mapping") + continue + if mode == TraceMode.OFF: + continue + if evt.flags.WRITE: + write(evt, **kwargs) + else: + read(evt, **kwargs) + def addr(self, addr): unslid_addr = addr + self.sym_offset if addr < self.tba.virt_base or unslid_addr < self.macho.vmin: @@ -204,19 +321,6 @@ class HV: return self.symbols[idx] - def handle_mmiotrace(self, data): - evt = EvtMMIOTrace.parse(data) - - obj, zone = self.mmio_handlers.lookup(evt.addr, None) - if obj is not None: - obj.handle_mmiotrace(evt, zone) - return - - dev, zone = self.device_addr_tbl.lookup(evt.addr) - t = "W" if evt.flags.WRITE else "R" - m = "+" if evt.flags.MULTI else " " - print(f"[0x{evt.pc:016x}] MMIO: {t}.{1<