mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
A TLB miss on an inline load or store leaves the arm64 EE rec at exception level in user code. Eight cases, all disabled, because the rec does not do this yet and the route to making it is staged: x86's behaviour first as a floor, then the divert built back up with these dropping their prefix one at a time. Found from the other end. `3D Pinball Space Cadet (PS2) (3.0) (RA)` hangs after "Parsing complete. Finalizing...", and the visible fault is a thread id of 0xff966c22 arriving at a caller whose syscall returned 1. That is strlen's `subu v0,v0,a0` on the correct v0, reached because the kernel's syscall epilogue eret'ed into the middle of strlen: 131072 of the run's next 162766 exceptions were taken with EXL already set, so none of them updated EPC. Upstream of all of it is one swallowed miss on a strlen(NULL). Two working hypotheses died on the way — that the pinned-GPR cache lost v0, and that the call-ret shadow stack mispopped — both refuted by reading the state at the moment of damage rather than by reasoning about the emitters. The tests are the chain in four instructions, plus the load and store halves of both inline emitter pairs, plus the flush the divert needs. Three separate defects fall out of the one missing poll. The block runs past the faulting load. EPC names the instruction after it, because cpuTlbMiss skips its `pc -= 4` for the rec while the rec's own cursor is already one instruction ahead outside a delay slot — the delay-slot case comes out right only because two errors cancel. And Cause is whatever exception came last, describing a different instruction than EPC does. Two findings shape the fix rather than the tests, so they are recorded here. Reaching the vector with guest state intact costs one writeback, not a general flush: iFlushCall(FLUSH_VTLB) already precedes every inline access and frees the caller-saved hosts, leaving only x28, the allocator's single callee-saved host. A boot with fastmem off puts a live dirty guest GPR there at 489 sites and nothing else anywhere. The fastmem backpatch thunk is the part with no clean answer. It is generated at fault time and cannot name the live guest values of the block around it, so it cannot divert. A census of the 12411 fastmem sites emitted during that boot says how much per-site state a precise one would need: 9355 have nothing live and dirty, 2480 have between one and seven GPRs, and 576 involve the NEON file. Lesson, from a guard test that was written wrong first. Three dirty registers before the faulting load is not enough to make the allocator reach x28, so that test passed with the writeback deleted — it guarded nothing. It takes sixteen live guest values at once. A test that guards a writeback has to create the pressure that puts something in the register the writeback exists for, and the way to find out is to delete the code and watch.