summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/guest-diag/wall2/README-session77.md84
-rw-r--r--tools/guest-diag/wall2/a8clear.py52
-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
5 files changed, 586 insertions, 0 deletions
diff --git a/tools/guest-diag/wall2/README-session77.md b/tools/guest-diag/wall2/README-session77.md
index 1ae9a62..1834c29 100644
--- a/tools/guest-diag/wall2/README-session77.md
+++ b/tools/guest-diag/wall2/README-session77.md
@@ -1061,3 +1061,87 @@ 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).
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/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")