aboutsummaryrefslogtreecommitdiffstats
path: root/hw/vmid_proof.py
blob: 83567febe6d056011039be631bc3b0879954d70a (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
#!/usr/bin/env python3
"""Decisive: does the frame's `vmid` byte select the MMHUB VM context?

A non-zero vmid makes the PSP stop responding and the fence never land. Two explanations:
  (a) the PSP translates its accesses through VM context `vmid`, which is unconfigured here,
      so every access faults;
  (b) the PSP rejects any non-zero vmid via some check and dies for an unrelated reason.

Distinguish by CONFIGURING context 1 with the same root page table as context 0. Under (a),
vmid=1 then translates identically and the command must SUCCEED. Under (b) it still fails.

  step 1  vmid=1 before configuring context 1   -> expect failure (baseline)
  step 2  configure MMVM_CONTEXT1, flush TLB
  step 3  vmid=1 again                          -> success proves (a)
Control: vmid=0 works at both steps.
"""
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
def alloc(n):
    pa=adev.mm.palloc(n,align=0x1000,zero=True,boot=False); return pa,adev.paddr2mc(pa)
MAGIC=0x7E571234
blob=open(glob.glob('/root/psp/contall/ta_42-0-0-6_1c874c3c*')[0],'rb').read()
ap_,amc=alloc(0x80000); cp,cmc=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()
def mk():
    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=cmc&0xFFFFFFFF,cmc>>32; t.cmd_buf_len=0x4000
    return bytes(memoryview(c).cast('B'))
def go(vmid, tag, wait=1.5):
    adev.vram.view(scratch,4,'I')[0]=0
    cb=mk()
    adev.vram.view(psp.cmd_paddr, len(cb))[:]=cb
    adev.vram.view(psp.cmd_paddr+RESP,4,'I')[0]=0xA5A5A5A5
    adev.gmc.flush_hdp()
    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)
    raw=bytearray(memoryview(f).cast('B')); raw[32]=vmid
    wmax=psp.ring_size//4
    prev=adev.reg(f"{psp.reg_pref}_67").read()%wmax
    adev.vram.view(psp.ring_paddr+prev*4, FRAME)[:]=bytes(raw)
    h0=adev.reg(f"{psp.reg_pref}_81").read()
    adev.reg(f"{psp.reg_pref}_67").write((prev+step)%wmax)
    t0=time.time(); st=None
    while time.time()-t0<wait:
        adev.gmc.flush_hdp()
        v=struct.unpack_from('<I',bytes(adev.vram.view(psp.cmd_paddr+RESP,4)[:]),0)[0]
        if v!=0xA5A5A5A5: st=v; break
    adev.gmc.flush_hdp()
    fence=struct.unpack_from('<I',bytes(adev.vram.view(scratch,4)[:]),0)[0]
    print('  %-34s vmid=%-3d resp=%-9s fence_landed=%-5s hb=%s'%(tag,vmid,
          hex(st) if st is not None else 'TIMEOUT', fence==MAGIC,
          adev.reg(f"{psp.reg_pref}_81").read()!=h0), flush=True)
    return st is not None and fence==MAGIC
# One vmid=1 attempt kills the PSP, so a before/after inside one boot is void: the "after"
# control inherits a dead PSP. Configure context 1 FIRST, then measure once.
CONF = len(sys.argv)>1 and sys.argv[1]=='conf'
if CONF:
    print('configuring MMVM_CONTEXT1 with the SAME root page table as context 0', flush=True)
    for inst in adev.gmc.mm_insts:
        adev.gmc.enable_vm_addressing(adev.mm.root_page_table, "MM", vmid=1, inst=inst)
    adev.gmc.flush_tlb(ip="MM", vmid=1)
else:
    print('context 1 left UNCONFIGURED', flush=True)
ok0 = go(0,'control vmid=0')
ok1 = go(1,'vmid=1')
print('RESULT conf=%s: vmid0=%s vmid1=%s'%(CONF,ok0,ok1), flush=True)