summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/wall2/scan_r673.py
blob: 71a012837bfaf90f427779f77c4c0fb0acae9aba (plain)
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
#!/usr/bin/env python3
# Analyze the live r673 guest RAM: locate every reference to the validated ring
# PA (0x12a783000) including flag-bit variants, and characterize the record
# arrays around each hit. Also dump the two pages named in the handoff.
import numpy as np, struct, sys, time

RAM="/dev/shm/vgpu-guest-ram"
RING=0x12a783000
MASK=~0xfff & ((1<<64)-1)

def gpa2off(pa):
    if pa < 0x80000000: return pa
    if pa < 0x180000000: return 0x80000000+(pa-0x100000000)
    return None
def off2gpa(off):
    return off if off < 0x80000000 else 0x100000000+(off-0x80000000)

img=open(RAM,"rb")
import os
fsz=os.path.getsize(RAM)
print("ram size 0x%x" % fsz)

t0=time.time()
hits=[]
CH=64<<20
for pos in range(0,fsz,CH):
    img.seek(pos); d=img.read(min(CH,fsz-pos))
    n=len(d)//8
    a=np.frombuffer(d[:n*8],dtype=np.uint64)
    idx=np.nonzero((a & np.uint64(MASK))==np.uint64(RING))[0]
    for i in idx:
        off=pos+int(i)*8
        val=int(a[i])
        hits.append((off,val))
print("hits=%d scan=%.1fs" % (len(hits), time.time()-t0))
for off,val in hits:
    print("  off=0x%x gpa=0x%x val=0x%x (low=0x%x)" % (off,off2gpa(off),val,val&0xfff))

# characterize each hit page
def rd(pa,n):
    off=gpa2off(pa)
    if off is None or off+n>fsz: return None
    img.seek(off); return img.read(n)

def plausible_pa(v): return 0x100000000 <= (v & MASK) < 0x180000000
def ptrish(v):
    return (0xffff800000000000 <= v < 0xffffffffffffffff)

for off,val in hits:
    gpa=off2gpa(off)
    page=gpa & MASK
    rec_off=gpa-page
    d=rd(page,0x1000)
    print("=== hit gpa=0x%x page=0x%x rec_off=0x%x ===" % (gpa,page,rec_off))
    for stride in (0x20,0x40,0x60,0x30,0x50,0x28,0x48):
        # record start such that the hit qword falls inside the record
        for rel in range(0,stride,8):
            start=rec_off-rel
            if start<0: continue
            # walk records; count plausible PA fields anywhere in each record
            n_pa=0; n_ptr=0; n_1000=0
            for o in range(start,0x1000-stride,stride):
                rec=d[o:o+stride]
                qs=struct.unpack_from("<%dQ"%(stride//8),rec,0)
                if any(plausible_pa(q) for q in qs): n_pa+=1
                if any(ptrish(q) for q in qs): n_ptr+=1
                if 0x1000 in qs: n_1000+=1
            if n_pa>=8:
                print("  stride=0x%x start=0x%x: PA-records=%d ptr-records=%d 0x1000=%d" % (stride,start,n_pa,n_ptr,n_1000))
    # dump the first 0x100 bytes of the page and around the hit
    print("  page head: %s" % d[:0x60].hex())
    s=max(0,rec_off-0x40); e=min(0x1000,rec_off+0x80)
    print("  around hit [0x%x..0x%x]: %s" % (s,e,d[s:e].hex()))

# dump the two named pages
for pa in (0x101499000,0x127cf2000):
    d=rd(pa,0x1000)
    print("=== page 0x%x ===" % pa)
    if d is None: print("  unreadable"); continue
    for o in range(0,0x120,0x20):
        qs=struct.unpack_from("<4Q",d,o)
        print("  +0x%03x: %016x %016x %016x %016x" % ((o,)+qs))