summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/wall2/wall2_livecap3.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/guest-diag/wall2/wall2_livecap3.py')
-rw-r--r--tools/guest-diag/wall2/wall2_livecap3.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/tools/guest-diag/wall2/wall2_livecap3.py b/tools/guest-diag/wall2/wall2_livecap3.py
new file mode 100644
index 0000000..db28bc2
--- /dev/null
+++ b/tools/guest-diag/wall2/wall2_livecap3.py
@@ -0,0 +1,106 @@
+#!/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("<I", 0x20050017)
+ out = []
+ CH = 64 << 20
+ pos = 0
+ while pos < fsz:
+ img.seek(pos); d = img.read(min(CH, fsz - pos))
+ st = 0
+ while True:
+ i = d.find(pat, st)
+ if i < 0: break
+ st = i + 1
+ if i + 24 > 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())