From 28cbb3a96772e306db075a82f1c095f2e4239628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 13 Aug 2026 10:50:44 +0200 Subject: [PATCH] Add the scratchpad's missing kernel-mode mirrors, fix a mask bug hiding them The scratchpad (PSP's repurposed-cache scratch RAM, 0x00010000+) was only mirrored for user-mode access (cached 0x00010000, uncached 0x40010000) - kernel-mode code sees it at 0x80010000/0xC0010000 (the kernel bit, 0x80000000, is independent of and combinable with the uncached bit, 0x40000000 - not "the uncached bit" as an earlier doc note in this branch mistakenly called it). Missing entirely from MemMap.cpp's views[] table, causing a real SIGSEGV the first time kernel-mode code (flash0:/reboot.bin) touched it. Adding the two missing views wasn't sufficient: the scratchpad range check is duplicated eight times (IsValidAddress/IsValid2AlignedAddress/ IsValid4AlignedAddress/MaxSizeAtAddress in MemMap.h, and four more in MemMapFunctions.cpp), and all eight used a mask (0xBFFFC000) that cleared the uncached bit but kept the kernel bit, rejecting 0x80010000 as invalid before ever reaching the now-mapped memory. Fixed all eight to 0x3FFFC000, matching Memory::MEMVIEW32_MASK (which the JIT backends already used correctly for the equivalent runtime check). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9 --- Core/MemMap.cpp | 8 ++++++++ Core/MemMap.h | 8 ++++---- Core/MemMapFunctions.cpp | 12 +++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 815b4c1f0e..aecb6aeb8f 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -53,6 +53,8 @@ MemArena g_arena; u8 *m_pNullPage; u8 *m_pPhysicalScratchPad; u8 *m_pUncachedScratchPad; +u8 *m_pKernelScratchPad; +u8 *m_pUncachedKernelScratchPad; // 64-bit: Pointers to high-mem mirrors // 32-bit: Same as above u8 *m_pPhysicalRAM[3]; @@ -92,6 +94,12 @@ static MemoryView views[] = { {&m_pNullPage, 0x00000000, 0x00010000, MV_NULL_PAGE}, // Null page, usually not enabled. Only used for working around some race condition bugs. {&m_pPhysicalScratchPad, 0x00010000, SCRATCHPAD_SIZE, 0}, {&m_pUncachedScratchPad, 0x40010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS}, + // Kernel-mode code (e.g. flash0:/reboot.bin) sees the scratchpad through these mirrors - + // same two address bits as RAM below (0x80000000 = kernel, 0x40000000 = uncached, + // independently combinable), just missing here until this was noticed via a real SIGSEGV + // writing 0x80010000 (see docs/VSHBootInvestigation.md). + {&m_pKernelScratchPad, 0x80010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS | MV_KERNEL}, + {&m_pUncachedKernelScratchPad,0xC0010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS | MV_KERNEL}, {&m_pPhysicalVRAM[0], 0x04000000, 0x00200000, 0}, {&m_pPhysicalVRAM[1], 0x04200000, 0x00200000, MV_MIRROR_PREVIOUS}, {&m_pPhysicalVRAM[2], 0x04400000, 0x00200000, MV_MIRROR_PREVIOUS}, diff --git a/Core/MemMap.h b/Core/MemMap.h index 18738ac6cb..cf554cc16f 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -305,7 +305,7 @@ inline bool IsValidAddress(const u32 address) { return true; } else if ((address & 0x3F800000) == 0x04000000) { return address < 0x80000000; // Let's disallow kernel-flagged VRAM. We don't have it mapped and I am not sure if it's accessible. - } else if ((address & 0xBFFFC000) == 0x00010000) { + } else if ((address & 0x3FFFC000) == 0x00010000) { return true; } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { return true; @@ -319,7 +319,7 @@ inline bool IsValid2AlignedAddress(const u32 address) { return true; } else if ((address & 0x3F800001) == 0x04000000) { return address < 0x80000000; // Let's disallow kernel-flagged VRAM. We don't have it mapped and I am not sure if it's accessible. - } else if ((address & 0xBFFFC001) == 0x00010000) { + } else if ((address & 0x3FFFC001) == 0x00010000) { return true; } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { return (address & 1) == 0; @@ -333,7 +333,7 @@ inline bool IsValid4AlignedAddress(const u32 address) { return true; } else if ((address & 0x3F800003) == 0x04000000) { return address < 0x80000000; // Let's disallow kernel-flagged VRAM. We don't have it mapped and I am not sure if it's accessible. - } else if ((address & 0xBFFFC003) == 0x00010000) { + } else if ((address & 0x3FFFC003) == 0x00010000) { return true; } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { return (address & 3) == 0; @@ -351,7 +351,7 @@ inline u32 MaxSizeAtAddress(const u32 address) { } else { return 0x04800000 - (address & 0x3FFFFFFF); } - } else if ((address & 0xBFFFC000) == 0x00010000) { + } else if ((address & 0x3FFFC000) == 0x00010000) { return 0x00014000 - (address & 0x3FFFFFFF); } else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { return 0x08000000 + g_MemorySize - (address & 0x3FFFFFFF); diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index 61301affae..ad10fe7e35 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -30,7 +30,7 @@ namespace Memory { u8 *GetPointerWriteOrException(const u32 address) { if ((address & 0x3E000000) == 0x08000000 || // RAM (address & 0xBF800000) == 0x04000000 || // VRAM - (address & 0xBFFFC000) == 0x00010000 || // Scratchpad + (address & 0x3FFFC000) == 0x00010000 || // Scratchpad ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize)) { // More RAM (remasters, etc.) return GetPointerWriteUnchecked(address); } else { @@ -43,7 +43,7 @@ u8 *GetPointerWriteOrException(const u32 address) { const u8 *GetPointerOrException(const u32 address) { if ((address & 0x3E000000) == 0x08000000 || // RAM (address & 0xBF800000) == 0x04000000 || // VRAM - (address & 0xBFFFC000) == 0x00010000 || // Scratchpad + (address & 0x3FFFC000) == 0x00010000 || // Scratchpad ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize)) { // More RAM (remasters, etc.) return GetPointerUnchecked(address); } else { @@ -89,7 +89,7 @@ template inline void ReadMemoryOrException(T &var, const u32 address) { if ((address & 0x3E000000) == 0x08000000 || // RAM (address & 0xBF800000) == 0x04000000 || // VRAM - (address & 0xBFFFC000) == 0x00010000 || // Scratchpad + (address & 0x3FFFC000) == 0x00010000 || // Scratchpad ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize)) { // More RAM (remasters, etc.) var = *((const T*)GetPointerUnchecked(address)); } else { @@ -102,7 +102,7 @@ template inline void WriteMemoryOrException(u32 address, const T data) { if ((address & 0x3E000000) == 0x08000000 || // RAM (address & 0xBF800000) == 0x04000000 || // VRAM - (address & 0xBFFFC000) == 0x00010000 || // Scratchpad + (address & 0x3FFFC000) == 0x00010000 || // Scratchpad ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize)) { // More RAM (remasters, etc.) *(T*)GetPointerUnchecked(address) = data; } else { @@ -121,7 +121,9 @@ bool IsRAMAddress(const u32 address) { } bool IsScratchpadAddress(const u32 address) { - return (address & 0xBFFFC000) == 0x00010000; + // Ignore both the kernel bit (0x80000000) and the uncached bit (0x40000000) - the + // scratchpad is mirrored across all four combinations, same as RAM (see MemMap.cpp). + return (address & ~(0xC0000000 | (SCRATCHPAD_SIZE - 1))) == 0x00010000; } u8 ReadOrException_U8(const u32 address) {