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
|
#!/usr/bin/env python3
"""Does GBR_IH_SET apply the trusted-region check that ring creation does?
Ring creation rejects a base inside the TMR with a distinct status (`0xa`). GBR_IH_SET
(`0x80000`) is the sibling path that points the PSP at a host-supplied interrupt ring; in the
control-space sweep it was inert because no parameters were supplied. It takes its address
from the same `C2PMSG_69/70` pair (and size in `_71`), so the check can be compared directly.
An IH ring is a structure hardware WRITES into, so a missing range check here would be
trusted firmware programming a writer at a host-chosen address -- the same shape as the
unvalidated `fence_addr`, but with the check demonstrably present on the neighbouring path.
Cases, each with the PSP proven healthy first in the same boot:
A control normal address -> baseline status
B address inside the TMR -> rejected like ring-create (0xa) or accepted?
C address 0 -> rejected?
D misaligned address -> rejected, or accepted like the ring base?
"""
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 = 0x18000001
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)
ih, ihmc = alloc(0x10000)
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)
def create():
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()
while time.time() - t0 < 3:
if adev.reg(f"{P}_64").read() & 0x80000000: break
adev.wreg_pair(P, "_69", "_70", ring_mc)
adev.reg(f"{P}_71").write(psp.ring_size)
adev.reg(f"{P}_64").write(am.PSP_RING_TYPE__KM << 16)
time.sleep(0.1)
return (adev.reg(f"{P}_64").read() & 0x8000FFFF) == 0x80000000
def submit():
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
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()
adev.reg(f"{P}_67").write(step)
t0 = time.time()
while time.time() - t0 < 2.0:
adev.gmc.flush_hdp()
v = struct.unpack_from('<I', bytes(adev.vram.view(psp.cmd_paddr + RESP, 4)[:]), 0)[0]
if v != 0xA5A5A5A5: return v
return None
def ih_set(addr, tag):
h0 = adev.reg(f"{P}_81").read()
adev.wreg_pair(P, "_69", "_70", addr)
adev.reg(f"{P}_71").write(0x10000)
adev.reg(f"{P}_64").write(am.GFX_CTRL_CMD_ID_GBR_IH_SET)
time.sleep(0.2)
v = adev.reg(f"{P}_64").read()
alive = adev.reg(f"{P}_81").read() != h0
ok = create() and submit() is not None
print(' %-26s addr=%#-13x _64=%#-12x status=%#-6x hb=%-5s psp_usable_after=%s'
% (tag, addr, v, v & 0xFFFF, alive, ok), flush=True)
return v & 0xFFFF, ok
if not create() or submit() is None:
print('CONTROL FAILED -> void'); sys.exit(2)
print('control passed first in this boot: ring creates, command completes', flush=True)
case = sys.argv[1]
CASES = {'normal': ihmc, 'tmr': tmr_mc + 0x4000, 'zero': 0, 'unalign': ihmc + 1}
ih_set(CASES[case], case)
|