aboutsummaryrefslogtreecommitdiffstats
path: root/hw/gcp_fe09_stack_slot_sweep.py
blob: c4d16c8c03e4e02b395a026ca7f62a767a858612 (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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env python3
"""Zero-write every stack slot in GCP's live frames; record survive/fault + fault signature.

Purpose: find control-flow divergence or error paths we have NOT theorised. For each target the
4x STRB window is aimed at P = T, zeroing the whole word T..T+3, with a FRESH TA load per slot so
tests are independent.

Measured frame map (22.0.4.50, hardware-confirmed in Addendum 6):
   stack [0x415000,0x419000)  SP0 = 0x419000   SP at the store = 0x418fa0
   gcp_dispatch  R4 0x418ff0  R5 0x418ff4  R6 0x418ff8  LR 0x418ffc (=0x405cdc, ARM)
   dispatcher    R3 0x418fc8 .. R11 0x418fe8  LR 0x418fec (=0x404053)
   handler FE09  R3 0x418fa0 .. R11 0x418fc0  LR 0x418fc4 (=0x402a3b)

A fault wedges the sOS ring, so the run STOPS at the first fault and prints RESUME=<next index>;
the outer driver power-cycles and restarts there. Non-faulting slots cost nothing.

argv[1] = start index. State is appended to /tmp/slotsweep.log on the box.
"""
import struct, sys, glob, os
sys.path.insert(0,'/root/psp/hw')
from tinygrad import Device
import tinygrad.runtime.autogen.am.am as am
adev=Device["AMD"].iface.dev_impl; psp=adev.psp
PARAMS_VA=0x04013000; BASE=PARAMS_VA+0x418
TA='ta_22-0-4-50_81b082b4'; BUFLEN=0x8000; PAT=0xA5
STORE_LO,STORE_HI=0x402df0,0x402e10
FAULT=[getattr(adev,'mmMP0_SMN_C2PMSG_%d'%n) for n in range(82,89)]
def frame(): return tuple(r.read()&0xFFFFFFFF for r in FAULT)
def alloc(n):
    pa=adev.mm.palloc(n,align=0x1000,zero=True,boot=False); return pa,adev.paddr2mc(pa)
app_pa,app_mc=alloc(0x80000); cp,cmc=alloc(BUFLEN)
blob=open(glob.glob('/root/psp/contall/%s*'%TA)[0],'rb').read()
def load():
    n=(len(blob)+3)&~3
    adev.vram.view(app_pa,n)[:]=blob+b'\x00'*(n-len(blob)); adev.gmc.flush_hdp()
    c=am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_LOAD_TA); t=c.cmd.cmd_load_ta
    t.app_phy_addr_lo,t.app_phy_addr_hi=app_mc&0xFFFFFFFF,app_mc>>32; t.app_len=len(blob)
    t.cmd_buf_phy_addr_lo,t.cmd_buf_phy_addr_hi=cmc&0xFFFFFFFF,cmc>>32; t.cmd_buf_len=BUFLEN
    try:
        r=psp._ring_submit(c); return r.resp.status&0xFFFFFFFF, r.resp.session_id
    except Exception: return -1,None
def unload(sid):
    c=am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_UNLOAD_TA); c.cmd.cmd_unload_ta.session_id=sid
    try: return psp._ring_submit(c).resp.status&0xFFFFFFFF
    except Exception: return -1
def fe09(sid,disp):
    buf=bytearray(bytes([PAT])*BUFLEN)
    struct.pack_into('<I',buf,0,0xFE09); struct.pack_into('<I',buf,0x10,0)
    for o in (0x414,0x818): struct.pack_into('<I',buf,o,0xFFFFFFFB)
    struct.pack_into('<I',buf,0x820,1); struct.pack_into('<I',buf,0x828,disp&0xFFFFFFFF)
    adev.vram.view(cp,BUFLEN)[:]=bytes(buf); adev.gmc.flush_hdp()
    c=am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_INVOKE_CMD)
    iv=c.cmd.cmd_invoke_cmd; iv.session_id,iv.ta_cmd_id=sid,0xFE09
    try: st=psp._ring_submit(c).resp.status&0xFFFFFFFF
    except Exception: st=-1
    adev.gmc.flush_hdp()
    return st, struct.unpack_from('<I',bytes(adev.vram.view(cp,16)[:]),8)[0]

OWN={}
for i in range(10):
    OWN[0x418fa0+4*i]=('handler', ['R3','R4','R5','R6','R7','R8','R9','R10','R11','LR'][i])
for i in range(10):
    OWN[0x418fc8+4*i]=('dispatcher', ['R3','R4','R5','R6','R7','R8','R9','R10','R11','LR'][i])
for i,r in enumerate(['R4','R5','R6','LR']):
    OWN[0x418ff0+4*i]=('gcp_dispatch', r)
TARGETS=[]
for a in sorted(OWN): TARGETS.append((a,'%s %s'%OWN[a]))
for a,lbl in ((0x418f9c,'dead: just below SP'),(0x418f00,'dead: SP-0xa0'),
              (0x418800,'dead: mid stack'),(0x415004,'dead: stack floor+4'),
              (0x415000,'stack floor'),(0x414ffc,'GUARD below stack'),
              (0x419000,'GUARD at stack top'),(0x418ffc+4,'=0x419000 dup')):
    TARGETS.append((a,lbl))

start=int(sys.argv[1]) if len(sys.argv)>1 else 0
f0=frame()
log=open('/tmp/slotsweep.log','a')
print('baseline frame %s  start=%d/%d'%([hex(x) for x in f0],start,len(TARGETS)),flush=True)
for i in range(start,len(TARGETS)):
    T,lbl=TARGETS[i]
    st,sid=load()
    if sid is None:
        print('IDX %d: load failed (%s) -- ring likely wedged'%(i,st),flush=True)
        print('RESUME=%d'%i,flush=True); break
    D=(T-BASE)&0xFFFFFFFF
    st2,ta=fe09(sid,D)
    fr=frame()
    faulted=(st2==-1) or (fr!=f0)
    if not faulted:
        line='IDX %-2d %#08x %-22s D=%#010x SURVIVED ta_status=%#x'%(i,T,lbl,D,ta)
        print(line,flush=True); log.write(line+'\n'); log.flush()
        unload(sid)
        continue
    pc,w3,addr=fr[2],fr[3],fr[4]
    kind='FAULT@STORE' if STORE_LO<=pc<=STORE_HI else ('RET-TO-ZERO' if (pc==0 or addr==0) else 'OTHER')
    line=('IDX %-2d %#08x %-22s D=%#010x FAULT %s pc=%#x w3=%#x addr=%#x  full=%s'
          %(i,T,lbl,D,kind,pc,w3,addr,[hex(x) for x in fr]))
    print(line,flush=True); log.write(line+'\n'); log.flush()
    print('RESUME=%d'%(i+1),flush=True)
    break
else:
    print('SWEEP-COMPLETE',flush=True)