summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/wall2/wall2_resolver5.py
blob: faec90202aa6d467a396f9bb2a514b9c17e15404 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env python3
# wall2_resolver5.py - final: continuous scan, all-candidate injection.
#
# Structure (r675, verified): an RM page holds the client channel's window
# records and memdescs:
#   {0x4480000, 0x20000}  push window record
#   {0x44a0000, 0x40000}  ring window record
#   {0x20000, 0x03008000, PUSH_PA}   push window memdesc
#   {0x40000, 0x03008000, RING_PA}   ring window memdesc (PA == validated ring pa)
# The resolver scans CONTINUOUSLY (no ring-line gate), finds pages holding the
# pair of window records, and injects 0x4480000 -> every 0x20000 memdesc PA in
# the same page (last write wins in the service; content check arbitrates).
import subprocess, struct, time, sys, os

RAM = "/dev/shm/vgpu-guest-ram"
PARAM = "/sys/module/vgsp/parameters/sema_map_add"
LOG = "/tmp/opencode/wall2_resolver5.log"
PUSH_VA = 0x4480000
RING_VA = 0x44a0000
WATCH_S = int(os.environ.get("W2_WATCH", "600"))

logf = open(LOG, "a", buffering=1)
def log(*a):
    s = " ".join(str(x) for x in a)
    print(s, flush=True)
    logf.write("[%s] %s\n" % (time.strftime("%H:%M:%S"), s))

img = open(RAM, "rb", buffering=0)
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)
def rd(pa, n):
    off = gpa2off(pa)
    if off is None: return None
    img.seek(off); return img.read(n)

def inject(va, pa, ln=0x1000, ap=1):
    cmd = "echo 0x%x,0x%x,0x%x,%d > %s" % (va, pa, ln, ap, PARAM)
    r = subprocess.run(["sudo", "sh", "-c", cmd], capture_output=True, text=True)
    if r.returncode == 0:
        log("INJECT va=0x%x -> pa=0x%x" % (va, pa))
        return True
    log("inject FAIL rc=%d %s" % (r.returncode, r.stderr.strip()))
    return False

def find_qpair(a, b):
    img.seek(0, 2); fsz = img.tell()
    pat = struct.pack("<QQ", a, b)
    out = []
    CH = 64 << 20
    pos = 0
    while pos < fsz:
        img.seek(pos); d = img.read(min(CH, fsz - pos))
        st = 0
        while True:
            i = d.find(pat, st)
            if i < 0: break
            st = i + 1
            gpa = off2gpa(pos + i)
            out.append(gpa & ~0xfff)
        pos += CH
    return out

def main():
    log("=== resolver5 start (watch=%ds) ===" % WATCH_S)
    injected = set()
    deadline = time.time() + WATCH_S
    while time.time() < deadline:
        try:
            push_pages = set(find_qpair(PUSH_VA, 0x20000))
            ring_pages = set(find_qpair(RING_VA, 0x40000))
        except Exception as e:
            log("scan error:", e); time.sleep(2); continue
        for page in sorted(push_pages & ring_pages):
            d = rd(page, 0x1000)
            if not d: continue
            # collect 0x20000 descs in this page
            cands = []
            for o in range(0, 0x1000 - 24, 8):
                sz, fl = struct.unpack_from("<QQ", d, o)
                if sz == 0x20000 and fl == 0x03008000:
                    pa = struct.unpack_from("<Q", d, o + 16)[0]
                    if 0x100000000 <= pa < 0x180000000:
                        cands.append((o, pa))
            for (o, pa) in cands:
                key = (page, o)
                if key in injected: continue
                pd = rd(pa, 0x40)
                nz = sum(1 for b in pd if b) if pd else -1
                log("page=0x%x desc@0x%x pa=0x%x live_nz=%d head=%s"
                    % (page, o, pa, nz, pd.hex()[:64] if pd else "?"))
                if inject(PUSH_VA, pa):
                    injected.add(key)
        time.sleep(1)
    log("=== resolver5 done: %d injection(s) ===" % len(injected))
    return 0

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