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.
