summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag
diff options
context:
space:
mode:
Diffstat (limited to 'tools/guest-diag')
-rw-r--r--tools/guest-diag/guest_pin_stream.py198
1 files changed, 113 insertions, 85 deletions
diff --git a/tools/guest-diag/guest_pin_stream.py b/tools/guest-diag/guest_pin_stream.py
index d8566c2..96c1e43 100644
--- a/tools/guest-diag/guest_pin_stream.py
+++ b/tools/guest-diag/guest_pin_stream.py
@@ -692,7 +692,20 @@ def resolve_va_client_mem(pid, ent_va, off, ln, out):
maps = open('/proc/%d/maps' % pid).read().splitlines()
except OSError:
return False
- tried = 0
+ # SESSION 153-r1398: collect the candidate maps first and run a
+ # GLOBAL two-pass candidate order - the neighbourhood offsets for
+ # EVERY map, then the full sweep. The r1398 client (glxgears) had
+ # 74 qualifying rw mappings ahead of the nvidiactl mapping that
+ # holds the MME 0x114 pack; their full sweeps burned 37 808 of the
+ # 8 192-candidate budget before the target map was reached, so the
+ # strict client-mem parse answered False while a manual parse of
+ # the same map accepted at idx 1 (near-first PER MAP is not enough
+ # when the budget is global; near-first ACROSS maps is). The
+ # content gates are unchanged: only _window_accept passes emit.
+ near_def = [0, 1, -1, 2, -2, 4, -4, 8, -8, 16, -16, 32, -32,
+ 64, -64, 128, -128, 256, -256]
+ near_set = set(near_def)
+ cand_maps = []
for line in maps:
parts = line.split()
if len(parts) < 5 or 'rw' not in parts[1]:
@@ -707,92 +720,107 @@ def resolve_va_client_mem(pid, ent_va, off, ln, out):
continue
if hi - lo < (need + 4) * 0x1000 or hi - lo > (64 << 20):
continue
- pages = (hi - lo) >> 12
- if k is None or k == 0:
- # Unknown family, or the FAMILY-BASE page (k == 0): neither
- # has an identity page index to key on, so sweep every page
- # start of the mapping (bounded). SESSION 153-CONTINUED-65:
- # the 0x3200000 macro-upload push's window sits at an
- # arbitrary page offset inside the client's mapping (the
- # r1124 power-of-two neighbourhood never tried it; the
- # window content measures code=712, exact-end, under the
- # strict walk).
- adjs = range(0, max(1, pages - need + 1))
- else:
- # SESSION 153-CONTINUED-65: neighbourhood FIRST, then the
- # FULL page sweep. The r1125 setup push (family pages k=1
- # and k=33, 60216 B) failed every power-of-two offset while
- # a matching window could sit at an arbitrary mapping page;
- # every candidate still passes _window_accept before any
- # pin is emitted, so the widened sweep cannot pin a window
- # the parse rejects.
- near = [0, 1, -1, 2, -2, 4, -4, 8, -8, 16, -16, 32, -32,
- 64, -64, 128, -128, 256, -256]
- near_set = set(near)
- rest = [a for a in range(-k, pages - need + 1 - k)
- if a not in near_set]
- adjs = near + rest
- for adj in adjs:
- idx = (k + adj) if k is not None else adj
- if idx < 0 or idx + need > pages:
- continue
- win = lo + idx * 0x1000 + off
- tried += 1
- if tried > 8192:
- break
- try:
- first = os.pread(mfd, 0x1000 - off, win)
- except OSError:
- continue
- if not first:
- continue
- # An all-zero first page is a legitimate push head (the
- # method stream may start later); only a page with non-zero
- # content and no MME-setup run is rejected outright.
- if not _mme_run_in_page(first) and any(first):
- continue
- buf = b''
- left = ln
- a = win
- ok = True
- while left > 0:
- n = min(left, 0x1000 - (a & 0xFFF))
+ cand_maps.append((lo, (hi - lo) >> 12))
+ tried = 0
+ if k is None or k == 0:
+ # Unknown family, or the FAMILY-BASE page (k == 0): neither has
+ # an identity page index to key on, so sweep every page start
+ # of the mapping (bounded). SESSION 153-CONTINUED-65: the
+ # 0x3200000 macro-upload push's window sits at an arbitrary
+ # page offset inside the client's mapping (the r1124
+ # power-of-two neighbourhood never tried it; the window content
+ # measures code=712, exact-end, under the strict walk).
+ phases = (0,)
+ else:
+ phases = (0, 1)
+ for phase in phases:
+ tried = 0
+ for (lo, pages) in cand_maps:
+ if k is None or k == 0:
+ adjs = range(0, max(1, pages - need + 1))
+ capped = True
+ elif phase == 0:
+ # SESSION 153-CONTINUED-65: neighbourhood first, then
+ # the FULL page sweep. The r1125 setup push (family
+ # pages k=1 and k=33, 60216 B) failed every power-of-two
+ # offset while a matching window could sit at an
+ # arbitrary mapping page; every candidate still passes
+ # _window_accept before any pin is emitted, so the
+ # widened sweep cannot pin a window the parse rejects.
+ # The near pass is cheap (<=19 windows per map) and is
+ # not charged against the r1398 global budget.
+ adjs = near_def
+ capped = False
+ else:
+ adjs = [a for a in range(-k, pages - need + 1 - k)
+ if a not in near_set]
+ capped = True
+ for adj in adjs:
+ idx = (k + adj) if k is not None else adj
+ if idx < 0 or idx + need > pages:
+ continue
+ win = lo + idx * 0x1000 + off
+ tried += 1
+ if capped and tried > 8192:
+ break
try:
- d = os.pread(mfd, n, a)
+ first = os.pread(mfd, 0x1000 - off, win)
except OSError:
- ok = False
- break
- if not d or len(d) != n:
- ok = False
- break
- buf += d
- a += n
- left -= n
- if not ok:
- continue
- acc, mode = _window_accept(buf, ln)
- if not acc:
- continue
- pas = []
- for j in range(need):
- vpg = (ent_va & ~0xFFF) + j * 0x1000
- cpg = lo + (idx + j) * 0x1000
- os.lseek(pfd, (cpg >> 12) * 8, 0)
- e = int.from_bytes(os.read(pfd, 8), 'little')
- if not (e >> 63) & 1:
- pas = None
- break
- pas.append(((e & ((1 << 55) - 1)) << 12))
- if not pas:
- continue
- for j, pa in enumerate(pas):
- out.append("0x%x,0x%x,1\n"
- % ((ent_va & ~0xFFF) + j * 0x1000, pa))
- dbg("demand-va: 0x%x -> pa=0x%x (%s client-mem parse"
- " %u B ok pages=%u map=0x%x idx=%d)"
- % (ent_va & ~0xFFF, pas[0], mode, ln, need, lo, idx))
- return True
- if tried > 8192:
+ continue
+ if not first:
+ continue
+ # An all-zero first page is a legitimate push head (the
+ # method stream may start later); only a page with non-zero
+ # content and no MME-setup run is rejected outright.
+ if not _mme_run_in_page(first) and any(first):
+ continue
+ buf = b''
+ left = ln
+ a = win
+ ok = True
+ while left > 0:
+ n = min(left, 0x1000 - (a & 0xFFF))
+ try:
+ d = os.pread(mfd, n, a)
+ except OSError:
+ ok = False
+ break
+ if not d or len(d) != n:
+ ok = False
+ break
+ buf += d
+ a += n
+ left -= n
+ if not ok:
+ continue
+ acc, mode = _window_accept(buf, ln)
+ if not acc:
+ continue
+ pas = []
+ for j in range(need):
+ vpg = (ent_va & ~0xFFF) + j * 0x1000
+ cpg = lo + (idx + j) * 0x1000
+ os.lseek(pfd, (cpg >> 12) * 8, 0)
+ e = int.from_bytes(os.read(pfd, 8), 'little')
+ if not (e >> 63) & 1:
+ pas = None
+ break
+ pas.append(((e & ((1 << 55) - 1)) << 12))
+ if not pas:
+ continue
+ for j, pa in enumerate(pas):
+ out.append("0x%x,0x%x,1\n"
+ % ((ent_va & ~0xFFF) + j * 0x1000, pa))
+ dbg("demand-va: 0x%x -> pa=0x%x (%s client-mem parse"
+ " %u B ok pages=%u map=0x%x idx=%d)"
+ % (ent_va & ~0xFFF, pas[0], mode, ln, need, lo, idx))
+ return True
+ # The near pass (phase 0, family anchors) is cheap per map and
+ # completes for every map; only the full-sweep pass keeps the
+ # historical 8192-candidate bound (r1398: prior maps burned
+ # 37 808 candidates before the content map, so the bound must
+ # never apply to the near pass or it dies before the target).
+ if (k is None or k == 0 or phase == 1) and tried > 8192:
break
return False
finally: