From f1bfc05fd26e0fd855ed991f0dac43d1e99d671b Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Fri, 17 Mar 2023 01:08:52 +0200 Subject: [PATCH] [Kernel] Better OS memory support. Gets Pokemon X, Pilotwings, and more further into boot --- include/memory.hpp | 33 ++++++++++++++++++------ src/core/memory.cpp | 61 ++++++++++++++++++++++----------------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/include/memory.hpp b/include/memory.hpp index dceb0859..ddc2da35 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -135,7 +135,8 @@ class Memory { public: u16 kernelVersion = 0; - u32 usedUserMemory = 0; + u32 usedUserMemory = 0_MB; // How much of the APPLICATION FCRAM range is used (allocated to the appcore) + u32 usedSystemMemory = 0_MB; // Similar for the SYSTEM range (reserved for the syscore) Memory(u64& cpuTicks); void reset(); @@ -158,6 +159,22 @@ public: u32 getLinearHeapVaddr(); u8* getFCRAM() { return fcram; } + // Total amount of OS-only FCRAM available (Can vary depending on how much FCRAM the app requests via the cart exheader) + u32 totalSysFCRAM() { + return FCRAM_SIZE - FCRAM_APPLICATION_SIZE; + } + + // Amount of OS-only FCRAM currently available + u32 remainingSysFCRAM() { + return totalSysFCRAM() - usedSystemMemory; + } + + // Physical FCRAM index to the start of OS FCRAM + // We allocate the first part of physical FCRAM for the application, and the rest to the OS. So the index for the OS = application ram size + u32 sysFCRAMIndex() { + return FCRAM_APPLICATION_SIZE; + } + enum class BatteryLevel { Empty = 0, AlmostEmpty, OneBar, TwoBars, ThreeBars, FourBars }; @@ -185,18 +202,20 @@ public: // Allocate "size" bytes of RAM starting from FCRAM index "paddr" (We pick it ourself if paddr == 0) // And map them to virtual address "vaddr" (We also pick it ourself if vaddr == 0). // If the "linear" flag is on, the paddr pages must be adjacent in FCRAM + // This function is for interacting with the *user* portion of FCRAM mainly. For OS RAM, we use other internal functions below // r, w, x: Permissions for the allocated memory // adjustAddrs: If it's true paddr == 0 or vaddr == 0 tell the allocator to pick its own addresses. Used for eg svc ControlMemory + // isMap: Shows whether this is a reserve operation, that allocates memory and maps it to the addr space, or if it's a map operation, + // which just maps memory from paddr to vaddr without hassle. The latter is useful for shared memory mapping, the "map" ControlMemory, op, etc // Returns the vaddr the FCRAM was mapped to or nullopt if allocation failed std::optional allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r = true, bool w = true, bool x = true, - bool adjustsAddrs = false); + bool adjustsAddrs = false, bool isMap = false); KernelMemoryTypes::MemoryInfo queryMemory(u32 vaddr); - // For internal use: - // Reserve FCRAM linearly starting from physical address "paddr" (paddr == 0 is NOT special) with a size of "size" - // Without actually mapping the memory to a vaddr - // Returns true if the reservation succeeded and false if not - bool reserveMemory(u32 paddr, u32 size); + // For internal use + // Allocates a "size"-sized chunk of system FCRAM and returns the index of physical FCRAM used for the allocation + // Used for allocating things like shared memory and the like + u32 allocateSysMemory(u32 size); // Map a shared memory block to virtual address vaddr with permissions "myPerms" // The kernel has a second permission parameter in MapMemoryBlock but not sure what's used for diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ee49d612..66dcca0f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -20,6 +20,7 @@ void Memory::reset() { memoryInfo.clear(); usedFCRAMPages.reset(); usedUserMemory = 0_MB; + usedSystemMemory = 0_MB; for (u32 i = 0; i < totalPageCount; i++) { readTable[i] = 0; @@ -42,14 +43,7 @@ void Memory::reset() { // Initialize shared memory blocks and reserve memory for them for (auto& e : sharedMemBlocks) { e.mapped = false; - - std::optional possiblePaddr = findPaddr(e.size); // Find a physical FCRAM index to allocate for the shared memory block - if (!possiblePaddr.has_value()) Helpers::panic("Failed to find paddr for shared memory block"); - - e.paddr = possiblePaddr.value(); - if (!reserveMemory(e.paddr, e.size)) { // Actually reserve the memory - Helpers::panic("Failed to reserve memory for shared memory block"); - } + e.paddr = allocateSysMemory(e.size); } // Map DSP RAM as R/W at [0x1FF00000, 0x1FF7FFFF] @@ -220,19 +214,20 @@ u32 Memory::getLinearHeapVaddr() { } std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r, bool w, bool x, - bool adjustAddrs) { + bool adjustAddrs, bool isMap) { // Kernel-allocated memory & size must always be aligned to a page boundary // Additionally assert we don't OoM and that we don't try to allocate physical FCRAM past what's available to userland + // If we're mapping there's no fear of OoM, because we're not really allocating memory, just binding vaddrs to specific paddrs assert(isAligned(vaddr) && isAligned(paddr) && isAligned(size)); - assert(size <= FCRAM_APPLICATION_SIZE); - assert(usedUserMemory + size <= FCRAM_APPLICATION_SIZE); - assert(paddr + size <= FCRAM_APPLICATION_SIZE); + assert(size <= FCRAM_APPLICATION_SIZE || isMap); + assert(usedUserMemory + size <= FCRAM_APPLICATION_SIZE || isMap); + assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap); // Amount of available user FCRAM pages and FCRAM pages to allocate respectively const u32 availablePageCount = (FCRAM_APPLICATION_SIZE - usedUserMemory) / pageSize; const u32 neededPageCount = size / pageSize; - assert(availablePageCount >= neededPageCount); + assert(availablePageCount >= neededPageCount || isMap); // If the paddr is 0, that means we need to select our own // TODO: Fix. This method always tries to allocate blocks linearly. @@ -244,7 +239,7 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l Helpers::panic("Failed to find paddr"); paddr = newPaddr.value(); - assert(paddr + size <= FCRAM_APPLICATION_SIZE); + assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap); } // If the vaddr is 0 that means we need to select our own @@ -262,7 +257,8 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l } } - usedUserMemory += size; + if (!isMap) + usedUserMemory += size; // Do linear mapping u32 virtualPage = vaddr >> pageShift; @@ -318,23 +314,31 @@ std::optional Memory::findPaddr(u32 size) { return std::nullopt; } -bool Memory::reserveMemory(u32 paddr, u32 size) { - if (!isAligned(paddr) || !isAligned(size)) { - Helpers::panic("Memory::reserveMemory: Physical address or size is not page aligned. Paddr: %08X, size: %08X", paddr, size); -; } +u32 Memory::allocateSysMemory(u32 size) { + // Should never be triggered, only here as a sanity check + if (!isAligned(size)) { + Helpers::panic("Memory::allocateSysMemory: Size is not page aligned (val = %08X)", size); + } - const u32 pageCount = size / pageSize; // Number of pages we need to reserve - const u32 startingPage = paddr / pageSize; // The first page of FCRAM we'll start allocating from + // We use a pretty dumb allocator for OS memory since this is not really accessible to the app and is only used internally + // It works by just allocating memory linearly, starting from index 0 of OS memory and going up + // This should also be unreachable in practice and exists as a sanity check + if (size > remainingSysFCRAM()) { + Helpers::panic("Memory::allocateSysMemory: Overflowed OS FCRAM"); + } + + const u32 pageCount = size / pageSize; // Number of pages that will be used up + const u32 startIndex = sysFCRAMIndex() + usedSystemMemory; // Starting FCRAM index + const u32 startingPage = startIndex / pageSize; - // Assert that all of the pages are not yet reserved. TODO: Smarter memory allocator for (u32 i = 0; i < pageCount; i++) { - if (usedFCRAMPages[startingPage + i]) + if (usedFCRAMPages[startingPage + i]) // Also a theoretically unreachable panic for safety Helpers::panic("Memory::reserveMemory: Trying to reserve already reserved memory"); usedFCRAMPages[startingPage + i] = true; } - usedUserMemory += size; - return true; + usedSystemMemory += size; + return startIndex; } // The way I understand how the kernel's QueryMemory is supposed to work is that you give it a vaddr @@ -371,12 +375,7 @@ u8* Memory::mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerm bool w = myPerms & 0b010; bool x = myPerms & 0b100; - // This memory was not actually used, we just didn't want QueryMemory, getResourceLimitCurrentValues and such - // To report memory sizes wrongly. We subtract the size from the usedUserMemory size so - // allocateMemory won't break - usedUserMemory -= size; - - const auto result = allocateMemory(vaddr, paddr, size, true, r, w, x); + const auto result = allocateMemory(vaddr, paddr, size, true, r, w, x, false, true); e.mapped = true; if (!result.has_value()) { Helpers::panic("Memory::mapSharedMemory: Failed to map shared memory block");