Find4GBBase found 256 MB of free address space for the views and then
unmapped it, so the gaps between the views went back to the system. On a
32-bit Android device, a running game had malloc heaps between VRAM and RAM
and GPU driver buffers just past RAM, and masked guest addresses (bits 30-31
cleared) reach a whole GB above base, mostly heap. A stray guest access
landed in those silently instead of faulting, and another thread could even
map something where a view was about to go, which the view's MAP_FIXED then
replaced. That fits the 32-bit-only crashes deep in system libraries and in
our own heap structures.
Keep the range reserved as PROT_NONE and map the views over it, putting the
reservation back when a view is released. Mask out bits 28-29 too on 32-bit
(no valid address has them), so accesses stay inside the reservation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The ME sees the main CPU's RAM, but has its own eDRAM at 0 (with
0x80000000 and 0xA0000000 mirrors) where the main CPU has its scratchpad,
its own memory where the main CPU has VRAM, and both CPUs share the boot
page at 0xBFC00000.
- The ME's own memory is views in the main arena flagged MV_ME_ONLY, which
only Memory::CreateMESpace() maps, into a second reservation along with
the shared views at their same arena positions. It gets its own block
shadow.
- The eDRAM (MV_EDRAM) is the ME's half of the eDRAM chip, the GE's VRAM
being the other: 2MB on the PSP-1000 (whose resident ME image says so),
4MB on the others (from mcidclan's libmecore, not hardware-checked).
- At VRAM's addresses, through 0x44000000 and 0xA4000000 alike, the ME
doesn't see VRAM but memory of its own (pspautotest me/vram, on a
PSP-1000): where homebrew keeps the VME's buffers. Its size isn't known;
2MB with VRAM's mirrors here, and no depth layout on 16-bit accesses.
- 0xA0000000 reaches physical memory uncached, so RAM is mapped at
0xA8000000 too.
- The boot page is a view too, which the main CPU reaches through a new
MMIO device (in savestates from MMIO section version 2). The ME runs
code from it, uncached, so the main CPU's writes invalidate code there.
On masked platforms it isn't mapped, and the device uses a plain
buffer.
- AddressSpace has IsValidAddress etc, with the ME's rules when edramSize
is set, and the interpreters and the IR interpreter check and fetch
instructions through their state's space. The IR compiler's checks still
use the main CPU's, until the ME gets a JIT.
- On Darwin, MemArena can hold several view reservations.
Masked platforms (iOS, 32-bit) don't get an ME space yet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On iOS, the base search stepped up from 0x100000000 in 8MB increments,
right through the app's binary and early heap: about 100 failed
vm_remaps (each logged twice) every time a game started. On macOS,
Find4GBBase freed its probe allocation before the views were mapped.
Now the kernel picks a free range of exactly the span the views need,
which stays reserved and inaccessible, and the views are mapped over it
with VM_FLAGS_OVERWRITE. One try, no race, and the gaps between views
fault. Unmasked (macOS), ask for 4GB alignment first, which lets the
ARM64 JIT pointerify. The old search remains as a fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>