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
|
"""DECISIVE: is the RAS TRIGGER_ERROR range check inverted?
dram_range_query returns:
0xFFFF0001 address IS inside a valid DRAM range
0 address is NOT in any range (also: not-initialised / recovery)
0xFFFF0006 address has bits above bit 43
and ras_dram_range_config does `CBZ R0 -> proceed`, i.e. proceeds on 0.
If that is the real polarity, then:
VALID in-range address -> rejected with 40986
INVALID out-of-range -> ACCEPTED, proceeds to program the UMC inject regs
which is exactly backwards. Test with addresses whose validity we know.
"""
import ctypes,struct,collections
from tinygrad import Device
from tinygrad.runtime.autogen.am import am
from tinygrad.helpers import lo32,hi32
d=Device["AMD"]; adev=d.iface.dev_impl; psp=adev.psp
SZ=ctypes.sizeof(am.struct_psp_gfx_cmd_resp); P='/Users/user/dev/amd-psp-re/psp_out/'; S=0x8000
def stat(): return int.from_bytes(bytes(adev.vram.view(psp.cmd_paddr,SZ)[:])[864:868],"little")
def alloc(n):
pa=adev.mm.palloc(n,align=0x1000,zero=True); return pa,adev.paddr2mc(pa)
def load(cmd,blob):
ap,amc=alloc(0x40000); cp,cmc=alloc(S)
adev.vram.view(ap,len(blob))[:]=blob; adev.gmc.flush_hdp()
adev.vram.view(psp.cmd_paddr,SZ)[:]=b"\x00"*SZ
c=am.struct_psp_gfx_cmd_resp(cmd_id=cmd); t=c.cmd.cmd_load_ta
t.app_phy_addr_hi,t.app_phy_addr_lo=hi32(amc),lo32(amc); t.app_len=len(blob)
t.cmd_buf_phy_addr_hi,t.cmd_buf_phy_addr_lo=hi32(cmc),lo32(cmc); t.cmd_buf_len=S
return psp._ring_submit(c).resp.session_id, cp
def inv(sid,shm,buf,tc):
adev.vram.view(shm,S)[:]=bytes(buf); adev.gmc.flush_hdp()
adev.vram.view(psp.cmd_paddr,SZ)[:]=b"\x00"*SZ
ic=am.struct_psp_gfx_cmd_resp(cmd_id=am.GFX_CMD_ID_INVOKE_CMD); iv=ic.cmd.cmd_invoke_cmd
iv.session_id,iv.ta_cmd_id=sid,tc
try: psp._ring_submit(ic); ps=0
except RuntimeError: ps=stat()
except TimeoutError: return "TIMEOUT",None
return ps, bytes(adev.vram.view(shm,S)[:])
ras,rsh=load(am.GFX_CMD_ID_LOAD_TA, open(P+'ras_full.bin','rb').read())
# a definitely-valid VRAM address we own
mypa,mymc=alloc(0x10000)
print("RAS sid=%s our VRAM buffer: pa=%#x mc=%#x MEMSIZE=%s"%(ras,mypa,mymc,hex(adev.rreg(0xde3))))
def trigger(addr, blk=2, v5=0):
b=bytearray(S); base=0x10
struct.pack_into('<I',b,0,2) # cmd 2 TRIGGER ERROR
struct.pack_into('<I',b,base+0,0) # request type must be 0
struct.pack_into('<I',b,base+4, blk) # v4 block type (2 or 4)
struct.pack_into('<I',b,base+8, 0)
struct.pack_into('<I',b,base+16, addr & 0xffffffff) # injection address lo
struct.pack_into('<I',b,base+20, (addr>>32) & 0xffffffff)
struct.pack_into('<I',b,base+24, v5)
struct.pack_into('<I',b,base+28, 0)
ps,o=inv(ras,rsh,b,2)
return None if o is None else struct.unpack_from('<I',o,8)[0]
NAMES={40986:'40986 REJECTED (0xA01A, range-query non-zero)',
40962:'40962 (0xA002 bad block/param)',40977:'40977 (0xA011 reg-access layer REACHED)',0:'0 OK'}
def show(label, addr, **kw):
r=trigger(addr, **kw)
print(" %-46s addr=%#014x -> %s"%(label,addr,NAMES.get(r,str(r))))
print("\n=== addresses that ARE valid DRAM (ours / low VRAM) ===")
for lbl,a in (("our allocated VRAM buffer (mc)",mymc),
("our allocated VRAM buffer (pa)",mypa),
("low VRAM 0x9a000",0x9a000),
("VRAM 0x1600000",0x1600000)):
for v5 in (0,4):
show(lbl+" v5=%d"%v5, a, v5=v5)
print("\n=== addresses that are NOT valid DRAM ===")
for lbl,a in (("zero",0),("tiny 0x40",0x40),("0xdead000",0xdead000),
("huge but <2^44 0xFFFFFFFF000",0xFFFFFFFF000),
("above bit43 (should be 0xFFFF0006)",0x1FFFFFFFF000)):
for v5 in (0,4):
show(lbl+" v5=%d"%v5, a, v5=v5)
print("\nMEMSIZE=%s sos_alive=%s"%(hex(adev.rreg(0xde3)),psp.is_sos_alive()))
|