aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/df_cfg_probe.py
blob: 6c75af448940bf2cce27fac71a12d89e2a763bf5 (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
#!/usr/bin/env python3
"""Does the DF FabricIndirectConfigAccess port work from the host? READ ONLY.

NV22 map:
    DF.FabricConfigAccessControl          SMN 0x1D040
    DF.FabricIndirectConfigAccessAddress  SMN 0x1D050
    DF.FabricIndirectConfigAccessDataLo   SMN 0x1D080
    DF.FabricIndirectConfigAccessDataHi   SMN 0x1D084

This is an index/data port into the Data Fabric's CONFIG space, which is where
address-map routing (DramBase/DramLimit style) lives. If it is host-readable we
can enumerate how the fabric routes physical addresses -- including, possibly,
how the PSP's TMR carve-out is expressed.

Nothing here writes a DF config register. The only writes are to the ADDRESS
half of the port, which is how a read is performed -- the same pattern the
driver uses for SMN itself.

Safety: the four registers are checked for decode FIRST. If the port does not
decode, nothing else is attempted.
"""
import struct, time
from tinygrad import Device

adev = Device["AMD"].iface.dev_impl
idx = adev.reg("regBIF_BX0_PCIE_INDEX2")
dat = adev.reg("regBIF_BX0_PCIE_DATA2")
hb = adev.reg("regMP0_SMN_C2PMSG_81")


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


def bswap(x):
    return struct.unpack("<I", struct.pack(">I", x & 0xFFFFFFFF))[0]


def rd(a):
    idx.write(a & 0xFFFFFFFF)
    return dat.read()


def wr(a, v):
    idx.write(a & 0xFFFFFFFF)
    dat.write(v & 0xFFFFFFFF)


def cls(a):
    v = rd(a)
    return v, ("MIRROR" if v == bswap(a) else "all-ones" if v == 0xFFFFFFFF else "DECODED")


CTL, ADDR, DLO, DHI = 0x1D040, 0x1D050, 0x1D080, 0x1D084

print("control C2PMSG_58 = %#x (expect 0x310036)" % rd(0x581E8))
print("psp alive: %s\n" % alive())

print("%-38s %-12s %s" % ("register", "value", "verdict"))
ok = True
for a, nm in ((CTL, "DF.FabricConfigAccessControl"),
              (ADDR, "DF.FabricIndirectConfigAccessAddress"),
              (DLO, "DF.FabricIndirectConfigAccessDataLo"),
              (DHI, "DF.FabricIndirectConfigAccessDataHi")):
    v, k = cls(a)
    ok &= (k == "DECODED")
    print("  %-36s %#010x %s" % (nm, v, k))

if not ok:
    print("\nport does not decode -- stopping, nothing further attempted")
    raise SystemExit(0)

print("\nport decodes. Reading DF config space through it (address-half writes only).")
# DF config: instance/function/offset packing varies; sweep a small offset range
# on instance 0 and look for non-trivial, non-repeating data.
seen = {}
for off in range(0, 0x100, 4):
    wr(ADDR, off)
    lo = rd(DLO)
    seen[off] = lo
print("  read %d offsets; distinct values: %d" % (len(seen), len(set(seen.values()))))
for off in sorted(seen)[:16]:
    print("     cfg offset %#05x = %#010x" % (off, seen[off]))
print("\npsp alive: %s" % alive())