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
|
#!/usr/bin/env python3
"""Wrapping PSP ring submit — lifts the ~1008-invoke ceiling in our harness.
tinygrad's `_ring_submit` advances the write pointer monotonically and never wraps it against
`ring_size`, so after 1024 submits it writes past the ring allocation and the PSP stalls
(pass 84). Every fuzz in this audit was therefore capped at ~1000 invokes per boot.
This module monkeypatches a wrapping version onto the live PSP object. Import and call
`install(psp, adev)`; it returns a submit function with identical semantics plus wraparound.
"""
import ctypes
from tinygrad.helpers import wait_cond
import tinygrad.runtime.autogen.am.am as am
def install(psp, adev):
frame_sz = ctypes.sizeof(am.struct_psp_gfx_rb_frame)
step = frame_sz // 4
def submit(cmd):
# The write pointer itself must stay within the ring, not merely the byte offset:
# letting it grow unbounded is what breaks at 1024 submits.
wptr_max = psp.ring_size // 4
prev = adev.reg(f"{psp.reg_pref}_67").read() % wptr_max
byte_off = prev * 4
msg = am.struct_psp_gfx_rb_frame(fence_value=prev + 1,
cmd_buf_addr_lo=adev.paddr2mc(psp.cmd_paddr) & 0xFFFFFFFF,
cmd_buf_addr_hi=adev.paddr2mc(psp.cmd_paddr) >> 32,
fence_addr_lo=adev.paddr2mc(psp.fence_paddr) & 0xFFFFFFFF,
fence_addr_hi=adev.paddr2mc(psp.fence_paddr) >> 32)
adev.vram.view(psp.cmd_paddr, ctypes.sizeof(cmd))[:] = memoryview(cmd).cast('B')
adev.vram.view(psp.ring_paddr + byte_off, ctypes.sizeof(msg))[:] = memoryview(msg).cast('B')
adev.reg(f"{psp.reg_pref}_67").write((prev + step) % wptr_max)
wait_cond(lambda: adev.vram.view(psp.fence_paddr, 4, 'I')[0], value=msg.fence_value,
msg="sOS ring not responding")
return type(cmd).from_buffer(bytearray(adev.vram.view(psp.cmd_paddr, ctypes.sizeof(cmd))[:]))
return submit
|