Core: Proper module memory mappings (#4273)

* Early memory regions setup

Needed to enable flexible allocations before mapping the eboot

* Improved accuracy of module mappings

To keep our current logic intact, reserve the full memory space, then perform fixed mappings for trampoline and segments. Segment mappings use type flexible, so they properly count toward flexible usage, while trampoline mappings use type code to bypass this.

* Asserts for failed offset finding

Always check before you leap.

* Hex logging for segment sizes

* Only map game modules to flex mem.

* Revert "Asserts for failed offset finding"

This reverts commit 38083f8cbb.

* Revert "Early memory regions setup"

This reverts commit da1552d4ac.

* Initialize memory regions in MemoryManager constructor

Much simpler approach to enabling the necessary flexible mappings on startup.
This commit is contained in:
Stephen Miller
2026-04-19 04:45:02 -05:00
committed by GitHub
parent 26c5963481
commit 92cd61fbf5
5 changed files with 62 additions and 27 deletions

View File

@@ -55,7 +55,7 @@ static constexpr u64 SystemReservedSize = SYSTEM_RESERVED_MAX - SYSTEM_RESERVED_
static constexpr u64 UserSize = USER_MAX - USER_MIN + 1;
// Required backing file size for mapping physical address space.
static u64 BackingSize = ORBIS_KERNEL_TOTAL_MEM_DEV_PRO;
static u64 BackingSize = ORBIS_KERNEL_TOTAL_MEM_DEV_PRO + ORBIS_KERNEL_FLEXIBLE_MEMORY_SIZE;
#ifdef _WIN32

View File

@@ -12,8 +12,8 @@ constexpr u64 ORBIS_KERNEL_TOTAL_MEM_DEV = 6656_MB;
// TODO: This value needs confirmation
constexpr u64 ORBIS_KERNEL_TOTAL_MEM_DEV_PRO = 7936_MB;
constexpr u64 ORBIS_FLEXIBLE_MEMORY_BASE = 64_MB;
constexpr u64 ORBIS_FLEXIBLE_MEMORY_SIZE = 512_MB;
constexpr u64 ORBIS_KERNEL_FLEXIBLE_MEMORY_BASE = 64_MB;
constexpr u64 ORBIS_KERNEL_FLEXIBLE_MEMORY_SIZE = 512_MB;
namespace Core::Loader {
class SymbolsResolver;

View File

@@ -75,7 +75,7 @@ void Linker::Execute(const std::vector<std::string>& args) {
}
// Configure the direct and flexible memory regions.
u64 fmem_size = ORBIS_FLEXIBLE_MEMORY_SIZE;
u64 fmem_size = ORBIS_KERNEL_FLEXIBLE_MEMORY_SIZE;
bool use_extended_mem1 = true, use_extended_mem2 = true;
const auto* proc_param = GetProcParam();
@@ -88,7 +88,7 @@ void Linker::Execute(const std::vector<std::string>& args) {
if (mem_param.size >=
offsetof(OrbisKernelMemParam, flexible_memory_size) + sizeof(u64*)) {
if (const auto* flexible_size = mem_param.flexible_memory_size) {
fmem_size = *flexible_size + ORBIS_FLEXIBLE_MEMORY_BASE;
fmem_size = *flexible_size + ORBIS_KERNEL_FLEXIBLE_MEMORY_BASE;
}
}
}

View File

@@ -27,6 +27,21 @@ MemoryManager::MemoryManager() {
LOG_INFO(Kernel_Vmm, "{:#x} - {:#x}", region.lower(), region.upper());
}
// Pre-initialize direct backing
auto total_size = ORBIS_KERNEL_TOTAL_MEM_DEV_PRO;
s32 extra_dmem = EmulatorSettings.GetExtraDmemInMBytes();
if (extra_dmem != 0) {
total_size += extra_dmem * 1_MB;
}
total_direct_size = total_size;
dmem_map.clear();
dmem_map.emplace(0, PhysicalMemoryArea{0, total_direct_size});
// Pre-initialize flexible backing
total_flexible_size = ORBIS_KERNEL_FLEXIBLE_MEMORY_SIZE;
fmem_map.clear();
fmem_map.emplace(total_size, PhysicalMemoryArea{total_size, total_flexible_size});
ASSERT_MSG(Libraries::Kernel::sceKernelGetCompiledSdkVersion(&sdk_version) == 0,
"Failed to get compiled SDK version");
}
@@ -35,6 +50,7 @@ MemoryManager::~MemoryManager() = default;
void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1,
bool use_extended_mem2) {
// Calculate actual direct and flexible memory sizes
const bool is_neo = ::Libraries::Kernel::sceKernelIsNeoMode();
auto total_size = is_neo ? ORBIS_KERNEL_TOTAL_MEM_PRO : ORBIS_KERNEL_TOTAL_MEM;
if (EmulatorSettings.IsDevKit()) {
@@ -53,20 +69,19 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
if (!use_extended_mem2 && !is_neo) {
total_size -= 128_MB;
}
total_flexible_size = flexible_size - ORBIS_FLEXIBLE_MEMORY_BASE;
// Update stored totals
total_flexible_size = flexible_size - ORBIS_KERNEL_FLEXIBLE_MEMORY_BASE;
ASSERT_MSG(total_flexible_size >= flexible_usage, "Unable to shrink flexible memory size");
u64 old_direct_size = total_direct_size;
total_direct_size = total_size - flexible_size;
// Insert an area that covers the direct memory physical address block.
// Note that this should never be called after direct memory allocations have been made.
dmem_map.clear();
dmem_map.emplace(0, PhysicalMemoryArea{0, total_direct_size});
// Insert an area that covers the flexible memory physical address block.
// Note that this should never be called after flexible memory allocations have been made.
const auto remaining_physical_space = total_size - total_direct_size;
fmem_map.clear();
fmem_map.emplace(total_direct_size,
PhysicalMemoryArea{total_direct_size, remaining_physical_space});
// Limit direct memory space to match actual limit
auto last_dmem_area = FindDmemArea(total_direct_size);
ASSERT_MSG(last_dmem_area->second.dma_type == PhysicalMemoryType::Free &&
last_dmem_area->second.size >= old_direct_size - total_direct_size,
"Unable to shrink dmem map");
last_dmem_area->second.size -= (old_direct_size - total_direct_size);
LOG_INFO(Kernel_Vmm, "Configured memory regions: flexible size = {:#x}, direct size = {:#x}",
total_flexible_size, total_direct_size);

View File

@@ -110,16 +110,19 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
const u64 base_size = CalculateBaseSize(elf_header, elf_pheader);
aligned_base_size = Common::AlignUp(base_size, BlockAlign);
// Map module segments (and possible TLS trampolines)
// Reserve memory area for module
void** out_addr = reinterpret_cast<void**>(&base_virtual_addr);
memory->MapMemory(out_addr, ModuleLoadBase, aligned_base_size + TrampolineSize,
MemoryProt::CpuReadWrite | MemoryProt::CpuExec, MemoryMapFlags::NoFlags,
VMAType::Code, name);
MemoryProt::NoAccess, MemoryMapFlags::NoFlags, VMAType::Reserved, name);
LOG_INFO(Core_Linker, "Loading module {} to {}", name, fmt::ptr(*out_addr));
#ifdef ARCH_X86_64
// Initialize trampoline generator.
void* trampoline_addr = std::bit_cast<void*>(base_virtual_addr + aligned_base_size);
VAddr trampoline_vaddr = base_virtual_addr + aligned_base_size;
void* trampoline_addr = std::bit_cast<void*>(trampoline_vaddr);
memory->MapMemory(&trampoline_addr, trampoline_vaddr, TrampolineSize,
MemoryProt::CpuReadWrite | MemoryProt::CpuExec, MemoryMapFlags::Fixed,
VMAType::Code, name);
RegisterPatchModule(*out_addr, aligned_base_size, trampoline_addr, TrampolineSize);
#endif
@@ -129,15 +132,32 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
LOG_INFO(Core_Linker, "aligned_base_size ......: {:#018x}", aligned_base_size);
const auto add_segment = [this](const elf_program_header& phdr, bool do_map = true) {
const VAddr segment_addr = base_virtual_addr + phdr.p_vaddr;
const VAddr segment_vaddr = base_virtual_addr + phdr.p_vaddr;
void* segment_addr = std::bit_cast<void*>(segment_vaddr);
const u64 segment_size = GetAlignedSize(phdr);
if (do_map) {
elf.LoadSegment(segment_addr, phdr.p_offset, phdr.p_filesz);
// Convert ELF flags to memory prot.
auto segment_prot = MemoryProt::NoAccess;
if ((phdr.p_flags & PF_READ) != 0) {
segment_prot |= MemoryProt::CpuRead;
}
if ((phdr.p_flags & PF_WRITE) != 0) {
segment_prot |= MemoryProt::CpuWrite;
}
if ((phdr.p_flags & PF_EXEC) != 0) {
segment_prot |= MemoryProt::CpuExec;
}
// Map module segments
const auto memory_type = IsSystemLib() ? VMAType::Code : VMAType::Flexible;
memory->MapMemory(&segment_addr, segment_vaddr, segment_size, segment_prot,
MemoryMapFlags::Fixed, memory_type, name);
elf.LoadSegment(segment_vaddr, phdr.p_offset, phdr.p_filesz);
}
if (info.num_segments < 4) {
auto& segment = info.segments[info.num_segments++];
segment.address = segment_addr;
segment.address = segment_vaddr;
segment.prot = phdr.p_flags;
segment.size = GetAlignedSize(phdr);
segment.size = segment_size;
} else {
LOG_ERROR(Core_Linker, "Attempting to add too many segments!");
}
@@ -159,8 +179,8 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
const auto segment_mode = elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags);
LOG_INFO(Core_Linker, "program header = [{}] type = {}", i, header_type);
LOG_INFO(Core_Linker, "segment_addr ..........: {:#018x}", segment_addr);
LOG_INFO(Core_Linker, "segment_file_size .....: {}", segment_file_size);
LOG_INFO(Core_Linker, "segment_memory_size ...: {}", segment_memory_size);
LOG_INFO(Core_Linker, "segment_file_size .....: {:#018x}", segment_file_size);
LOG_INFO(Core_Linker, "segment_memory_size ...: {:#018x}", segment_memory_size);
LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode);
add_segment(elf_pheader[i]);