#!/usr/bin/env python3 """Does the PSP validate the ring base and size it is given at ring creation? Last host->PSP surface: the ring is created by writing base (C2PMSG_69/70) and size (_71) and a create command to _64, whose low 16 bits are the PSP's own status. So the firmware reports acceptance directly -- no inference needed. A zero or huge size is the interesting case: the index bound demonstrated in pass 136 has to be derived from this size, so a bogus size is the way to remove that bound. A base inside the trusted region asks whether the PSP will fetch its own commands from protected memory. Each case: destroy the ring, program the values, create, read the status. If creation is accepted, submit one real command and see whether it executes -- an accepted-but-bogus ring that still runs commands is the dangerous outcome. argv[1] selects the case so one wedge cannot contaminate the others. """ import ctypes, glob, struct, sys, time 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 RESP=864; FRAME=ctypes.sizeof(am.struct_psp_gfx_rb_frame); step=FRAME//4 P=psp.reg_pref def alloc(n): pa=adev.mm.palloc(n,align=0x1000,zero=True,boot=False); return pa,adev.paddr2mc(pa) MAGIC=0x71000001 blob=open(glob.glob('/root/psp/contall/ta_42-0-0-6_1c874c3c*')[0],'rb').read() ap_,amc=alloc(0x80000); cbuf,cbmc=alloc(0x8000); scratch,smc=alloc(0x1000) n=(len(blob)+3)&~3 adev.vram.view(ap_,n)[:]=blob+b'\x00'*(n-len(blob)); adev.gmc.flush_hdp() ring_mc=adev.paddr2mc(psp.ring_paddr); tmr_mc=adev.paddr2mc(psp.tmr_paddr) CASES={ 'control': (ring_mc, psp.ring_size), 'size0': (ring_mc, 0), 'size1': (ring_mc, 1), 'sizemax': (ring_mc, 0xFFFFFFFF), 'size4g': (ring_mc, 0x80000000), 'base_tmr':(tmr_mc, psp.ring_size), 'base0': (0, psp.ring_size), 'unalign': (ring_mc+1, psp.ring_size), } case=sys.argv[1]; base,size=CASES[case] print('case=%s base=%#x size=%#x (real base %#x size %#x)'%(case,base,size,ring_mc,psp.ring_size), flush=True) if adev.reg(f"{P}_71").read()!=0: adev.reg(f"{P}_64").write(am.GFX_CTRL_CMD_ID_DESTROY_RINGS); time.sleep(0.05) t0=time.time(); ready=False while time.time()-t0<3: if adev.reg(f"{P}_64").read() & 0x80000000: ready=True; break print(' sOS ready before create: %s'%ready, flush=True) adev.wreg_pair(P, "_69", "_70", base) adev.reg(f"{P}_71").write(size) adev.reg(f"{P}_64").write(am.PSP_RING_TYPE__KM << 16) time.sleep(0.1) st64=adev.reg(f"{P}_64").read() status=st64 & 0xFFFF print(' create: _64=%#-12x status=%#-8x -> %s'%(st64,status, 'ACCEPTED' if (st64 & 0x8000FFFF)==0x80000000 else 'rejected/not-ready'), flush=True) if (st64 & 0x8000FFFF)!=0x80000000: print(' RESULT %s: creation refused, ring unchanged'%case, flush=True); sys.exit(0) # accepted -- does a real command execute through this ring? 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=amc&0xFFFFFFFF,amc>>32; t.app_len=len(blob) t.cmd_buf_phy_addr_lo,t.cmd_buf_phy_addr_hi=cbmc&0xFFFFFFFF,cbmc>>32; t.cmd_buf_len=0x4000 adev.vram.view(psp.cmd_paddr, ctypes.sizeof(c))[:]=memoryview(c).cast('B') adev.vram.view(psp.cmd_paddr+RESP,4,'I')[0]=0xA5A5A5A5 adev.vram.view(scratch,4,'I')[0]=0 f=am.struct_psp_gfx_rb_frame(fence_value=MAGIC, cmd_buf_addr_lo=adev.paddr2mc(psp.cmd_paddr)&0xFFFFFFFF, cmd_buf_addr_hi=adev.paddr2mc(psp.cmd_paddr)>>32, fence_addr_lo=smc&0xFFFFFFFF, fence_addr_hi=smc>>32) adev.vram.view(psp.ring_paddr, FRAME)[:]=bytes(memoryview(f).cast('B')) adev.gmc.flush_hdp() h0=adev.reg(f"{P}_81").read() adev.reg(f"{P}_67").write(step) t0=time.time(); rs=None while time.time()-t0<2.0: adev.gmc.flush_hdp() v=struct.unpack_from('