summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/wall2/gr_walk2.py
blob: 7a50bc8aeeeab0d20c3ce9d71a82ba6dcad1aad7 (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
83
84
85
86
87
88
89
#!/usr/bin/env python3
# gr_walk2.py - deeper walk: from the window memdesc records, follow the
# kernel pointers 2-3 levels and search for page-array records
# {vmap_cpu, flags, PA, 0x1000} (2+ consecutive) to recover the page lists.
import sys, struct
sys.path.insert(0, "/tmp")
import guest_pin_stream as gps

PAT_SIZE = struct.pack("<Q", 0x20000)
PAT_FLAGS = struct.pack("<Q", 0x03008000)

def is_pa(v):
    return 0x100000000 <= v < 0x180000000

def page_array_at(kc, va, n=0x400):
    """Return [(off, [PA,...])] for runs of {ptr,flags,PA,0x1000} records."""
    d = kc.read(va, n)
    if not d:
        return []
    out = []
    i = 0
    while i + 32 <= len(d):
        p, fl, pa, z = struct.unpack_from("<4Q", d, i)
        if z == 0x1000 and is_pa(pa) and gps.is_kern_ptr(p):
            run = [pa]
            j = i + 32
            while j + 32 <= len(d):
                p2, fl2, pa2, z2 = struct.unpack_from("<4Q", d, j)
                if z2 == 0x1000 and is_pa(pa2):
                    run.append(pa2)
                    j += 32
                else:
                    break
            if len(run) >= 2:
                out.append((i, run))
            i = j
        else:
            i += 8
    return out

def main():
    kc = gps.Kcore()
    if not kc.load():
        print("# kcore load failed"); return 1
    pov = kc.page_offset_base
    # find memdesc records (fast direct-map scan)
    found = []
    CH = 1 << 20
    pa = 0x100000000
    while pa < 0x180000000 and len(found) < 12:
        d = kc.read(pov + pa, CH)
        if d:
            st = 0
            while True:
                i = d.find(PAT_SIZE, st)
                if i < 0:
                    break
                st = i + 1
                if i + 16 <= len(d) and d[i+8:i+16] == PAT_FLAGS:
                    found.append((pa + i, d[i:i+0x40]))
        pa += CH
    print("# memdesc records: %d" % len(found))
    for (gpa, rec) in found:
        size, flags = struct.unpack_from("<QQ", rec, 0)
        wpa = struct.unpack_from("<Q", rec, 0x10)[0]
        print("rec@0x%x size=0x%x pa=0x%x" % (gpa, size, wpa))
        kptrs = [struct.unpack_from("<Q", rec, o)[0] for o in range(0x18, 0x40, 8)]
        for kp in kptrs:
            if not gps.is_kern_ptr(kp):
                continue
            # level 1: read the target, look for page arrays directly
            for arr in page_array_at(kc, kp):
                print("   L1 kptr 0x%x: page-array run at +0x%x: %s" %
                      (kp, arr[0], ["0x%x" % x for x in arr[1][:12]]))
            # level 2: follow pointers in the target
            d = kc.read(kp, 0x100)
            if not d:
                continue
            for o in range(0, 0x100, 8):
                p2 = struct.unpack_from("<Q", d, o)[0]
                if not gps.is_kern_ptr(p2):
                    continue
                for arr in page_array_at(kc, p2):
                    print("   L2 0x%x -> 0x%x: page-array at +0x%x: %s" %
                          (kp, p2, arr[0], ["0x%x" % x for x in arr[1][:12]]))
    return 0

if __name__ == "__main__":
    sys.exit(main())