diff --git a/include/loader/ncch.hpp b/include/loader/ncch.hpp index 7f0ff37f..f00ec7b8 100644 --- a/include/loader/ncch.hpp +++ b/include/loader/ncch.hpp @@ -47,6 +47,9 @@ struct NCCH { bool seedCrypto = false; u8 secondaryKeySlot = 0; + // Contains info such as the ideal processor for threads, affinity mask, and how much memory should be reserved for the application + u8 flag0 = 0; + static constexpr u64 mediaUnit = 0x200; u64 size = 0; // Size of NCCH converted to bytes u32 stackSize = 0; diff --git a/include/memory.hpp b/include/memory.hpp index fd1b10b4..4f53890c 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -123,16 +123,21 @@ public: static constexpr u32 pageMask = pageSize - 1; static constexpr u32 totalPageCount = 1 << (32 - pageShift); - static constexpr u32 FCRAM_SIZE = u32(128_MB); - static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB); + // TODO: All of these change on N3DS + static constexpr u32 FCRAM_SIZE = u32(128_MB); // Total amount of FCRAM static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize; - static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize; + static constexpr u32 FCRAM_MAX_APPLICATION_SIZE = u32(96_MB); // Max amount available for applications + static constexpr u32 FCRAM_APPLICATION_MAX_PAGE_COUNT = FCRAM_MAX_APPLICATION_SIZE / pageSize; static constexpr u32 DSP_RAM_SIZE = u32(512_KB); static constexpr u32 DSP_CODE_MEMORY_OFFSET = u32(0_KB); static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB); private: + // This will get adjusted based on the cartridge exheader via setFcramApplicationSize, 64MB is just a default + u32 fcramApplicationSize = u32(64_MB); + u32 fcramApplicationPageCount = u32(64_MB) / pageSize; // Same here + std::bitset usedFCRAMPages; std::optional findPaddr(u32 size); u64 timeSince3DSEpoch(); @@ -186,9 +191,23 @@ public: u32 getLinearHeapVaddr(); u8* getFCRAM() { return fcram; } + // Set the amount of application FCRAM + void setApplicationRamSize(u32 size) { + fcramApplicationSize = size; + fcramApplicationPageCount = size / pageSize; + + if (usedUserMemory > fcramApplicationSize) { + Helpers::panic("Set application FCRAM to a value less than the currently used application memory"); + } + + if (usedSystemMemory > totalSysFCRAM()) { + Helpers::panic("Set system FCRAM to a value less than the currently used system memory"); + } + } + // 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; + return FCRAM_SIZE - fcramApplicationSize; } // Amount of OS-only FCRAM currently available @@ -198,8 +217,8 @@ public: // 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; + u32 sysFCRAMStartIndex() { + return fcramApplicationSize; } enum class BatteryLevel { diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index cb854e07..b9e337e1 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -21,6 +21,9 @@ std::optional Memory::loadELF(std::ifstream& file) { return std::nullopt; } + // Use 64MB of application FCRAM for ELFs by default + setApplicationRamSize(64_MB); + auto segNum = reader.segments.size(); printf("Number of segments: %d\n", segNum); printf(" # Perms Vaddr File Size Mem Size\n"); diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index d3d05839..862431d5 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -160,6 +160,10 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn text.extract(&exheader[0x10]); rodata.extract(&exheader[0x20]); data.extract(&exheader[0x30]); + + // Access Control Info section of the exheader + const u8* aci = &exheader[0x200]; + flag0 = aci[0xE]; } printf("Stack size: %08X\nBSS size: %08X\n", stackSize, bssSize); diff --git a/src/core/loader/ncsd.cpp b/src/core/loader/ncsd.cpp index 8e8a5839..2bea082b 100644 --- a/src/core/loader/ncsd.cpp +++ b/src/core/loader/ncsd.cpp @@ -13,6 +13,31 @@ bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) { static constexpr std::array regionNames = {"Japan", "North America", "Europe", "Australia", "China", "Korea", "Taiwan" }; + // The application RAM sizes for Old 3DS titles, chosen based on flag0 of the exheader + static constexpr std::array applicationRamSizes_O3DS = { + 64_MB, // Prod + 64_MB, // Unused + 96_MB, // Dev1 + 80_MB, // Dev2 + 72_MB, // Dev3 + 32_MB, // Dev4 + 64_MB, 64_MB, 64_MB, 64_MB, 64_MB, 64_MB, 64_MB, 64_MB, 64_MB, 64_MB, + }; + + // Similar for New 3DS titles chosed based on flag2 from exheader. Index 0 isn't actually used, and denotes that this should use the Old3DS setting + [[maybe_unused]] static constexpr std::array applicationRamSizes_New3DS = { + 64_MB, // Legacy mode + 124_MB, // Prod + 178_MB, // Dev1 + 124_MB, // Dev2 + 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, 124_MB, + }; + + // TODO: Handle N3DS FCRAM here + const u32 memorySize = applicationRamSizes_O3DS[Helpers::getBits<4, 4>(cxi.flag0)]; + setApplicationRamSize(memorySize); + printf("Set application RAM size to %dMB\n", u32(memorySize / 1_MB)); + // Set autodetected 3DS region to one of the values allowed by the CXI's SMDH region = cxi.region.value(); printf("Console region autodetected to: %s\n", regionNames[static_cast(region)]); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index b532fb41..e8163bf4 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -289,12 +289,12 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l // 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 || isMap); - assert(usedUserMemory + size <= FCRAM_APPLICATION_SIZE || isMap); - assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap); + assert(size <= fcramApplicationSize || isMap); + assert(usedUserMemory + size <= fcramApplicationSize || isMap); + assert(paddr + size <= fcramApplicationSize || isMap); // Amount of available user FCRAM pages and FCRAM pages to allocate respectively - const u32 availablePageCount = (FCRAM_APPLICATION_SIZE - usedUserMemory) / pageSize; + const u32 availablePageCount = (fcramApplicationSize - usedUserMemory) / pageSize; const u32 neededPageCount = size / pageSize; assert(availablePageCount >= neededPageCount || isMap); @@ -367,7 +367,7 @@ std::optional Memory::findPaddr(u32 size) { // If this ends up >= than neededPages then the paddr is good (ie we can use the candidate page as a base address) u32 counter = 0; - for (u32 i = 0; i < FCRAM_APPLICATION_PAGE_COUNT; i++) { + for (u32 i = 0; i < fcramApplicationPageCount; i++) { if (usedFCRAMPages[i]) { // Page is occupied already, go to new candidate candidatePage = i + 1; counter = 0; @@ -392,19 +392,22 @@ u32 Memory::allocateSysMemory(u32 size) { } // 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 + // It works by just allocating memory linearly, starting from the end of system memory and going back + // This happens in order to allow changing the size of application FCRAM when loading a ROM, and not risking messing up our data // 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 pageCount = size / pageSize; // Number of pages that will be used up + const u32 startIndex = FCRAM_SIZE - usedSystemMemory - size; // Starting FCRAM index const u32 startingPage = startIndex / pageSize; for (u32 i = 0; i < pageCount; i++) { - if (usedFCRAMPages[startingPage + i]) // Also a theoretically unreachable panic for safety + 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; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 75b5dbdd..234a8998 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -444,6 +444,8 @@ bool Emulator::loadROM(const std::filesystem::path& path) { } kernel.initializeFS(); + memory.setApplicationRamSize(64_MB); // Set the application RAM size to a default 64MB that loaded apps can override if needed + auto extension = path.extension(); bool success; // Tracks if we loaded the ROM successfully