aboutsummaryrefslogtreecommitdiffstats
path: root/hw/bl_runtime_probe.py
blob: e15648b12b7455a918b2d23abe989380a3372a43 (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
#!/usr/bin/env python3
"""Is the PSP BOOTLOADER command interface still live after sOS has booted?

The bootloader loads sOS components pre-boot via C2PMSG_35 (component id + status) and _36
(message address >> 20). It is a host->PSP interface completely separate from the ring, and
nothing in this audit has ever touched it.

Observed: `_35` reads `0x80000000` -- the "BL ready" bit -- while sOS is alive and the ring is
running. That is only suggestive: a latched idle bit is not proof the bootloader will accept
a command.

The clean experiment uses an INVALID component id. If the bootloader answers (clears the
ready bit and/or returns a status), the interface is live at runtime -- established without
asking it to load anything real. If `_35` never changes, the bit is idle state and the
interface is closed.

Deliberately NOT used: LOAD_IP_FW, a known wedge with junk payloads, and the DRAM training
ids, which would retrain memory on a running system.

argv[1]: 'invalid' (bogus id, default) | 'zero' | hex id
"""
import sys, time
sys.path.insert(0, '/root/psp/hw')
from tinygrad import Device
adev = Device["AMD"].iface.dev_impl; psp = adev.psp
P = psp.reg_pref
def snap(tag):
    d = {r: adev.reg(f"{P}_{r}").read() for r in (33, 35, 36, 64, 67, 81)}
    print('  %-10s _35=%#-12x _36=%#-10x _64=%#-12x _81=%#-11x _33=%#x'
          % (tag, d[35], d[36], d[64], d[81], d[33]), flush=True)
    return d
mode = sys.argv[1] if len(sys.argv) > 1 else 'invalid'
cid = {'invalid': 0x44000000, 'zero': 0x0}.get(mode, None)
if cid is None: cid = int(mode, 0)
print('bootloader runtime probe: component id %#x' % cid, flush=True)
before = snap('before')
print('  BLREADY_before=%s SOSALIVE=%s'
      % (bool(before[35] & 0x80000000), bool(psp.is_sos_alive())), flush=True)
if not (before[35] & 0x80000000):
    print('  ready bit not set -> nothing to test'); sys.exit(0)
pa = adev.mm.palloc(0x100000, align=0x100000, zero=True, boot=False)
mc = adev.paddr2mc(pa)
adev.gmc.flush_hdp()
adev.reg(f"{P}_36").write(mc >> 20)
adev.reg(f"{P}_35").write(cid)
t0 = time.time(); changed = None
while time.time() - t0 < 5.0:
    v = adev.reg(f"{P}_35").read()
    if v != cid and v != before[35]:
        changed = v; break
    time.sleep(0.05)
after = snap('after')
print('  VERDICT _35=%#x %s' % (after[35],
      'BOOTLOADER RESPONDED - interface live at runtime' if changed is not None
      else 'unchanged or echo - no response'), flush=True)
print('  SOSALIVE_after=%s HB_advancing=%s'
      % (bool(psp.is_sos_alive()), after[81] != before[81]), flush=True)