Resolve rest-of-RAM heaps to the main thread's stack base - #216
Open
TheTharin wants to merge 1 commit into
Open
Conversation
The retail kernel ties the heap end to the main thread's stack: SetupHeap with heap_size=-1 resolves to the stack base recorded by SetupThread, and EndOfHeap returns it (Play!'s kernel HLE implements the same rule; ps2sdk's crt0 passes -1 by default). The runtime instead capped every heap at a fixed 0x1F00000, leaving an 832KB dead zone below the default main stack at 0x1FD0000. SetupThread now records the main thread's stack base; SetupHeap resolves heap_size 0/-1 against it and no longer clamps explicit sizes below the RAM size; the old constant remains only as a fallback until SetupThread has run. The async-callback stack floor follows the configured heap limit so runtime-reserved stacks stay above the heap. Games that size a master arena as "all remaining RAM" depend on this. Rogue Galaxy (SCUS-97490) allocates a 0x18B0000-byte arena which failed under the cap; unchecked, the game then memsets 26MB from the null result, wiping its own loaded image. Adds two kernel tests covering the -1 resolution and the pre-SetupThread fallback.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
SetupHeap/EndOfHeapsyscall handlers and the runtime's guest-heap machinery capped the guest heap at a fixed0x01F00000(31MB). On real hardware there is no such ceiling: the kernel resolves a "rest of RAM" heap (heap_size = -1) to the main thread's stack base, which sits at the top of the 32MB.Games commonly link their CRT with
heap_size = -1and then size a master allocation from whateverEndOfHeapreports. With a fixed 31MB cap such games lose memory they own on retail hardware — and the cap was also internally inconsistent:SetupThreadhere already places the default main stack atPS2_RAM_SIZE - stack_size(0x1FD0000for a typical 192KB stack), so there was an 832KB dead zone between the heap ceiling and the stack that neither could use.What the retail kernel does
SetupThread(gp, stack, stack_size, ...)— withstack = -1, the stack is placed at the top of RAM; the kernel records the stack base in the thread state.SetupHeap(heap_base, heap_size)— withheap_size = -1, the heap end resolves to that recorded stack base. An explicit size resolves toheap_base + heap_size, unclamped.EndOfHeap()— returns the resolved heap end.References:
Source/ee/PS2OS.cpp:SetupHeap(base, -1)and a master allocation that only fits if the heap runs to the stack. It boots on real hardware and on PCSX2 — which performs no kernel HLE at all (it requires a BIOS dump and executes the original kernel's MIPS code), so its behavior is the retail kernel's. A fixed 31MB ceiling would make this retail disc unbootable on a real PS2.crt0.c(reimplementation of the standard SCE startup) callsSetupHeap(&_end, (int)&_heap_size)with_heap_sizeconventionally-1, i.e. "rest of RAM up to the stack" is the default contract for real software.Note on the exact resolution point: ps2tek describes InitHeap(-1) as resolving to "the thread's stack pointer" (for stack=-1 that is
end of RDRAM - stack_size), while Play! resolves to the stack base below a 4KB top pad. This runtime'sSetupThreadalready computes the ps2tek value (PS2_RAM_SIZE - stack_size), so this PR records and reuses exactly that — consistent with ps2tek's wording and conservative (never above the stack) in the explicit-stack case.Reproducible failure case: Rogue Galaxy (SCUS-97490)
The game's CRT calls
SetupHeap(0x528200, -1)with a 192KB main stack. Early in boot,GameMainsizes a master arena as "total budget minus two pools" =0x18B0000bytes (25.9MB) and allocates it withmemalign— at that point the heap break is already at~0x6B9230, so the heap must reach0x1F69230. Under the 31MB cap the allocation fails by ~430KB; the game does not check the result, stores the null pointer into its arena descriptor, and its arena-clear loop memsets 26MB starting at guest address 0 — wiping the game's own loaded image (the recompiled code keeps running, so the failure shows up much later as an infinitesceCdSearchFile("")spin over a zeroed path table).With this change the allocation succeeds and the game proceeds to load its first overlay (
BIN/TITLE.BIN).Implementation
Following Play!'s shape (record the stack base, resolve
-1against it):SetupThreadnow records the main thread's stack base in the runtime (PS2Runtime::setGuestMainStackBase). It already computed the value; this just stores it.SetupHeappassesheapLimit = 0("rest of RAM") toconfigureGuestHeapforheap_size0/-1, andheap_base + heap_size(clamped only toPS2_RAM_SIZE) for explicit sizes.configureGuestHeap's limit resolution (clampGuestHeapLimit) treats 0 as "resolve to the recorded stack base"; beforeSetupThreadhas run it falls back to the old bounded default (kGuestHeapFallbackLimit = 0x01F00000, now used only as that fallback).EndOfHeapreturnsguestHeapLimit()as before — which now reports the resolved value.configureGuestHeap, so runtime-reserved callback stacks always stay above the heap.Behavior for explicit heap sizes and for the pre-
SetupThreadwindow is unchanged.Tests
Two new cases in
ps2xTest/src/ps2_runtime_kernel_tests.cpp:SetupThread(stack=-1, size=0x30000)thenSetupHeap(base, -1):EndOfHeapmust returnPS2_RAM_SIZE - 0x30000, and a 25.9MB master arena (Rogue Galaxy's exact allocation) must succeed.EndOfHeapreports the bounded fallback.The full suite passes with this change (427/427 on the final run). During testing, one unrelated case — "VU0 macro mappings cover all S1/S2 enums" — failed intermittently both on unmodified
mainand with this change applied, so it appears flaky independently of this PR.Verified on a real game as well: with this change Rogue Galaxy (SCUS-97490) gets its 25.9MB arena (
memalign -> 0x6B9230, arena descriptor showing base0x6B9230/ count0x18B000quadwords) and boots past the point where it previously destroyed itself, proceeding to loadBIN/TITLE.BIN.