summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-31 10:50:08 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-31 10:50:08 -0400
commitcc6c198a234cf3a42ae565328fe83d490faf1f02 (patch)
tree9b19ae21184bc35c5ee65c26551377d089203c39 /tools/guest-diag
parentc0a62cfcec2b09fff7bf0f86a129f7a04a27d70a (diff)
downloadopen-iov-cc6c198a234cf3a42ae565328fe83d490faf1f02.tar.gz
open-iov-cc6c198a234cf3a42ae565328fe83d490faf1f02.zip
vgsp: replay/block capacity 1024->2048 words (r1126 drop was arithmetic: window 390+prelude 901+sa 47 > 1024), two-page module pushbuffer (VA_PB2 0x102000 small-PT 0x102, CE SRC->0x109000, warm-sem probe page 0x10a000), gsp_phys_gr_pb_copy page-wise, static_asserts - planted verified (r1127 pb page2+probe planted before first fetch); >4KB path never exercised (5 boots beef never forwarded; walk PA 0x23921d000=8.89GB outside 8GB image); guest setup push NOT in guest RAM (ram_content_pin accepted 0/74); next: drop+re-walk src=4 all-zero reads, reject PA>guest-RAM top - SESSION 153 r1127
Diffstat (limited to 'tools/guest-diag')
-rwxr-xr-xtools/guest-diag/ram_content_pin.py160
1 files changed, 160 insertions, 0 deletions
diff --git a/tools/guest-diag/ram_content_pin.py b/tools/guest-diag/ram_content_pin.py
new file mode 100755
index 0000000..d521d91
--- /dev/null
+++ b/tools/guest-diag/ram_content_pin.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+"""Host-side CONTENT-based resolver for demanded GR setup-push windows.
+
+SESSION 153-CONTINUED-66. The client's GL setup pushes (and its 0x3200000 /
+0x6f00000 macro-upload windows) live in allocations that have NO kernel vmap
+and NO client CPU mapping on the failing boots: the guest scanner's area+idx
+identity sweep and its client-mem sweep both fail (`strict unknown-family
+client-mem failed`, `strict parse failed (N B, M windows tried)`), so the
+armed GR setup hold never lands its pin and the MME prelude stays 0.
+
+The bytes ARE in guest RAM - a RAM-wide pattern scan finds the MME/SA
+prelude runs (`0xa0100045` = INC 0x114 x16 args, `0x20020047` = INC 0x11c x2
+args). This tool therefore resolves a demanded window from CONTENT instead of
+identity:
+
+ 1. read the demand set the service publishes (debugfs sema_setup_demand:
+ "0xVA 0xOFF LEN" per line);
+ 2. locate every physical page in the guest RAM image that carries an
+ MME/SA prelude run signature;
+ 3. for each candidate physical page P and each window page index j, read
+ the EXACT window bytes the host would read - [P - j*0x1000 + OFF, +LEN)
+ - straight out of the RAM image and run the host's own method-stream
+ acceptance on them;
+ 4. inject the validated page run into the service through the
+ sema_map_add module parameter (0xVA,0xPA,LEN,1), so the host read
+ resolves the demanded window from real content.
+
+Nothing is injected unless the host's parser accepts the exact bytes, so a
+wrong candidate cannot mis-resolve a demand VA (the r1120 lesson).
+
+Usage: ram_content_pin.py [ram-image] [seconds]
+"""
+import importlib.util
+import mmap
+import os
+import re
+import subprocess
+import sys
+import time
+
+import numpy as np
+
+RAM = sys.argv[1] if len(sys.argv) > 1 else '/dev/shm/vgpu-guest-ram'
+SECS = float(sys.argv[2]) if len(sys.argv) > 2 else 120.0
+DEMAND = '/sys/kernel/debug/vgsp/sema_setup_demand'
+MAPADD = '/sys/module/vgsp/parameters/sema_map_add'
+GPS = ('/home/rmcore/Git/integdev_gpu_drv/open-iov/tools/guest-diag/'
+ 'guest_pin_stream.py')
+SIGS = (0xa0100045, 0x20020047) # INC 0x114 x16, INC 0x11c x2
+PAGE = 0x1000
+MAXHITS = 4096
+
+
+def log(msg):
+ print('[%s] %s' % (time.strftime('%H:%M:%S'), msg), flush=True)
+
+
+def load_accept():
+ spec = importlib.util.spec_from_file_location('gps', GPS)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
+ return mod._window_accept
+
+
+def read_demand():
+ out = []
+ try:
+ with open(DEMAND) as f:
+ for ln in f:
+ m = re.match(r'0x([0-9a-fA-F]+)\s+0x([0-9a-fA-F]+)\s+(\d+)',
+ ln.strip())
+ if not m:
+ continue
+ out.append((int(m.group(1), 16), int(m.group(2), 16),
+ int(m.group(3))))
+ except OSError:
+ pass
+ return out
+
+
+def inject(va_page, pa_page, ln):
+ cmd = "printf '0x%x,0x%x,0x%x,1' > %s" % (va_page, pa_page, ln, MAPADD)
+ try:
+ r = subprocess.run(['sudo', '-n', 'sh', '-c', cmd],
+ capture_output=True, text=True, timeout=10)
+ except subprocess.TimeoutExpired:
+ return False
+ return r.returncode == 0
+
+
+def main():
+ accept = load_accept()
+ f = open(RAM, 'rb')
+ mm = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
+ size = len(mm)
+ a32 = np.frombuffer(mm, dtype=np.uint32)
+ hits = []
+ for sig in SIGS:
+ idx = np.flatnonzero(a32 == sig)
+ log('sig 0x%08x: %d hit(s)' % (sig, idx.size))
+ for i in idx[:MAXHITS]:
+ hits.append(int(i) * 4)
+ del a32
+ hits = sorted(set(h >> 12 for h in hits))
+ log('candidate physical pages: %d (ram image %d MB)'
+ % (len(hits), size >> 20))
+ if not hits:
+ return 1
+ seen = set()
+ t0 = time.time()
+ while time.time() - t0 < SECS:
+ dem = read_demand()
+ for (va, off, ln) in dem:
+ key = (va & ~(PAGE - 1), ln)
+ if key in seen:
+ continue
+ if ln <= 0 or ln > 0x40000:
+ continue
+ need = (off + ln + PAGE - 1) // PAGE
+ if need > 64:
+ continue
+ va_page = va & ~(PAGE - 1)
+ found = None
+ for P in hits:
+ for j in range(need):
+ P0 = P - j * PAGE
+ if P0 < 0 or P0 + off + ln > size:
+ continue
+ buf = mm[P0 + off:P0 + off + ln]
+ try:
+ ok, mode = accept(buf, ln)
+ except Exception:
+ ok = False
+ if ok:
+ found = (P0, mode)
+ break
+ if found:
+ break
+ if not found:
+ log('demand va=0x%x off=0x%x len=%d: no content candidate'
+ % (va, off, ln))
+ seen.add(key)
+ continue
+ P0, mode = found
+ if inject(va_page, P0, need * PAGE):
+ log('demand va=0x%x off=0x%x len=%d -> pa=0x%x (%s parse, '
+ 'pages=%d) INJECTED'
+ % (va, off, ln, P0, mode, need))
+ seen.add(key)
+ else:
+ log('demand va=0x%x: injection refused by the module param'
+ % va)
+ seen.add(key)
+ time.sleep(1.0)
+ log('done')
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())