aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/probe_asd_direct_invoke.py
blob: 1aa3de2110c630f0b20c924f486a63800e893e67 (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
"""Can the HOST invoke ASD directly, choosing the command id?

DISTINCT-WRITE-HUNT.md excluded ASD cmd 0x301 (sub_D132A8 — a clean
content-controlled write of up to 5120 bytes) purely on reachability: no TA
forwarder builds 0x301. Census across all 10 HDCP builds + DTM + RAS confirms
that, so rollback does not open it.

But that argument only covers TA->ASD cross-process RPC. ASD is loaded through
the ordinary TA path (`tee_cmd_dispatch` routes case 1 and case 4 both to
`tee_load_ta`), its header entry is a trampoline
`MOV R0,R2 ; BLX asd_rpc_dispatch ; SVC 0x75`, and asd_rpc_dispatch takes its
opcode from frame[0]. Existing probes call LOAD_ASD but DISCARD the returned
session_id and only ever invoke HDCP.

This probe keeps ASD's session_id and invokes ASD directly, sweeping ta_cmd_id.

Reference points from the dispatcher's own opcode map:
  0x100-0x10D topology/session   0x160-0x161 link      0x180-0x1BF HDCP1.x
  0x1A0-0x1AA HDCP2.2            0x200-0x20E service   0x300-0x309 SRM/revocation
Known-forwarded (so should behave): 0x300 0x302 0x303 0x307 0x308
Target (never forwarded):           0x301
Controls:                           0x999 (no such opcode), 0x200 (service)
"""
import ctypes, struct, time
from tinygrad import Device
from tinygrad.runtime.autogen.am import am
from tinygrad.helpers import lo32, hi32

d = Device["AMD"]; adev = d.iface.dev_impl; psp = adev.psp
SZ = ctypes.sizeof(am.struct_psp_gfx_cmd_resp)
P = '/Users/user/dev/amd-psp-re/psp_out/'

def stat(): return int.from_bytes(bytes(adev.vram.view(psp.cmd_paddr, SZ)[:])[864:868], "little")
def alloc(n):
    pa = adev.mm.palloc(n, align=0x1000, zero=True); return pa, adev.paddr2mc(pa)

def load(cmd, blob):
    ap, amc = alloc(0x40000); cp, cmc = alloc(0x8000)
    adev.vram.view(ap, len(blob))[:] = blob; adev.gmc.flush_hdp()
    adev.vram.view(psp.cmd_paddr, SZ)[:] = b"\x00" * SZ
    c = am.struct_psp_gfx_cmd_resp(cmd_id=cmd); t = c.cmd.cmd_load_ta
    t.app_phy_addr_hi, t.app_phy_addr_lo = hi32(amc), lo32(amc); t.app_len = len(blob)
    t.cmd_buf_phy_addr_hi, t.cmd_buf_phy_addr_lo = hi32(cmc), lo32(cmc); t.cmd_buf_len = 0x8000
    r = psp._ring_submit(c)
    return r.resp.session_id, cp

asd_sid, asd_shm = load(am.GFX_CMD_ID_LOAD_ASD, open(P + 'asd_full.bin', 'rb').read())
print("ASD loaded: session_id=%#x  shm=%#x" % (asd_sid, asd_shm))
if asd_sid == 0:
    print("!! LOAD_ASD returned session_id 0 -- ASD may not be invokable this way")

def invoke_asd(tc, payload=None):
    b = bytearray(0x8000)
    if payload: b[:len(payload)] = payload
    adev.vram.view(asd_shm, 0x8000)[:] = bytes(b); adev.gmc.flush_hdp()
    adev.vram.view(psp.cmd_paddr, SZ)[:] = b"\x00" * SZ
    ic = am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_INVOKE_CMD)
    iv = ic.cmd.cmd_invoke_cmd
    iv.session_id, iv.ta_cmd_id = asd_sid, tc
    t0 = time.time()
    try:
        psp._ring_submit(ic); ps = 0
    except RuntimeError:
        ps = stat()
    except TimeoutError:
        return "TIMEOUT", None, time.time() - t0
    out = bytes(adev.vram.view(asd_shm, 0x40)[:])
    return ps, out, time.time() - t0

print("\n%-28s %-12s %-12s %s" % ("ta_cmd_id", "psp_status", "shm[0:4]", "dt"))
for tc, note in ((0x300, "SRM read (forwarded)"), (0x301, "*** TARGET ***"),
                 (0x302, "SRM query (forwarded)"), (0x303, "SRM check (fwd)"),
                 (0x200, "service/log"), (0x999, "bogus control")):
    ps, out, dt = invoke_asd(tc)
    first = out[:4].hex() if out else "-"
    pss = ps if isinstance(ps, str) else hex(ps)
    print("  %#06x %-20s %-12s %-12s %.3fs" % (tc, note, pss, first, dt))