1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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())
|