aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/probe_tsu_mailbox.py
blob: cb8b5dd482aa9d28ec9a8d80e8d565970b21afbb (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
"""Route B liveness: the TSU flash/update mailbox (MP0 C2PMSG 115/116).

Verified statically:
  tsu_mailbox_task  (0xE1CA98): ORs 0x3C0 into the host-permission word (opens 115/116
                    to the host), registers object 89, sets mbx115 bit31 = ready, then
                    loops on SVC 0x5A waiting for a doorbell -> tsu_mailbox_dispatch.
  tsu_mailbox_dispatch (0xE1C948): command byte = BYTE2 of mbx115; submit = bit31 CLEAR;
                    replies *mbx115 = (uint16)status | 0x80000000; data/arg in mbx116.
                    Unknown command -> status 0xFFFF000A (low16 = 0x000A).

Only READ-ONLY commands are used here:
    0x01 -> returns platform_state[2132]+1 in mbx116
    0x0B -> flash_info_get()            in mbx116
Commands 0x02/0x03 (set flash context) and 0x04 (parse/update) are NOT sent.

Stage 1 submits without ringing the shared doorbell (mbx73), which would wake ~5 tasks.
Stage 2 (--doorbell) adds the doorbell only if stage 1 gets no reply.
"""
import sys, time, struct
from tinygrad import Device

USE_DOORBELL = "--doorbell" in sys.argv

d = Device["AMD"]; adev = d.iface.dev_impl
base = adev.reg("regMP0_SMN_C2PMSG_73").addr[0]
def mb(n): return adev.rreg(base + (n - 73))
def mbw(n, v): adev.wreg(base + (n - 73), v)
def bs(v): return struct.unpack('<I', struct.pack('>I', v))[0]
def alive(): return adev.reg("regMP0_SMN_C2PMSG_81").read() != 0

# guard: only trust mailboxes whose writes actually land
def writable(n):
    o = mb(n); pat = 0x9234567A
    mbw(n, pat); ok = (mb(n) == pat); mbw(n, o)
    return ok

print("sOS alive:", alive())
for n in (115, 116, 73):
    print("  mbx%-4d writable=%-5s  value=%#010x" % (n, writable(n), mb(n)))

def submit(cmd, arg=0, timeout=1.0, doorbell=False):
    mbw(116, arg)
    mbw(115, (cmd & 0xFF) << 16)          # BYTE2 = command, bit31 clear = submit
    if doorbell: mbw(73, 1)
    t0 = time.time()
    while time.time() - t0 < timeout:
        v = mb(115)
        if v & 0x80000000:
            return v, mb(116), time.time() - t0
        time.sleep(0.001)
    return None, mb(116), time.time() - t0

print("\n=== Route B probe (doorbell=%s) ===" % USE_DOORBELL)
print("cmd   reply         status  data(mbx116)  dt      alive")
TESTS = [(0x7F, "unknown -> expect 0x8000000A"),
         (0x01, "read-only status byte"),
         (0x0B, "flash_info_get"),
         (0x7E, "unknown control #2")]
for cmd, label in TESTS:
    if not alive():
        print("  !! sOS dead -- stopping"); break
    v, data, dt = submit(cmd, doorbell=USE_DOORBELL)
    if v is None:
        print("  0x%02X  TIMEOUT                     %#010x   %.3fs  %s   (%s)"
              % (cmd, data, dt, alive(), label))
    else:
        print("  0x%02X  %#010x  %#06x  %#010x    %.3fs  %s   (%s)"
              % (cmd, v, v & 0xFFFF, data, dt, alive(), label))

print("\nsOS alive at end:", alive())
print("final mbx115=%#010x mbx116=%#010x" % (mb(115), mb(116)))