#!/usr/bin/env python3 # wall2_livecap3.py - capture the UVM GR channel-init push page via the host # (C46F) semaphore release group emitted by uvm_hal_turing_host_semaphore_release: # {0x20050017 (INC_METHOD count=5 addr=0x5c), sem_lo, sem_hi, payload, 0, exec} # The group sits at push_va + entry_len - 24 = push_va + 0x580 (entry0 len 1432), # with the header at +0x598 (entry1's 4-byte push). The push VA comes from the # service's own entry[0] decode in dmesg. import subprocess, struct, time, sys, re, os RAM = "/dev/shm/vgpu-guest-ram" PARAM = "/sys/module/vgsp/parameters/sema_map_add" LOG = "/tmp/opencode/wall2_livecap3.log" MASK = ~0xfff & ((1 << 64) - 1) WATCH_S = int(os.environ.get("W2_WATCH", "240")) PUSH_VA_DEFAULT = 0x4480000 logf = open(LOG, "a", buffering=1) def log(*a): s = " ".join(str(x) for x in a) print(s, flush=True) logf.write("[%s] %s\n" % (time.strftime("%H:%M:%S"), s)) img = open(RAM, "rb", buffering=0) def gpa2off(pa): if pa < 0x80000000: return pa if pa < 0x180000000: return 0x80000000 + (pa - 0x100000000) return None def off2gpa(off): return off if off < 0x80000000 else 0x100000000 + (off - 0x80000000) def rd(pa, n): off = gpa2off(pa) if off is None: return None img.seek(off); return img.read(n) def dmesg(): return subprocess.run(["sudo", "dmesg"], capture_output=True, text=True).stdout def push_va_now(): # newest entry[0] line for the Vulkan client's channel va = None for ln in dmesg().splitlines(): m = re.search(r"entry\[0\] h=0x([0-9a-f]+) eng=0 raw=\S+ va=0x([0-9a-f]+) len=(\d+)", ln) if m: va = int(m.group(2), 16) return va def scan(): img.seek(0, 2); fsz = img.tell() pat = struct.pack(" len(d): continue lo, hi, pay, w4, w5 = struct.unpack_from("<5I", d, i + 4) # data-word sanity: payload_hi==0, RELEASE op, sane payload if w4 != 0: continue if (w5 & 0x7) != 1: continue if not (0 < pay < (1 << 24)): continue sem = ((hi & 0xffffffff) << 32) | ((lo & 0x3fffffff) << 2) gpa = off2gpa(pos + i) page = gpa & MASK off = gpa - page out.append((page, off, sem, pay, w5)) pos += CH return out def main(): log("=== livecap3 start watch=%ds ===" % WATCH_S) injected = {} seen = set() deadline = time.time() + WATCH_S while time.time() < deadline: hits = scan() if hits: for (page, off, sem, pay, w5) in hits: key = (page, off) if key in seen: continue seen.add(key) d = rd(page, 0x1000) head = d[:0x20].hex() if d else "?" log("hit page=0x%x off=0x%x sem=0x%x pay=%u exec=0x%08x head=%s" % (page, off, sem, pay, w5, head)) log(" @0x580: %s" % (d[0x580:0x5b0].hex() if d else "?")) if off in (0x598, 0x580, 0x590): pv = push_va_now() or PUSH_VA_DEFAULT if pv not in injected: try: with open(PARAM, "w") as f: f.write("0x%x,0x%x,0x%x,1\n" % (pv, page, 0x1000)) log(" INJECT 0x%x -> 0x%x" % (pv, page)) injected[pv] = page except OSError as e: log(" inject FAIL", e) time.sleep(0.5) log("=== livecap3 done: %d injections ===" % len(injected)) return 0 if __name__ == "__main__": sys.exit(main())