aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/vcn_sweep_detect.py
blob: 0e347a101798b063adc5a5f71d2c6496f7d9b461 (plain) (blame)
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
#!/usr/bin/env python3
"""Sweep index space and diff the VCN MMIO window after each batch.

First attempt used indices 0x100..0x13E and nothing in MMIO 0x7800..0x7F00 held
the magic. Either the mapping is not base+index into that window, or the writes
land in the DPG shadow register file rather than the MMIO-visible set.

Cover the low index space densely (0..503, 8 calls x 63 entries) and also probe
a wider MMIO window, so a mapping offset cannot hide the effect.
"""
import struct, time
from tinygrad import Device
import tinygrad.runtime.autogen.am.am as am

OUT = "/tmp/vcnsweep.txt"
MAGIC = 0xA5A50000


def say(s):
    with open(OUT, "a") as f:
        f.write(s + "\n")
    print(s, flush=True)


adev = Device["AMD"].iface.dev_impl
psp = adev.psp
hb = adev.reg("regMP0_SMN_C2PMSG_81")


def alive():
    a = hb.read(); time.sleep(0.25); return hb.read() != a


BASE, END = 0x7000, 0x8200          # wider than the VCN bases 0x7800 / 0x7E00


def snap():
    o = {}
    for r in range(BASE, END):
        try:
            o[r] = adev.rreg(r)
        except Exception:
            pass
    return o


def submit(payload):
    psp._prep_msg1(memoryview(bytes(payload)))
    c = am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_LOAD_IP_FW)
    c.cmd.cmd_load_ip_fw.fw_phy_addr_lo = psp.msg1_addr & 0xFFFFFFFF
    c.cmd.cmd_load_ip_fw.fw_phy_addr_hi = psp.msg1_addr >> 32
    c.cmd.cmd_load_ip_fw.fw_size = len(payload)
    c.cmd.cmd_load_ip_fw.fw_type = 49
    try:
        return psp._ring_submit(c).resp.status & 0xFFFFFFFF
    except RuntimeError as e:
        return int(str(e).rsplit(" ", 1)[-1]) & 0xFFFFFFFF


def ent(i, v):
    return struct.pack("<II", i, v)


say("\n######## dense index sweep + wide diff ########")
say("MMIO window %#x..%#x" % (BASE, END))
before = snap()
say("snapshot: %d regs, %d non-trivial" % (len(before), sum(1 for v in before.values() if v not in (0, 0xFFFFFFFF))))

ok = 0
for batch in range(8):
    lo = batch * 63
    tbl = ent(195, 0) + b"".join(ent(lo + i, MAGIC | (lo + i)) for i in range(63))
    st = submit(tbl)
    ok += (st == 0)
    say("  batch %d indices %3d..%3d -> status %#010x" % (batch, lo, lo + 62, st))
    if not alive():
        say("  !! heartbeat stopped"); break

after = snap()
d = {k: (before.get(k), after.get(k)) for k in after if before.get(k) != after.get(k)}
hits = {k: b for k, (a, b) in d.items() if b is not None and (b & 0xFFFF0000) == MAGIC}
say("\naccepted batches: %d/8" % ok)
say("regs changed: %d ; holding magic: %d" % (len(d), len(hits)))
for k, v in list(hits.items())[:16]:
    say("   MMIO dword %#06x = %#010x  <= index %d" % (k, v, v & 0xFFFF))
if not hits:
    say("   changed (first 10): %s" % [(hex(k), hex(a or 0), hex(b or 0)) for k, (a, b) in list(d.items())[:10]])
say("\npsp alive %s / smu alive %s" % (alive(), adev.smu.is_smu_alive()))