summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/guest-diag/wall2/README-session77.md132
-rw-r--r--tools/guest-diag/wall2/a8clear.py52
-rw-r--r--tools/guest-diag/wall2/fasttick.py44
-rw-r--r--tools/guest-diag/wall2/fence_assist.py112
-rw-r--r--tools/guest-diag/wall2/find_push.py59
-rw-r--r--tools/guest-diag/wall2/kc_dump_fn.py21
-rw-r--r--tools/guest-diag/wall2/pdbtick.py69
-rw-r--r--tools/guest-diag/wall2/runbook.py58
-rw-r--r--tools/guest-diag/wall2/t112c-gdbstub.py42
-rw-r--r--tools/guest-diag/wall2/t120.py148
-rw-r--r--tools/guest-diag/wall2/t121.py151
-rw-r--r--tools/guest-diag/wall2/t122.py151
-rw-r--r--tools/vk/vk_frame.c29
13 files changed, 1066 insertions, 2 deletions
diff --git a/tools/guest-diag/wall2/README-session77.md b/tools/guest-diag/wall2/README-session77.md
index 1ae9a62..d830bef 100644
--- a/tools/guest-diag/wall2/README-session77.md
+++ b/tools/guest-diag/wall2/README-session77.md
@@ -1061,3 +1061,135 @@ service-side durable route: have the SERVICE write the marker into the
channel-buffer page once it can resolve it (the memdesc VA = the CE
channel's buffer; the service's channel registration fields + the PT walk
give the page candidates).
+
+**39. Sessions 112b-114: THE SCRUB GATE FELL, THE COMPR WALL WAS DECODED AND FLIPPED, AND THE FIRST VULKAN SUBMISSION LANDED (VKFENCES IS THE NEW WALL).**
+Context first (sessions 112b/112c, commits `97489187`/`cb7d8ce7`/`752e2b30`/
+`d6ccdaf3`/`8700f65a`): the CeUtils scrubber's finish payload is authored
+in-service (bound to the EXACT 4096-entry channel), the client then leaves
+`scrubCheckAndWaitForSize` permanently and runs
+vkCreateImage -> vkGetImageMemoryRequirements -> vkAllocateMemory; the
+remaining `-2` was decomposed to the guest-internal VidHeap `#149`-shape
+call (attr COMPR_ANY + 2MB page + owner 0x474C0000) returning 0x51
+NV_ERR_NO_MEMORY; session 113 read that as a 64KB comptag/CBM alloc. This
+session REFINED it: the COMPR+2MB class is not always 64KB - the r705
+runs show the *image data* alloc itself (size 0x140000 = 1.25 MB for
+640x480x4 + pad, attr 0x1002000, flags 0xc10d) in exactly the same shape.
+The "separate 64KB CBM" reading was too narrow; every alloc of the class
+{attr&0xF000 == 0x2000, 2MB page, owner 0x474C0000} fails 0x51.
+
+**A. THE ZERO-TOUCH CREATE RECIPE (NEW, SCRIPTED).** Two live assists are
+required per client, both proven again this session: (1) the
+instance-phase marker clears at eglcore+0xB33D30 (t106/t99 `BC`, 5
+clears); (2) THE DEVICE-PHASE WAIT-WORD CHASER at eglcore+0xDCEBD9
+(t99 `BW`: word = *(holder+0x58)+*(u32)(obj2+0x60) with
+obj2 = *(*(rbx+0x70)+0x58); write 1 when 0). Without (2) the client
+reaches `VKDEVICE`'s poll(`/dev/nvidia0`, 10 ms) retry loop and sits
+there INDEFINITELY (session-76 state; the a097 engine-monitor worker
+keeps polling 0x2080a097 every 1.25 s regardless - its reply words are
+still a red herring for the main-thread gate). With (1)+(2) the run is
+`VKINSTANCE ok / VKDEVS 2 / VKDEVICE ok family=0` then straight to the
+image path. `t99.py`-style attach works too: attaching `BW` to an
+already-stuck client writes the word and the client immediately
+proceeds (proven on a stuck pid 15797: WORDWROTE x2 -> VKDEVICE ok ->
+VKFAIL -2 at the alloc).
+NEW TOOLS: `t120.py` (BC+BW+single-shot COMPR->non-COMPR flip),
+`t121.py` (BC+BW+full VidHeap trace from #120), `t122.py` (BC+BW+flip
+for ANY COMPR+2MB internal alloc, size >= 0x10000), `a8clear.py` (the
+no-ptrace count-gate clearer for base+0x2070b7c, reconstructed).
+
+**B. THE FLIP (THE DECISIVE EXPERIMENT).** With `t122.py`: at the
+vidheap ioctl whose params match (attr&0xF000)==0x2000, owner
+0x474C0000, size>=0x10000, the script clears attr bits 13:12
+(COMPR_ANY -> NONE) in the client's own params before the kernel sees
+them. Result (r705c, reproducible twice on the same boot):
+`HIT149 size=0x140000 attr=0x1002000 -> 0x1000000` (image data alloc
+SUCCEEDS), a second `HIT149 size=0x140000 attr=0x1022004 -> 0x1020004`
+(buffer-class COMPR alloc) also succeeds, and the client runs the WHOLE
+pipeline for the first time in project history:
+vkCreateImageView -> buffer -> shader module -> descriptor set
+layout/pool -> pipeline layout -> compute pipeline -> command pool ->
+command buffer -> fence -> vkResetFences -> **vkQueueSubmit OK** ->
+`VKFAIL vkWaitForFences(dev, 1, &fence, VK_TRUE, 30000000000ull) -> 2
+(line 459)` - THE FIRST VULKAN SUBMISSION, then the FIRST FENCE TIMEOUT
+(30 s, the client's own bound; the run exits rc=1 cleanly).
+
+**C. THE NEW WALL = THE FIRST FENCE RELEASE.** Host-side evidence in the
+submit window: the client's GR channel registers
+(`sema-chan: h=0xbeec0100 eng=0 gpfifo=0x4cc0000 ent=32768
+userd=0xfee00 put=11 get=2 ring rc=0 pa=0x1217e3000 ap=1`) - the guest
+driver published 11 GPFIFO entries, the emulation's reader consumed 2;
+the service still reports the channel-init pushes unread
+(`sema-entry: unread h=0xbeec0100 idx=2 va=0x4ca059c len=976 want=0
+exp=0 unread=1`) and `gr_wedge` grew 3 -> 9 over the runs. So the fence's
+release is never authored after the submit - this is the session-76/93
+"wall #2" class (push-page readability/completion), now the ONLY gate
+between the flipped client and the first `VKFRAME seq/DONE` + PPM.
+
+**D. DURABLE-FIX LEADS (for the COMPR class, no guest patching).**
+(1) memmgrAllocHal_GM107 (570.144) downgrades COMPR_ANY -> NONE when
+`pMemorySystemConfig->bDisableCompbitBacking` is set - the guest's
+MEMORY_SYSTEM_STATIC_CONFIG comes from the GSP (the MEMSYS_GET_STATIC_
+CONFIG control 0x20800a1c / the GSP static info path); answering it (or
+the same knob wherever the guest's config actually comes from) is the
+host-side durable equivalent of the flip. 0x20800a1c was NEVER observed
+in the RM_CONTROL log, so first find where the guest's config is fed
+(control cache? GSP init args?) before coding the handler.
+(2) Alternatively locate glcore's COMPR decision (attr construction at
+eglcore+0x2c83fb/0x2c8465 and +0x50e038/0x50e894/0x50e8bd per session
+113) and answer the capability that drives it. (3) The session-85
+device-local -2 story (2g mdev type) remains a prerequisite context.
+
+NEXT (ranked): (1) author the fence release for the first submit (the
+push-page-5 content-match / pinning machinery; or have the service
+complete the fence when it consumes the submit's GPFIFO entries); (2)
+then the first VKFRAME seq/DONE + PPM; (3) fold the COMPR flip into a
+host-side answer (see D); (4) the mission series (Xorg/GZDOOM, KVMFR,
+CUDA 15/15 + soak, test series, video, Windows).
+
+**40. Session 115: THE FENCE WALL DECOMPOSED - THE CLIENT VAS PAGE DIRECTORY IS UNDISCOVERABLE AND THE PUSHES ARE NONCONTIGUOUS; RING PAGES ARE CONTENT-LOCATABLE AND INJECTABLE, BUT THE RING PAIR ALONE DOES NOT UNLOCK THE PUSH DECODE.**
+(1) The fence timeout is upstream of everything else: the sema service cannot
+decode the submit's GR pushes (`sema-entry: unread ... va=0x4ca0000
+len=1432` -> `skipping stale entry ... after 185-194 failed rescues`;
+`work_gr submitted=0` - no GR forward ever starts). (2) The pump's push
+reads resolve ONLY through the client VAS page directory (or pins). The
+service has NO pdb for this client: `walk_probe` of the ring and push VAs
+prints `discovered pdb=0x0 found=0` and no registered VASes; the r496f
+vidmem pdb discovery never ran (it is only called from the RAM ring-scan
+latch), and the r496j COPY_SERVER_PDES candidate (0x109b9c000) is garbage
+(its content is not a page table). (3) THE RAM RING SCAN MISSES BY
+CONSTRUCTION for this client: it sweeps [0, 6 GB) in 12 slices and gives
+up (`found no candidate in 12 slice(s)`), but the sweep runs BEFORE the
+client's pushes exist and never re-runs - while the ring page content is
+provably in RAM (content-located below). (4) CONTENT LOCATION WORKS: the
+GR ring page (ring VA 0x4cc0000, first entries {va=0x4ca0000 len=1432,
+va=0x4ca0598 len=4}) was found in the guest RAM file by its first two GPFIFO
+entries (at GPA 0x12C600000 then 0x12898c000 on later runs - per-run
+allocations), and `sema_map_add` injection of the TRUE ring pair is
+accepted (`INJECT va=0x4d20000 pa=0x13619d000 : ok`). BUT the pushes do not
+become readable from the ring pair alone: the pushbuffer is NONCONTIGUOUS
+(the window guess `ring_pa - 0x20000` reads zeros for both the GR and the
+CE channels), the pushes are not CPU-mapped in the client process (the
+ring GPA is absent from /proc/PID/pagemap of every rw-s nvidia mapping),
+and a bottom-up search for the level-3 table (a 4 KB page whose index-2
+big/small entry maps into the ring's 2 MB window) found ZERO candidates in
+RAM - the page tables are almost certainly in VIDMEM (r496f's note) or use
+a shape the walker does not model. (5) The service's OWN successful read of
+one CE init push (`sema-push: h=0xbeec0104 va=0x4d00000 ... rel=0x4b19b10
+pay=1 have_rel=1`, `ops_runs=0`) did NOT apply its release either -
+whatever resolved that one read is not reproducible for the GR pushes.
+NEW TOOLS: `fence_assist.py` (host: watches a `dmesg -w` capture for the
+pump's entry decodes, content-locates the ring pages by their first two
+entries in the RAM file, injects the pairs via sema_map_add - proven to
+find and inject, but the ring pair alone is insufficient) and
+`find_push.py` (guest: tests whether the client CPU-maps the ring GPA).
+NEXT (ranked, concrete): (1) find the client VAS page directory: extract the
+pdb from the GUEST kernel's RM objects (kcore walk of the VAS/GPU object
+for this channel - the GPFIFO ring page content anchors the search), then
+register it via the service's VAS table (a new sysfs/debugfs entry point
+or the existing candidate path) - this is the "PDB injection" the mission
+names; (2) or scan VIDMEM for the pdb with the ring pair as the verifier
+(extend `svc_xvas_pdb_discover`'s trigger: call it from the ring-injection
+path, not only from the RAM scan latch); (3) make the RAM ring scan re-arm
+per channel with the pushes present (its 12-slice give-up is the reason
+the auto-discovery never latches on this client); (4) then the submit
+decodes, the release lands and `vkWaitForFences` returns -> VKFRAME.
diff --git a/tools/guest-diag/wall2/a8clear.py b/tools/guest-diag/wall2/a8clear.py
new file mode 100644
index 0000000..261312a
--- /dev/null
+++ b/tools/guest-diag/wall2/a8clear.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# a8clear.py - no-ptrace A8/NvOgl count clearer: finds each live vk_frame's
+# libnvidia-eglcore base (the off==0 mapping) and writes 0 to the u32 at
+# base+0x2070b7c whenever it is nonzero (the count gate t91 cleared via gdb).
+import os, subprocess, sys, time
+DUR = float(sys.argv[1]) if len(sys.argv) > 1 else 600
+def pids():
+ try:
+ out = subprocess.run(["pgrep", "-x", "vk_frame"], capture_output=True).stdout
+ return [int(x) for x in out.split()]
+ except Exception:
+ return []
+log = set()
+end = time.time() + DUR
+while time.time() < end:
+ for pid in pids():
+ base = None
+ try:
+ for line in open("/proc/%d/maps" % pid):
+ p = line.split()
+ if len(p) < 6 or "libnvidia-eglcore" not in p[-1]:
+ continue
+ try:
+ off = int(p[2], 16)
+ except Exception:
+ continue
+ if off == 0:
+ base = int(p[0].split("-")[0], 16)
+ break
+ except Exception:
+ continue
+ if not base:
+ continue
+ w = base + 0x2070b7c
+ try:
+ fd = os.open("/proc/%d/mem" % pid, os.O_RDWR)
+ os.lseek(fd, w, 0)
+ v = os.read(fd, 4)
+ if len(v) == 4:
+ n = int.from_bytes(v, "little")
+ if n:
+ os.lseek(fd, w, 0)
+ os.write(fd, b"\x00\x00\x00\x00")
+ k = (pid, n)
+ if len(log) < 40:
+ log.add(k)
+ print("A8CLEAR pid=%d count=%u -> 0" % (pid, n), flush=True)
+ os.close(fd)
+ except Exception:
+ pass
+ time.sleep(0.5)
+print("A8CLEAR-DONE", flush=True)
diff --git a/tools/guest-diag/wall2/fasttick.py b/tools/guest-diag/wall2/fasttick.py
new file mode 100644
index 0000000..413d3ba
--- /dev/null
+++ b/tools/guest-diag/wall2/fasttick.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# fasttick.py - watch the service log for the GR0 channel's ring resolution
+# (sema-chan: h=0xbeec0100 ... ring rc=0 pa=0x...) and immediately hand the
+# verified {ring VA -> ring PA} pair to the service (sema_pdb_discover), so
+# the parked VAS pdb candidate can be verified before/at age-out.
+import re, subprocess, sys, time
+
+PARAM = "/sys/module/vgsp/parameters/sema_pdb_discover"
+RING_VA = 0x4cc0000
+
+def main():
+ dur = float(sys.argv[1]) if len(sys.argv) > 1 else 120
+ end = time.time() + dur
+ fired = set()
+ seen_pdbs = set()
+ print("# fasttick start (%.0fs)" % dur, flush=True)
+ while time.time() < end:
+ out = subprocess.run(["sudo", "dmesg"], capture_output=True).stdout.decode(errors="ignore")
+ tail = out.splitlines()[-120:]
+ for ln in tail:
+ m = re.search(r"sema-chan: h=0xbeec0100 eng=\d+ cid=\d+ gpfifo=0x([0-9a-f]+) ent=\d+ userd=0x[0-9a-f]+ as=\d+ put=\d+ get=\d+ ring rc=0 pa=0x([0-9a-f]+)", ln)
+ if m:
+ ring_va = int(m.group(1), 16)
+ ring_pa = int(m.group(2), 16)
+ key = (ring_va, ring_pa)
+ if key not in fired:
+ fired.add(key)
+ cmd = "echo 0x%x,0x%x > %s" % (ring_va, ring_pa, PARAM)
+ r = subprocess.run(["sudo", "sh", "-c", cmd], capture_output=True)
+ print("[%s] TRIGGER va=0x%x pa=0x%x rc=%d" % (
+ time.strftime("%H:%M:%S"), ring_va, ring_pa, r.returncode), flush=True)
+ m2 = re.search(r"page directory (?:registered UNVERIFIED after \d+ ms parked \()?pdb=0x([0-9a-f]+) entries=(\d+)", ln)
+ if not m2:
+ m2 = re.search(r"VA space 0x[0-9a-f]+ page directory 0x([0-9a-f]+) entries=(\d+)", ln)
+ if m2:
+ key = int(m2.group(1), 16)
+ if key not in seen_pdbs:
+ seen_pdbs.add(key)
+ print("[%s] PDB candidate 0x%x entries=%s" % (
+ time.strftime("%H:%M:%S"), key, m2.group(2)), flush=True)
+ time.sleep(0.3)
+ print("# fasttick end", flush=True)
+
+main()
diff --git a/tools/guest-diag/wall2/fence_assist.py b/tools/guest-diag/wall2/fence_assist.py
new file mode 100644
index 0000000..4ea0b0c
--- /dev/null
+++ b/tools/guest-diag/wall2/fence_assist.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+# fence_assist.py - content-locate the client's ring pages in guest RAM from the
+# service's own GPFIFO entry decodes (dmesg) and inject the TRUE VA->PA pairs
+# via sema_map_add, so the pump's push reads (and its pushbuffer pairing) have
+# a correct anchor.
+import mmap, re, struct, subprocess, sys, threading, time
+
+F = "/dev/shm/vgpu-guest-ram"
+PARAM = "/sys/module/vgsp/parameters/sema_map_add"
+
+def gpa_of(fo):
+ return fo if fo < 0x80000000 else fo - 0x80000000 + 0x100000000
+
+def inject(va, pa, ln=0x1000, flag=1):
+ cmd = "echo 0x%x,0x%x,0x%x,%d > %s" % (va & ~0xfff, pa & ~0xfff, ln, flag, PARAM)
+ r = subprocess.run(["sudo", "sh", "-c", cmd], capture_output=True)
+ print("INJECT va=0x%x pa=0x%x : %s" % (va & ~0xfff, pa & ~0xfff,
+ "ok" if r.returncode == 0 else "FAIL"), flush=True)
+
+def search(mm, pats, results):
+ CH = 8 << 20
+ size = len(mm)
+ off = 0
+ while off < size:
+ d = mm[off:off+CH]
+ for (name, pat) in pats.items():
+ if name in results:
+ continue
+ i = d.find(pat)
+ if i >= 0:
+ results[name] = gpa_of(off + i)
+ off += CH - 32
+
+def main():
+ dur = float(sys.argv[1]) if len(sys.argv) > 1 else 120
+ f = open(F, "rb")
+ mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
+ end = time.time() + dur
+ pats = {} # name -> pattern bytes
+ ring_va = {} # name -> ring VA
+ seen = set() # raw entry lines processed
+ done = set() # injected names
+ print("# fence_assist start (%.0fs)" % dur, flush=True)
+ while time.time() < end:
+ try:
+ with open("/tmp/opencode/dmsg_w.txt", "r", errors="ignore") as df:
+ tail = df.read().splitlines()[-6000:]
+ out = "\n".join(tail)
+ except OSError:
+ out = ""
+ tail = []
+ tail = [ln for ln in tail if "sema-chan:" in ln or "sema-push" in ln]
+ cur = {}
+ for ln in tail:
+ m = re.search(r"sema-chan: entry\[(\d+)\] h=0x([0-9a-f]+) eng=\d+ raw=([0-9a-f]+) va=0x([0-9a-f]+)", ln)
+ if m:
+ idx, h, raw, va = int(m.group(1)), m.group(2), int(m.group(3), 16), int(m.group(4), 16)
+ cur.setdefault(h, {})[idx] = raw
+ if idx == 0:
+ ring_va[h] = va # entry[0] va is the push VA (ring_va - 0x20000)
+ for h, ents in cur.items():
+ key = (h, tuple(sorted(ents.items())))
+ if key in seen or len(ents) < 2:
+ continue
+ seen.add(key)
+ name = h
+ pat = b"".join(struct.pack("<Q", ents[i]) for i in sorted(ents)[:2])
+ pats[name] = pat
+ print("# channel h=0x%s entries=%s pattern ready" % (h, sorted(ents)), flush=True)
+ if pats:
+ results = {}
+ threads = []
+ # split the map among 4 threads
+ LO = 0x100000000; HI = 0x180000000
+ size = len(mm)
+ step = size // 4
+ class Sl(shim := object):
+ def __init__(self, a, b):
+ self.a, self.b = a, b
+ def worker(a, b):
+ CH = 8 << 20
+ off = a
+ while off < b:
+ if off < 0x80000000 and b <= 0x80000000: pass
+ d = mm[off:off+CH]
+ for (nm, p) in list(pats.items()):
+ if nm in results: continue
+ i = d.find(p)
+ if i >= 0:
+ results[nm] = gpa_of(off + i)
+ off += CH - 32
+ for k in range(4):
+ a = 0x80000000 + k * (0x80000000 // 4)
+ b = 0x80000000 + (k + 1) * (0x80000000 // 4)
+ t = threading.Thread(target=worker, args=(a, b))
+ t.start(); threads.append(t)
+ for t in threads: t.join()
+ for nm, gpa in results.items():
+ if nm in done: continue
+ done.add(nm)
+ # ring page content starts with entry[0]; the ring VA is the
+ # channel's gpfifo VA = we reconstruct: entry0 va is the push,
+ # ring = first "gpfifo=0x..." dmesg line for this h
+ m = re.search(r"sema-chan: h=0x%s eng=\d+ cid=\d+ gpfifo=0x([0-9a-f]+)" % nm, out)
+ rva = int(m.group(1), 16) if m else None
+ if rva:
+ print("RING FOUND h=0x%s gpfifo_va=0x%x -> gpa=0x%x" % (nm, rva, gpa), flush=True)
+ inject(rva, gpa, 0x1000)
+ time.sleep(0.6)
+ print("# fence_assist done, injected %d" % len(done), flush=True)
+
+main()
diff --git a/tools/guest-diag/wall2/find_push.py b/tools/guest-diag/wall2/find_push.py
new file mode 100644
index 0000000..4bf8ccc
--- /dev/null
+++ b/tools/guest-diag/wall2/find_push.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# find_push.py - locate the client's CPU mapping that owns the GR ring page
+# (GPA 0x12C600000 as content-located on the host) and print the CPU VA<->
+# GPU VA correspondence for the low GPU VAs (0x4ca0000 etc).
+import os, struct, sys, subprocess, mmap, time
+
+RING_GPA = 0x12C600000
+PUSH_GVA = 0x4ca0000
+RING_GVA = 0x4cc0000
+
+def pids():
+ out = subprocess.run(["pgrep", "-x", "vk_frame"], capture_output=True).stdout
+ return [int(x) for x in out.split()]
+
+def read_pagemap(pid, va):
+ try:
+ with open("/proc/%d/pagemap" % pid, "rb") as f:
+ f.seek((va // 4096) * 8)
+ d = f.read(8)
+ if len(d) < 8:
+ return None
+ e = int.from_bytes(d, "little")
+ if not (e >> 63) & 1:
+ return None
+ return (e & ((1 << 55) - 1)) << 12
+ except OSError:
+ return None
+
+def main():
+ for pid in pids():
+ print("pid=%d" % pid)
+ try:
+ maps = open("/proc/%d/maps" % pid).read().splitlines()
+ except OSError:
+ continue
+ for line in maps:
+ p = line.split()
+ if len(p) < 6 or "nvidia" not in p[-1]:
+ continue
+ lo, hi = [int(x, 16) for x in p[0].split("-")]
+ if hi - lo > 0x1000000:
+ continue
+ # scan pages; look for RING_GPA
+ for va in range(lo, hi, 0x10000):
+ for off in range(0, 0x10000, 0x1000):
+ pa = read_pagemap(pid, va + off)
+ if pa == RING_GPA:
+ print("FOUND ring page: cpu_va=0x%x (map 0x%x-0x%x %s)" % (va+off, lo, hi, p[-1]))
+ delta_cpu = (va + off) - RING_GVA
+ push_cpu = PUSH_GVA + delta_cpu
+ print(" delta_cpu = 0x%x" % delta_cpu)
+ for gva in (PUSH_GVA, PUSH_GVA+0x1000, 0x4ca059c, 0x4ca0980, 0x4ca5870, 0x4903000):
+ cva = gva + delta_cpu
+ pa2 = read_pagemap(pid, cva & ~0xfff)
+ print(" gva=0x%x -> cpu=0x%x -> pa=%s" % (gva, cva, hex(pa2) if pa2 else None))
+ return
+ print(" ring GPA 0x%x not found in this pid's mappings" % RING_GPA)
+
+main()
diff --git a/tools/guest-diag/wall2/kc_dump_fn.py b/tools/guest-diag/wall2/kc_dump_fn.py
new file mode 100644
index 0000000..e481d24
--- /dev/null
+++ b/tools/guest-diag/wall2/kc_dump_fn.py
@@ -0,0 +1,21 @@
+import struct, sys
+VA = int(sys.argv[1], 16)
+N = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x800
+f = open("/proc/kcore","rb")
+hdr = f.read(64)
+e_phoff = struct.unpack_from("<Q", hdr, 0x20)[0]
+e_phentsize = struct.unpack_from("<H", hdr, 0x36)[0]
+e_phnum = struct.unpack_from("<H", hdr, 0x38)[0]
+segs = []
+for i in range(e_phnum):
+ f.seek(e_phoff + i*e_phentsize)
+ ph = f.read(e_phentsize)
+ p_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz = struct.unpack_from("<IIQQQQQ", ph, 0)
+ if p_type == 1:
+ segs.append((p_vaddr, p_memsz, p_offset))
+for v, sz, off in segs:
+ if v <= VA < v+sz:
+ f.seek(off + (VA - v))
+ d = f.read(min(N, v+sz-VA))
+ sys.stdout.write(d.hex())
+ break
diff --git a/tools/guest-diag/wall2/pdbtick.py b/tools/guest-diag/wall2/pdbtick.py
new file mode 100644
index 0000000..d65b79d
--- /dev/null
+++ b/tools/guest-diag/wall2/pdbtick.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# pdbtick.py - content-locate the GR0 ring page (deterministic first three
+# GPFIFO entries) in the guest RAM file, then hand the verified
+# {ring VA -> ring PA} pair to the service (sema_pdb_discover), which
+# verifies the parked COPY_SERVER_PDES candidates and falls back to the
+# vidmem walk-root sweep. Runs while the client is alive.
+import mmap, struct, subprocess, sys, threading, time
+
+F = "/dev/shm/vgpu-guest-ram"
+PARAM = "/sys/module/vgsp/parameters/sema_pdb_discover"
+PAT = struct.pack("<QQQ", 0x0005980004ca0000, 0x8000040004ca0598,
+ 0x0003d00004ca059c)
+RING_VA = 0x4cc0000
+
+def gpa_of(x):
+ return x if x < 0x80000000 else x - 0x80000000 + 0x100000000
+
+def main():
+ fobj = open(F, "rb")
+ mm = mmap.mmap(fobj.fileno(), 0, access=mmap.ACCESS_READ)
+ size = len(mm)
+ nthr = 8
+ step = size // nthr
+ res = []
+ lock = threading.Lock()
+ def worker(a, b):
+ CH = 8 << 20
+ off = a
+ while off < b:
+ d = mm[off:off + CH]
+ i = d.find(PAT)
+ while i >= 0:
+ with lock:
+ res.append(off + i)
+ i = d.find(PAT, i + 1)
+ off += CH - 64
+ ts = []
+ for k in range(nthr):
+ a = k * step
+ b = (k + 1) * step if k < nthr - 1 else size
+ t = threading.Thread(target=worker, args=(a, b)); t.start(); ts.append(t)
+ for t in ts: t.join()
+ if not res:
+ print("RING NOT FOUND")
+ return None
+ hits = [gpa_of(x) for x in res]
+ print("ring candidates:", [hex(h) for h in hits], flush=True)
+ for gpa in hits:
+ cmd = "echo 0x%x,0x%x > %s" % (RING_VA, gpa, PARAM)
+ r = subprocess.run(["sudo", "sh", "-c", cmd], capture_output=True)
+ print("sema_pdb_discover 0x%x,0x%x rc=%d" % (RING_VA, gpa, r.returncode), flush=True)
+ return hits
+
+
+def loop():
+ import time as _t
+ dur = float(sys.argv[1]) if len(sys.argv) > 1 else 120
+ end = _t.time() + dur
+ fired = set()
+ while _t.time() < end:
+ hits = main()
+ if hits:
+ new = [h for h in hits if h not in fired]
+ if new:
+ fired.update(new)
+ _t.sleep(1.0)
+ print("# pdbtick end", flush=True)
+
+loop()
diff --git a/tools/guest-diag/wall2/runbook.py b/tools/guest-diag/wall2/runbook.py
new file mode 100644
index 0000000..256d255
--- /dev/null
+++ b/tools/guest-diag/wall2/runbook.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# runbook.py - watch for the GR0 channel ring registration, hand the pair to
+# sema_pdb_discover, then (after the pdb anchor) issue bounded
+# sema_chan_rewind calls so entries skipped before the anchor re-decode.
+import re, subprocess, sys, time
+
+PDB = "/sys/module/vgsp/parameters/sema_pdb_discover"
+REW = "/sys/module/vgsp/parameters/sema_chan_rewind"
+
+def dmesg_tail(n=150):
+ out = subprocess.run(["sudo", "dmesg"], capture_output=True).stdout.decode(errors="ignore")
+ return out.splitlines()[-n:]
+
+def main():
+ dur = float(sys.argv[1]) if len(sys.argv) > 1 else 180
+ end = time.time() + dur
+ fired = set()
+ rewinds = {}
+ print("# runbook start (%.0fs)" % dur, flush=True)
+ while time.time() < end:
+ for ln in dmesg_tail():
+ m = re.search(r"sema-chan: h=0x([0-9a-f]+) eng=\d+ cid=\d+ gpfifo=0x([0-9a-f]+) ent=\d+ userd=0x[0-9a-f]+ as=\d+ put=\d+ get=\d+ ring rc=0 pa=0x([0-9a-f]+)", ln)
+ if not m:
+ continue
+ h = int(m.group(1), 16)
+ gv = int(m.group(2), 16)
+ rp = int(m.group(3), 16)
+ key = (h, gv, rp)
+ if key in fired:
+ # after the anchor has had time to land, rewind this channel
+ if h == 0xbeec0100:
+ t0 = rewinds.setdefault(key, time.time())
+ n = int((time.time() - t0) // 8)
+ done = rewinds.get(("done", key), 0)
+ if n > done:
+ subprocess.run(["sudo", "sh", "-c",
+ "echo all > %s" % REW], capture_output=True)
+ rewinds[("done", key)] = n
+ print("[%s] REWIND all (h=0x%x, n=%d)" % (
+ time.strftime("%H:%M:%S"), h, n), flush=True)
+ continue
+ fired.add(key)
+ r = subprocess.run(["sudo", "sh", "-c",
+ "echo 0x%x,0x%x > %s" % (gv, rp, PDB)],
+ capture_output=True)
+ print("[%s] TRIGGER h=0x%x va=0x%x pa=0x%x rc=%d" % (
+ time.strftime("%H:%M:%S"), h, gv, rp, r.returncode), flush=True)
+ # report decodes/releases
+ out = subprocess.run(["sudo", "dmesg"], capture_output=True).stdout.decode(errors="ignore")
+ for ln in out.splitlines()[-8:]:
+ if ("sema-push: h=0xbeec0100" in ln and "have_rel=1" in ln) or \
+ "registered after verified" in ln or "rewind" in ln or \
+ ("sema-push: h=0xbeec0100" in ln and "len=1432" in ln):
+ print("LOG %s" % ln.strip()[-170:], flush=True)
+ time.sleep(0.4)
+ print("# runbook end", flush=True)
+
+main()
diff --git a/tools/guest-diag/wall2/t112c-gdbstub.py b/tools/guest-diag/wall2/t112c-gdbstub.py
new file mode 100644
index 0000000..27d0b9d
--- /dev/null
+++ b/tools/guest-diag/wall2/t112c-gdbstub.py
@@ -0,0 +1,42 @@
+import gdb
+
+gr = gdb.execute
+
+def u32(a):
+ return int(gdb.parse_and_eval("*(unsigned int*)0x%x" % a))
+def u64(a):
+ return int(gdb.parse_and_eval("*(unsigned long*)0x%x" % a))
+
+state = {"hits": {}}
+
+class HB(gdb.Breakpoint):
+ def __init__(self, pa, tag, quiet_after=8):
+ super().__init__("*0x%x" % pa, type=gdb.BP_BREAKPOINT, internal=False)
+ self.tag = tag
+ self.quiet_after = quiet_after
+ self.silent = True
+ def stop(self):
+ n = state["hits"].get(self.tag, 0) + 1
+ state["hits"][self.tag] = n
+ try:
+ rdi = int(gdb.parse_and_eval("$rdi"))
+ rsi = int(gdb.parse_and_eval("$rsi"))
+ rdx = int(gdb.parse_and_eval("$rdx"))
+ rcx = int(gdb.parse_and_eval("$rcx"))
+ r8 = int(gdb.parse_and_eval("$r8"))
+ r9 = int(gdb.parse_and_eval("$r9"))
+ if n <= self.quiet_after or self.tag in ("PRED", "COMPR"):
+ print("HIT %s #%d rdi=0x%x rsi=0x%x rdx=0x%x rcx=0x%x r8=0x%x r9=0x%x" %
+ (self.tag, n, rdi, rsi, rdx, rcx, r8, r9), flush=True)
+ except Exception as e:
+ print("err", e, flush=True)
+ return False
+
+HB(0xffffffffc0c403b0, "VIDH")
+HB(0xffffffffc0d667a0, "PMA", 4)
+HB(0xffffffffc0d6ae30, "PRED")
+HB(0xffffffffc0d57d60, "COMPR", 16)
+HB(0xffffffffc0d607f0, "SCRUB", 4)
+print("HB-SET", flush=True)
+gr("continue")
+print("T112B-DONE %s" % state["hits"], flush=True)
diff --git a/tools/guest-diag/wall2/t120.py b/tools/guest-diag/wall2/t120.py
new file mode 100644
index 0000000..68a1b8b
--- /dev/null
+++ b/tools/guest-diag/wall2/t120.py
@@ -0,0 +1,148 @@
+import gdb
+gr = gdb.execute
+state = {"n": 0, "any": 0, "pend": None, "base": None, "chits": 0, "cleared": 0,
+ "whits": 0, "wrote": 0}
+
+def u32(a):
+ return int(gdb.parse_and_eval("*(unsigned int*)0x%x" % a))
+def u64(a):
+ return int(gdb.parse_and_eval("*(unsigned long*)0x%x" % a))
+
+def ioc(d, t, nr, size):
+ return (d << 30) | (size << 16) | (t << 8) | nr
+
+VIDHEAP = ioc(3, 0x46, 0x4a, 0xb8)
+
+def glbase():
+ try:
+ pid = gdb.selected_inferior().pid
+ for line in open("/proc/%d/maps" % pid):
+ parts = line.split()
+ if "libnvidia-eglcore" in line and int(parts[2], 16) == 0:
+ return int(parts[0].split("-")[0], 16)
+ except Exception:
+ pass
+ return None
+
+class BC(gdb.Breakpoint):
+ def __init__(self, addr):
+ super().__init__("*0x%x" % addr, internal=False)
+ self.silent = True
+ def stop(self):
+ state["chits"] += 1
+ try:
+ obj = int(gdb.parse_and_eval("$rdi"))
+ n = int(gdb.parse_and_eval("$rsi"))
+ cnt = u32(obj + 0x22efc)
+ for i in range(0, min(cnt, 32)):
+ p = u64(obj + 0x1428 + i * 8)
+ if not p:
+ continue
+ tgt = p + n * 0x10
+ try:
+ v16 = int(gdb.parse_and_eval("*(unsigned short*)0x%x" % (tgt + 0xe)))
+ if v16 == 0xffff:
+ gr("set {unsigned short}0x%x = 0" % (tgt + 0xe))
+ gr("set {unsigned int}0x%x = 0" % tgt)
+ gr("set {unsigned short}0x%x = 0" % (tgt + 0xc))
+ state["cleared"] += 1
+ print("CLEAR#%d obj=0x%x n=0x%x tgt=0x%x" % (state["cleared"], obj, n, tgt), flush=True)
+ except Exception:
+ pass
+ except Exception:
+ pass
+ return False
+
+class BW(gdb.Breakpoint):
+ def __init__(self, addr):
+ super().__init__("*0x%x" % addr, internal=False)
+ self.silent = True
+ def stop(self):
+ state["whits"] += 1
+ try:
+ rbx = int(gdb.parse_and_eval("$rbx"))
+ sub = u64(rbx + 0x70)
+ obj2 = u64(sub + 0x58)
+ off = u32(obj2 + 0x60)
+ holder = u64(obj2 + 0x58)
+ base2 = u64(holder + 0x58)
+ word = base2 + off
+ val = u32(word)
+ if state["whits"] <= 6 or val == 0:
+ print("WORD#%d addr=0x%x val=0x%x" % (state["whits"], word, val), flush=True)
+ if val == 0:
+ gr("set {unsigned int}0x%x = 1" % word)
+ state["wrote"] += 1
+ except Exception:
+ pass
+ return False
+
+class IOC(gdb.Breakpoint):
+ def __init__(self):
+ super().__init__("ioctl", internal=False)
+ self.silent = True
+ def stop(self):
+ if state["base"] is None:
+ b = glbase()
+ if b:
+ state["base"] = b
+ print("GLBASE 0x%x" % b, flush=True)
+ try:
+ BC(b + 0xB33D30)
+ BW(b + 0xDCEBD9)
+ print("BC-BW-SET", flush=True)
+ except Exception as e:
+ print("bpset-fail %s" % e, flush=True)
+ try:
+ req = int(gdb.parse_and_eval("$rsi")) & 0xFFFFFFFF
+ arg = int(gdb.parse_and_eval("$rdx"))
+ except Exception:
+ return False
+ if state["pend"] is not None:
+ try:
+ st = u32(state["pend"] + 0x14)
+ at = u32(state["pend"] + 0x38)
+ off = u64(state["pend"] + 0x68)
+ print("PREV149-OUT st=0x%x attr=0x%x off=0x%x" % (st, at, off), flush=True)
+ except Exception as e:
+ print("PREV149-STATUS unreadable %s" % e, flush=True)
+ state["pend"] = None
+ if req != VIDHEAP:
+ return False
+ state["any"] += 1
+ try:
+ fn = u32(arg + 0x08)
+ size = u64(arg + 0x58)
+ attr = u32(arg + 0x38)
+ owner = u32(arg + 0x28)
+ flags = u32(arg + 0x34)
+ except Exception:
+ return False
+ if fn == 2 and size == 0x10000 and (attr & 0xf000) == 0x2000 and owner == 0x474C0000:
+ state["n"] += 1
+ print("HIT149 n=%d attr=0x%x flags=0x%x owner=0x%x align=0x%x" % (
+ state["n"], attr, flags, owner, u64(arg + 0x60)), flush=True)
+ if state["n"] == 1:
+ gr("bt 12")
+ newattr = attr & 0xFFFFCFFF
+ gr("set {unsigned int}0x%x = 0x%x" % (arg + 0x38, newattr))
+ print("FLIP149 attr 0x%x -> 0x%x" % (attr, newattr), flush=True)
+ state["pend"] = arg
+ return False
+
+try:
+ IOC()
+ print("BPS-SET", flush=True)
+except Exception as e:
+ print("bpset-fail %s" % e, flush=True)
+
+gr("set pagination off")
+gr("set confirm off")
+try:
+ gr("run")
+except Exception as e:
+ print("runend %s" % e, flush=True)
+print("T120-DONE n=%d any=%d chits=%d cleared=%d whits=%d wrote=%d" % (
+ state["n"], state["any"], state["chits"], state["cleared"],
+ state["whits"], state["wrote"]), flush=True)
+gr("quit")
diff --git a/tools/guest-diag/wall2/t121.py b/tools/guest-diag/wall2/t121.py
new file mode 100644
index 0000000..1ae25ed
--- /dev/null
+++ b/tools/guest-diag/wall2/t121.py
@@ -0,0 +1,151 @@
+import gdb
+gr = gdb.execute
+state = {"n": 0, "any": 0, "pend": None, "base": None, "chits": 0, "cleared": 0,
+ "whits": 0, "wrote": 0}
+
+def u32(a):
+ return int(gdb.parse_and_eval("*(unsigned int*)0x%x" % a))
+def u64(a):
+ return int(gdb.parse_and_eval("*(unsigned long*)0x%x" % a))
+
+def ioc(d, t, nr, size):
+ return (d << 30) | (size << 16) | (t << 8) | nr
+
+VIDHEAP = ioc(3, 0x46, 0x4a, 0xb8)
+
+def glbase():
+ try:
+ pid = gdb.selected_inferior().pid
+ for line in open("/proc/%d/maps" % pid):
+ parts = line.split()
+ if "libnvidia-eglcore" in line and int(parts[2], 16) == 0:
+ return int(parts[0].split("-")[0], 16)
+ except Exception:
+ pass
+ return None
+
+class BC(gdb.Breakpoint):
+ def __init__(self, addr):
+ super().__init__("*0x%x" % addr, internal=False)
+ self.silent = True
+ def stop(self):
+ state["chits"] += 1
+ try:
+ obj = int(gdb.parse_and_eval("$rdi"))
+ n = int(gdb.parse_and_eval("$rsi"))
+ cnt = u32(obj + 0x22efc)
+ for i in range(0, min(cnt, 32)):
+ p = u64(obj + 0x1428 + i * 8)
+ if not p:
+ continue
+ tgt = p + n * 0x10
+ try:
+ v16 = int(gdb.parse_and_eval("*(unsigned short*)0x%x" % (tgt + 0xe)))
+ if v16 == 0xffff:
+ gr("set {unsigned short}0x%x = 0" % (tgt + 0xe))
+ gr("set {unsigned int}0x%x = 0" % tgt)
+ gr("set {unsigned short}0x%x = 0" % (tgt + 0xc))
+ state["cleared"] += 1
+ print("CLEAR#%d obj=0x%x n=0x%x tgt=0x%x" % (state["cleared"], obj, n, tgt), flush=True)
+ except Exception:
+ pass
+ except Exception:
+ pass
+ return False
+
+class BW(gdb.Breakpoint):
+ def __init__(self, addr):
+ super().__init__("*0x%x" % addr, internal=False)
+ self.silent = True
+ def stop(self):
+ state["whits"] += 1
+ try:
+ rbx = int(gdb.parse_and_eval("$rbx"))
+ sub = u64(rbx + 0x70)
+ obj2 = u64(sub + 0x58)
+ off = u32(obj2 + 0x60)
+ holder = u64(obj2 + 0x58)
+ base2 = u64(holder + 0x58)
+ word = base2 + off
+ val = u32(word)
+ if state["whits"] <= 6 or val == 0:
+ print("WORD#%d addr=0x%x val=0x%x" % (state["whits"], word, val), flush=True)
+ if val == 0:
+ gr("set {unsigned int}0x%x = 1" % word)
+ state["wrote"] += 1
+ except Exception:
+ pass
+ return False
+
+class IOC(gdb.Breakpoint):
+ def __init__(self):
+ super().__init__("ioctl", internal=False)
+ self.silent = True
+ def stop(self):
+ if state["base"] is None:
+ b = glbase()
+ if b:
+ state["base"] = b
+ print("GLBASE 0x%x" % b, flush=True)
+ try:
+ BC(b + 0xB33D30)
+ BW(b + 0xDCEBD9)
+ print("BC-BW-SET", flush=True)
+ except Exception as e:
+ print("bpset-fail %s" % e, flush=True)
+ try:
+ req = int(gdb.parse_and_eval("$rsi")) & 0xFFFFFFFF
+ arg = int(gdb.parse_and_eval("$rdx"))
+ except Exception:
+ return False
+ if state["pend"] is not None:
+ try:
+ st = u32(state["pend"] + 0x14)
+ at = u32(state["pend"] + 0x38)
+ off = u64(state["pend"] + 0x68)
+ print("PREV149-OUT st=0x%x attr=0x%x off=0x%x" % (st, at, off), flush=True)
+ except Exception as e:
+ print("PREV149-STATUS unreadable %s" % e, flush=True)
+ state["pend"] = None
+ if req != VIDHEAP:
+ return False
+ state["any"] += 1
+ try:
+ fn = u32(arg + 0x08)
+ size = u64(arg + 0x58)
+ attr = u32(arg + 0x38)
+ owner = u32(arg + 0x28)
+ flags = u32(arg + 0x34)
+ except Exception:
+ return False
+ if fn == 2:
+ state["n"] += 1
+ if state["n"] >= 120:
+ print("VH#%d size=0x%x align=0x%x attr=0x%x attr2=0x%x flags=0x%x owner=0x%x range=0x%x-0x%x" % (
+ state["n"], size, u64(arg + 0x60), attr, u32(arg + 0x90), flags, owner,
+ u64(arg + 0x80), u64(arg + 0x88)), flush=True)
+ if size == 0x10000 and (attr & 0xf000) == 0x2000 and owner == 0x474C0000:
+ print("HIT149 attr=0x%x flags=0x%x" % (attr, flags), flush=True)
+ gr("bt 12")
+ newattr = attr & 0xFFFFCFFF
+ gr("set {unsigned int}0x%x = 0x%x" % (arg + 0x38, newattr))
+ print("FLIP149 attr 0x%x -> 0x%x" % (attr, newattr), flush=True)
+ state["pend"] = arg
+ return False
+
+try:
+ IOC()
+ print("BPS-SET", flush=True)
+except Exception as e:
+ print("bpset-fail %s" % e, flush=True)
+
+gr("set pagination off")
+gr("set confirm off")
+try:
+ gr("run")
+except Exception as e:
+ print("runend %s" % e, flush=True)
+print("T120-DONE n=%d any=%d chits=%d cleared=%d whits=%d wrote=%d" % (
+ state["n"], state["any"], state["chits"], state["cleared"],
+ state["whits"], state["wrote"]), flush=True)
+gr("quit")
diff --git a/tools/guest-diag/wall2/t122.py b/tools/guest-diag/wall2/t122.py
new file mode 100644
index 0000000..8c9c6fd
--- /dev/null
+++ b/tools/guest-diag/wall2/t122.py
@@ -0,0 +1,151 @@
+import gdb
+gr = gdb.execute
+state = {"n": 0, "any": 0, "pend": None, "base": None, "chits": 0, "cleared": 0,
+ "whits": 0, "wrote": 0}
+
+def u32(a):
+ return int(gdb.parse_and_eval("*(unsigned int*)0x%x" % a))
+def u64(a):
+ return int(gdb.parse_and_eval("*(unsigned long*)0x%x" % a))
+
+def ioc(d, t, nr, size):
+ return (d << 30) | (size << 16) | (t << 8) | nr
+
+VIDHEAP = ioc(3, 0x46, 0x4a, 0xb8)
+
+def glbase():
+ try:
+ pid = gdb.selected_inferior().pid
+ for line in open("/proc/%d/maps" % pid):
+ parts = line.split()
+ if "libnvidia-eglcore" in line and int(parts[2], 16) == 0:
+ return int(parts[0].split("-")[0], 16)
+ except Exception:
+ pass
+ return None
+
+class BC(gdb.Breakpoint):
+ def __init__(self, addr):
+ super().__init__("*0x%x" % addr, internal=False)
+ self.silent = True
+ def stop(self):
+ state["chits"] += 1
+ try:
+ obj = int(gdb.parse_and_eval("$rdi"))
+ n = int(gdb.parse_and_eval("$rsi"))
+ cnt = u32(obj + 0x22efc)
+ for i in range(0, min(cnt, 32)):
+ p = u64(obj + 0x1428 + i * 8)
+ if not p:
+ continue
+ tgt = p + n * 0x10
+ try:
+ v16 = int(gdb.parse_and_eval("*(unsigned short*)0x%x" % (tgt + 0xe)))
+ if v16 == 0xffff:
+ gr("set {unsigned short}0x%x = 0" % (tgt + 0xe))
+ gr("set {unsigned int}0x%x = 0" % tgt)
+ gr("set {unsigned short}0x%x = 0" % (tgt + 0xc))
+ state["cleared"] += 1
+ print("CLEAR#%d obj=0x%x n=0x%x tgt=0x%x" % (state["cleared"], obj, n, tgt), flush=True)
+ except Exception:
+ pass
+ except Exception:
+ pass
+ return False
+
+class BW(gdb.Breakpoint):
+ def __init__(self, addr):
+ super().__init__("*0x%x" % addr, internal=False)
+ self.silent = True
+ def stop(self):
+ state["whits"] += 1
+ try:
+ rbx = int(gdb.parse_and_eval("$rbx"))
+ sub = u64(rbx + 0x70)
+ obj2 = u64(sub + 0x58)
+ off = u32(obj2 + 0x60)
+ holder = u64(obj2 + 0x58)
+ base2 = u64(holder + 0x58)
+ word = base2 + off
+ val = u32(word)
+ if state["whits"] <= 6 or val == 0:
+ print("WORD#%d addr=0x%x val=0x%x" % (state["whits"], word, val), flush=True)
+ if val == 0:
+ gr("set {unsigned int}0x%x = 1" % word)
+ state["wrote"] += 1
+ except Exception:
+ pass
+ return False
+
+class IOC(gdb.Breakpoint):
+ def __init__(self):
+ super().__init__("ioctl", internal=False)
+ self.silent = True
+ def stop(self):
+ if state["base"] is None:
+ b = glbase()
+ if b:
+ state["base"] = b
+ print("GLBASE 0x%x" % b, flush=True)
+ try:
+ BC(b + 0xB33D30)
+ BW(b + 0xDCEBD9)
+ print("BC-BW-SET", flush=True)
+ except Exception as e:
+ print("bpset-fail %s" % e, flush=True)
+ try:
+ req = int(gdb.parse_and_eval("$rsi")) & 0xFFFFFFFF
+ arg = int(gdb.parse_and_eval("$rdx"))
+ except Exception:
+ return False
+ if state["pend"] is not None:
+ try:
+ st = u32(state["pend"] + 0x14)
+ at = u32(state["pend"] + 0x38)
+ off = u64(state["pend"] + 0x68)
+ print("PREV149-OUT st=0x%x attr=0x%x off=0x%x" % (st, at, off), flush=True)
+ except Exception as e:
+ print("PREV149-STATUS unreadable %s" % e, flush=True)
+ state["pend"] = None
+ if req != VIDHEAP:
+ return False
+ state["any"] += 1
+ try:
+ fn = u32(arg + 0x08)
+ size = u64(arg + 0x58)
+ attr = u32(arg + 0x38)
+ owner = u32(arg + 0x28)
+ flags = u32(arg + 0x34)
+ except Exception:
+ return False
+ if fn == 2:
+ state["n"] += 1
+ if state["n"] >= 120:
+ print("VH#%d size=0x%x align=0x%x attr=0x%x attr2=0x%x flags=0x%x owner=0x%x range=0x%x-0x%x" % (
+ state["n"], size, u64(arg + 0x60), attr, u32(arg + 0x90), flags, owner,
+ u64(arg + 0x80), u64(arg + 0x88)), flush=True)
+ if size >= 0x10000 and (attr & 0xf000) == 0x2000 and owner == 0x474C0000:
+ print("HIT149 size=0x%x attr=0x%x flags=0x%x" % (size, attr, flags), flush=True)
+ gr("bt 12")
+ newattr = attr & 0xFFFFCFFF
+ gr("set {unsigned int}0x%x = 0x%x" % (arg + 0x38, newattr))
+ print("FLIP149 attr 0x%x -> 0x%x" % (attr, newattr), flush=True)
+ state["pend"] = arg
+ return False
+
+try:
+ IOC()
+ print("BPS-SET", flush=True)
+except Exception as e:
+ print("bpset-fail %s" % e, flush=True)
+
+gr("set pagination off")
+gr("set confirm off")
+try:
+ gr("run")
+except Exception as e:
+ print("runend %s" % e, flush=True)
+print("T120-DONE n=%d any=%d chits=%d cleared=%d whits=%d wrote=%d" % (
+ state["n"], state["any"], state["chits"], state["cleared"],
+ state["whits"], state["wrote"]), flush=True)
+gr("quit")
diff --git a/tools/vk/vk_frame.c b/tools/vk/vk_frame.c
index 8b864ff..62548be 100644
--- a/tools/vk/vk_frame.c
+++ b/tools/vk/vk_frame.c
@@ -27,6 +27,8 @@
#define FRAME_OFFSET 0x100000UL
#define HDR_MAGIC 0x4b564d46u
+static VkImageTiling g_tiling = VK_IMAGE_TILING_OPTIMAL;
+
struct frame_hdr {
uint32_t magic;
uint32_t version;
@@ -59,6 +61,11 @@ static uint64_t now_ns(void)
int main(int argc, char **argv)
{
+ if (argc > 7 && atoi(argv[7]) == 1) {
+ g_tiling = VK_IMAGE_TILING_LINEAR;
+ printf("TILING linear\n");
+ }
+
int frames = argc > 1 ? atoi(argv[1]) : 60;
uint32_t W = argc > 2 ? (uint32_t)atoi(argv[2]) : 640;
uint32_t H = argc > 3 ? (uint32_t)atoi(argv[3]) : 480;
@@ -179,7 +186,7 @@ int main(int argc, char **argv)
ci.mipLevels = 1;
ci.arrayLayers = 1;
ci.samples = VK_SAMPLE_COUNT_1_BIT;
- ci.tiling = VK_IMAGE_TILING_OPTIMAL;
+ ci.tiling = g_tiling;
ci.usage = VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
@@ -244,7 +251,25 @@ int main(int argc, char **argv)
break;
}
}
- VKC(vkAllocateMemory(dev, &ma, NULL, &buf_mem));
+ {
+ int rc_ = vkAllocateMemory(dev, &ma, NULL, &buf_mem);
+ if (rc_ != VK_SUCCESS) {
+ VkPhysicalDeviceMemoryProperties mp2;
+ uint32_t k2;
+ vkGetPhysicalDeviceMemoryProperties(phys, &mp2);
+ for (k2 = 0; k2 < mp2.memoryTypeCount && rc_ != VK_SUCCESS; k2++) {
+ if (!(mr.memoryTypeBits & (1u << k2)))
+ continue;
+ ma.memoryTypeIndex = k2;
+ rc_ = vkAllocateMemory(dev, &ma, NULL, &buf_mem);
+ printf("BUF retry type=%u rc=%d\n", k2, rc_);
+ }
+ }
+ if (rc_ != VK_SUCCESS) {
+ printf("VKFAIL vkAllocateMemory(buf) -> %d (line %d)\n", rc_, __LINE__);
+ return 1;
+ }
+ }
VKC(vkBindBufferMemory(dev, buf, buf_mem, 0));
}
/* shader + pipeline */