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
|
#!/usr/bin/env python3
# pt_bind.py - bind GMMU PT pages to VA bases and read out VA->PA for the
# client's ring/window family. Strategy:
# 1. ring-page candidates: pages whose 8B-aligned u64 pairs look like
# GPFIFO entries (e0 = push VA & ~3 in the 0x44x-0x4ex families with
# va<4G; e1 top byte 0; length bits sane).
# 2. anchor: for each candidate PA and each family VA, check every 4KB
# page for entry idx=(VA>>12)&0x1ff whose PA part == candidate PA.
# A hit binds (PT page, VA, PA).
# 3. read out: from bound PT pages, decode entries for the whole VA list.
import mmap, struct, sys
F = "/dev/shm/vgpu-guest-ram"
FAMILY = [
0x4020000, 0x4100000, 0x4160000, # r689 eng rings
0x44a0000, 0x4500000, 0x4560000, # r690 ring VAs
0x4480000, 0x4485000, 0x4480980, 0x4480f1c,
0x4ca0000, 0x4ca5000, 0x4d00000, 0x4b17000,
]
def gpa_to_off(pa):
return pa if pa < 0x80000000 else pa - 0x80000000
def off_to_gpa(off):
return off if off < 0x80000000 else off + 0x80000000
def pte_pa(v):
return (v >> 8) << 12
def main():
f = open(F, "rb")
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
size = len(mm)
print("# file size 0x%x" % size)
# ---- pass 1: ring-page candidates by content ----
cands = []
pos = 0
step = 4096
while pos + step <= size:
d = mm[pos:pos+step]
hits = []
for i in range(0, step, 8):
e0 = int.from_bytes(d[i:i+4], "little")
e1 = int.from_bytes(d[i+4:i+8], "little")
if 0x4400000 <= e0 <= 0x4e00000 and (e0 & 3) == 0 and (e1 & 0xff) == 0:
lw = (e1 >> 10) & 0x1fffff
if 0x40 <= lw <= 0x100000:
hits.append((i, e0, lw << 2))
if len(hits) >= 3:
cands.append((off_to_gpa(pos), hits))
if len(cands) >= 12:
break
pos += step
print("# ring-page candidates: %d" % len(cands))
for pa, hits in cands:
print("cand pa=0x%x hits=%d first=%s" %
(pa, len(hits), ["0x%x/0x%x" % (h[1], h[2]) for h in hits[:4]]))
if not cands:
return 1
# ---- pass 2: anchor PT pages for candidate PAs x family VAs ----
idxs = {}
for va in FAMILY:
idxs.setdefault((va >> 12) & 0x1ff, []).append(va)
cand_pas = set(pa for pa, _ in cands)
bound = []
pos = 0
while pos + step <= size:
pg = off_to_gpa(pos)
# skip scanning the candidate pages themselves
if pg not in cand_pas:
d = mm[pos:pos+step]
for idx, vas in idxs.items():
o = idx * 8
v = int.from_bytes(d[o:o+8], "little")
if v & 1:
pa = pte_pa(v)
if pa in cand_pas:
bound.append((pg, idx, vas, pa, v & 0xff))
pos += step
print("# PT anchors found: %d" % len(bound))
for pg, idx, vas, pa, fl in bound[:20]:
print("PT pa=0x%x entry[0x%x] flags=0x%x -> PA=0x%x (VA cands %s)" %
(pg, idx, fl, pa, ["0x%x" % x for x in vas]))
# ---- pass 3: read out PT entries for the whole family from each bound PT page ----
seen_pt = set()
for pg, idx, vas, pa, fl in bound:
if pg in seen_pt:
continue
seen_pt.add(pg)
off = gpa_to_off(pg)
d = mm[off:off+4096]
print("== PT page pa=0x%x family readout:" % pg)
for va in FAMILY:
i = (va >> 12) & 0x1ff
v = int.from_bytes(d[i*8:i*8+8], "little")
if v & 1:
print(" va=0x%x idx=0x%x -> pa=0x%x (raw=0x%x fl=0x%x)" %
(va, i, pte_pa(v), v, v & 0xff))
else:
print(" va=0x%x idx=0x%x -> absent (raw=0x%x)" % (va, i, v))
mm.close(); f.close()
return 0
if __name__ == "__main__":
sys.exit(main())
|