aboutsummaryrefslogtreecommitdiffstats

Driving the RAS TA on real silicon, and what it settles

The emulator cannot open RAS's init gate: SYS_DRV RPC opcode 0x000d fails a capacity check because the context at 0xE575B8 is zero (see harness/sysdrv_bridge.py). A bounded search found nothing that populates it -- not init, not any of RAS's RPCs, no low opcode, none of 36 sampled high opcodes, and not a full full_system.py boot. The open question was whether that is a missing-state gap in our emulation or something the firmware genuinely never does.

That question is not answerable inside the emulator. It is answerable on a card.

Tooling: a LOAD_TA path that did not exist

tinygrad has no TA loading support, so psp_out/load_ta.py builds one on top of AM_PSP._ring_submit: allocate VRAM for the signed TA container and a shared command buffer, fill cmd_load_ta with MC addresses, submit GFX_CMD_ID_LOAD_TA to get a session id, then GFX_CMD_ID_INVOKE_CMD per command, and GFX_CMD_ID_UNLOAD_TA at the end. PSP heartbeat is checked before and after every submit.

The TA driven is the card's own signed RAS 27.0.2.5 container, taken from psp_13_0_0_ta.bin -- the same TA whose body is byte-identical to our ras2.elf, so this is the exact code the emulator runs.

The control that saved the result

The first run reported status=0 for commands 1, 2, 3 and 4 and looked like a clean sweep of successes. It was not. resp.status is ring delivery only: feeding it 0xdead and 0x7fff also returned status=0 with byte-identical output. Reporting that would have been a fabricated "all RAS commands succeed on hardware".

The TA's own verdict lives in the shared buffer, struct ta_ras_shared_memory:

+0x00 cmd_id   +0x04 resp_id   +0x08 ras_status   +0x0c if_version

and the command id goes in the buffer, not only in the ring command -- the same shape as the HDCP invoke ABI decoded earlier in this tree. With that fixed, valid and invalid commands separate cleanly.

The RAS command surface on this card

command ring status ras_status
1 0x0 0x0 -- success
2 0x0 0xa002
3 0x0 0xa010
4 0x0 0xa010
5 .. 0x10 0xFFFF000A (rejected at the ring) --

So RAS exposes exactly four commands, 1 through 4; everything above is refused before reaching the TA. Command 1 completes successfully, 2 and 3/4 return TA-level errors in RAS's 0xa0XX family. (3 and 4 returning "not available" on a consumer card with no ECC memory is unsurprising, but that reading is an interpretation, not something measured here.)

What this settles

RAS initialises on real hardware. Command 1 returns ras_status = 0, which means the TA processed it rather than short-circuiting on its init gate. RAS gates every command on the byte at 0x4060E0, written only inside TA_OpenSessionEntryPoint on a path that bottoms out in the SYS_DRV RPC chain including opcode 0x000d. For command 1 to succeed, that chain must have succeeded, so the SYS_DRV context at 0xE575B8 is populated on a real boot.

The emulator blocker is therefore a missing-state gap in our emulation, not a firmware defect -- which was always the suspicion, and is now evidence. The harness note was already careful to say "uninitialised SYS_DRV state, NOT a defect"; that call is now confirmed from outside the emulator.

What this does NOT settle

It does not reveal what populates 0xE575B8. That is PSP-internal state on a path the host cannot observe -- the host speaks GFX ring commands, and the capacity is set somewhere inside SYS_DRV. Hardware confirms the destination; finding the writer remains an emulator-side problem.

It also does not make RAS command coverage reportable in the emulator. What it does provide is an oracle: once the gate opens, the emulator must reproduce 1 -> 0x0, 2 -> 0xa002, 3/4 -> 0xa010, and reject 5+ at the ring. Any emulated RAS result that disagrees with that table is wrong.

Housekeeping

LOAD_TA allocates a fresh session id on every call (observed climbing 0, 1, 2, ... across runs), so sessions are a finite resource that leaks if not released. load_ta.py now issues UNLOAD_TA before exiting. The card was verified healthy after every run: heartbeat advancing, LTSSM 0x78 (L0, link up). No ATX cycle was needed at any point.