diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 70786db9..6641373a 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -118,6 +118,13 @@ if(BUILD_TESTING) target_link_options(boot_state_handoff_test PRIVATE -flto -Wl,--gc-sections) target_link_libraries(boot_state_handoff_test PRIVATE ZLIB::ZLIB) add_test(NAME boot_state_handoff_test COMMAND boot_state_handoff_test) + add_executable(boot_state_impl_tag_test tests/test_boot_state_handoff.c) + target_include_directories(boot_state_impl_tag_test PRIVATE include src) + target_compile_definitions(boot_state_impl_tag_test PRIVATE PSX_COSIM=1 PSX_SAVESTATE_IMPL_TAG=0x54474801u) + target_compile_options(boot_state_impl_tag_test PRIVATE -O2 -flto -ffunction-sections -fdata-sections -UNDEBUG) + target_link_options(boot_state_impl_tag_test PRIVATE -flto -Wl,--gc-sections) + target_link_libraries(boot_state_impl_tag_test PRIVATE ZLIB::ZLIB) + add_test(NAME boot_state_impl_tag_test COMMAND boot_state_impl_tag_test) add_executable(static_match_cache_test tests/test_static_match_cache.c src/overlay_loader.c src/crc32.c) diff --git a/runtime/include/boot_state.h b/runtime/include/boot_state.h index 27a94b44..ee25845d 100644 --- a/runtime/include/boot_state.h +++ b/runtime/include/boot_state.h @@ -66,7 +66,7 @@ typedef struct { uint32_t entry_pc; /* game PS-EXE entry PC */ uint32_t codegen_hash; /* PSX_OVERLAY_CODEGEN_HASH (auto-gen by cmake) */ int32_t abi_tag; /* PSX_OVERLAY_ABI_TAG (abi version | flavor<<16) */ - uint32_t codegen_ver; /* PSX_OVERLAY_CODEGEN_VER */ + uint32_t codegen_ver; /* codegen version XOR build PSX_SAVESTATE_IMPL_TAG */ /* ---- layout ---- */ uint32_t section_count; /* number of sections that follow */ uint32_t reserved; /* v8: enhancement-memory layout cookie; vanilla 0 */ diff --git a/runtime/src/boot_state.c b/runtime/src/boot_state.c index c48e929e..08b731ef 100644 --- a/runtime/src/boot_state.c +++ b/runtime/src/boot_state.c @@ -2,6 +2,13 @@ #include "fntrace.h" #include "mod_memory.h" #include "overlay_api.h" /* PSX_OVERLAY_CODEGEN_HASH / _ABI_TAG / _CODEGEN_VER */ + +/* Build-selected HLE may use private execution boundaries. Zero preserves + * the existing snapshot key for titles that do not select an implementation. */ +#ifndef PSX_SAVESTATE_IMPL_TAG +#define PSX_SAVESTATE_IMPL_TAG 0u +#endif +#define BOOT_STATE_IMPLEMENTATION_KEY ((uint32_t)PSX_OVERLAY_CODEGEN_VER ^ (uint32_t)PSX_SAVESTATE_IMPL_TAG) #include "dirty_ram_interp.h" #include "gpu.h" /* gpu_get_vram — CPU-auth mirror under dual-raster */ #include "gpu_render.h" /* gr_vram_transfer_in / gr_vram_transfer_out */ @@ -390,7 +397,7 @@ static int boot_state_save_to(BsOut* o, const CPUState* cpu, h.entry_pc = entry_pc; h.codegen_hash = (uint32_t)PSX_OVERLAY_CODEGEN_HASH; h.abi_tag = (int32_t)PSX_OVERLAY_ABI_TAG; - h.codegen_ver = (uint32_t)PSX_OVERLAY_CODEGEN_VER; + h.codegen_ver = BOOT_STATE_IMPLEMENTATION_KEY; h.section_count = 17 + (psx_mod_memory_snapshot_bytes() ? 1u : 0u); ok = write_header_le(o, &h); @@ -870,9 +877,9 @@ int boot_state_check_buffer(const uint8_t* file, size_t file_len, (int)h.abi_tag, (int)PSX_OVERLAY_ABI_TAG); boot_state_append_reason(reason, reason_cap, part); } - if (h.codegen_ver != (uint32_t)PSX_OVERLAY_CODEGEN_VER) { + if (h.codegen_ver != BOOT_STATE_IMPLEMENTATION_KEY) { snprintf(part, sizeof(part), "codegen_ver=%u(want %u)", - (unsigned)h.codegen_ver, (unsigned)PSX_OVERLAY_CODEGEN_VER); + (unsigned)h.codegen_ver, (unsigned)BOOT_STATE_IMPLEMENTATION_KEY); boot_state_append_reason(reason, reason_cap, part); } diff --git a/runtime/tests/test_boot_state_handoff.c b/runtime/tests/test_boot_state_handoff.c index 74040d65..c6493085 100644 --- a/runtime/tests/test_boot_state_handoff.c +++ b/runtime/tests/test_boot_state_handoff.c @@ -68,6 +68,16 @@ int main(void) { /* Malformed, missing and duplicate phase sections must reject before * changing CPU, RAM or the previously active handoff latch. */ cpu.pc=0xdeadbeef; ram[0x100]=0x55; fntrace_restore_game_started(!started); + /* A different build-selected implementation must reject atomically, + * including when its only difference is an HLE execution boundary. */ + PstR key_reader; uint32_t implementation_key; + pst_r_init(&key_reader, saved + 24, 4); + assert(pst_r_u32(&key_reader, &implementation_key)); + assert(implementation_key == BOOT_STATE_IMPLEMENTATION_KEY); + u32(saved + 24, implementation_key ^ 0x54474801u); + assert(!boot_state_load_buffer(saved,len,123,0x801b0000,&cpu)); + assert(cpu.pc==0xdeadbeef && ram[0x100]==0x55 && fntrace_is_game_started()==!started); + u32(saved + 24, implementation_key); saved[len-4]=2; assert(!boot_state_load_buffer(saved,len,123,0x801b0000,&cpu)); saved[len-4]=started;