From e6809ccf7d778a4c2f32d9e27c0ec31a44cbd2ba Mon Sep 17 00:00:00 2001 From: Martin Penkava Date: Sun, 12 Jul 2026 16:54:02 +0200 Subject: [PATCH] runtime: fix boot freeze from overlay-loader init race + discovery gap Two deterministic boot hangs (WWF SmackDown! 2, SLUS-01234): 1. overlay_loader_dispatch()/is_candidate()/call_native() ran during early BIOS kernel init (0xBFC0DB10 memcard polling loop) before overlay_loader_init() built the range-index structures, walking uninitialized lists until a PC=0 trap (froze at cycle 208195 every boot). Guard all three entry points on s_active. 2. Code at 0x800D1F28 (boot-text populated by bulk host transfer that bypassed the RAM write-marking hooks) was neither compiled, dirty, nor overlay -> unknown-dispatch fail at frame 1340. Add a fallback: an address above overlay_region_floor whose word decodes as valid MIPS is marked executable and handed to the dirty-RAM interpreter. Validated 5/5 reproducible boots, 2000+ frame sustained runs. Co-Authored-By: Claude Fable 5 --- runtime/src/dirty_ram_interp.c | 15 ++++++++++++++- runtime/src/overlay_loader.c | 5 ++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/runtime/src/dirty_ram_interp.c b/runtime/src/dirty_ram_interp.c index b52072bf..bfebac42 100644 --- a/runtime/src/dirty_ram_interp.c +++ b/runtime/src/dirty_ram_interp.c @@ -279,6 +279,7 @@ extern void psx_dispatch_call(CPUState* cpu, uint32_t addr, uint32_t return_addr /* Forward decls from memory.c — used to read instruction bytes. */ extern uint8_t *memory_get_ram_ptr(void); +extern void dirty_ram_mark_executable_range(uint32_t phys, uint32_t len); /* MIPS instruction field decoders. */ static inline uint32_t op_field (uint32_t i) { return (i >> 26) & 0x3Fu; } @@ -2210,7 +2211,19 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_ } #define OV_FPLOG_RET1() do { if (_ovfp) overlay_fp_log(addr, _in_regs, cpu, 0); return 1; } while (0) - if (!dirty_ram_is_dirty(phys) && !clean_game_text_miss) return 0; + if (!dirty_ram_is_dirty(phys) && !clean_game_text_miss) { + /* Some games populate post-EXE overlays through bulk host transfers that + * do not pass through the normal RAM write hooks. A real control transfer + * to decodable code above the configured boot-EXE text end is sufficient + * evidence that the page is executable; mark it so local overlay flow can + * remain in the interpreter. Invalid/data targets still fail dispatch. */ + if (phys < (2u * 1024u * 1024u) && phys >= g_overlay_region_floor && + dirty_ram_word_looks_decodable(fetch_word(phys))) { + dirty_ram_mark_executable_range(phys, 4u); + } else { + return 0; + } + } /* Interp-pressure signal for variant-capture automation (step 2.8): * counts dispatches the interpreter actually handles inside a capture diff --git a/runtime/src/overlay_loader.c b/runtime/src/overlay_loader.c index 4c674bae..485049c6 100644 --- a/runtime/src/overlay_loader.c +++ b/runtime/src/overlay_loader.c @@ -2298,6 +2298,7 @@ static int overlay_find_by_range(uint32_t phys) { int overlay_loader_dispatch(CPUState *cpu, uint32_t addr) { uint32_t phys = addr & 0x1FFFFFFFu; + if (!s_active) return 0; int lazy_loaded = 0; retry_candidates: int head = idx_head(phys); @@ -2791,6 +2792,7 @@ static FpEnt s_fp[FP_CAP]; static uint64_t s_fp_seq = 0; int overlay_loader_is_candidate(uint32_t phys) { + if (!s_active) return 0; phys &= 0x1FFFFFFFu; return idx_head(phys) >= 0 || lazy_has_exact_entry(phys); } @@ -3137,7 +3139,8 @@ void overlay_fp_log(uint32_t addr, const uint32_t *in_regs, * leaks (root cause of the dwarf->overworld native blue screen). * Returns 1 iff a native candidate ran. */ int overlay_loader_call_native(CPUState *cpu, uint32_t addr) { - if (!s_native_exec) return 0; /* interp mode: keep the legacy inline path */ + if (!s_active || !s_native_exec) + return 0; /* inactive/interp mode: keep the legacy inline path */ uint32_t phys = addr & 0x1FFFFFFFu; if (idx_head(phys) < 0 && !lazy_has_exact_entry(phys)) return 0; /* neither a registered nor an exact cached entry */