diff --git a/src/core/core.cpp b/src/core/core.cpp index 01ab8481c..03bd384f2 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -174,7 +174,9 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st } ASSERT(system_mode.first); - ResultStatus init_result{Init(emu_window, *system_mode.first)}; + auto n3ds_mode = app_loader->LoadKernelN3dsMode(); + ASSERT(n3ds_mode.first); + ResultStatus init_result{Init(emu_window, *system_mode.first, *n3ds_mode.first)}; if (init_result != ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", static_cast(init_result)); @@ -246,7 +248,7 @@ void System::Reschedule() { } } -System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode) { +System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode) { LOG_DEBUG(HW_Memory, "initialized OK"); std::size_t num_cores = 2; @@ -259,7 +261,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo timing = std::make_unique(num_cores); kernel = std::make_unique( - *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores); + *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode); if (Settings::values.use_cpu_jit) { #ifdef ARCHITECTURE_x86_64 diff --git a/src/core/core.h b/src/core/core.h index 2727ea78c..4bc8e6a85 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -303,7 +303,7 @@ private: * @param system_mode The system mode. * @return ResultStatus code, indicating if the operation succeeded. */ - ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode); + ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode); /// Reschedule the core emulation void Reschedule(); diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h index 8deda1fff..7eadd9835 100644 --- a/src/core/file_sys/ncch_container.h +++ b/src/core/file_sys/ncch_container.h @@ -149,7 +149,8 @@ struct ExHeader_StorageInfo { struct ExHeader_ARM11_SystemLocalCaps { u64_le program_id; u32_le core_version; - u8 reserved_flags[2]; + u8 reserved_flag; + u8 n3ds_mode; union { u8 flags0; BitField<0, 2, u8> ideal_processor; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index c0b6f8308..995cfc658 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -19,10 +19,10 @@ namespace Kernel { /// Initialize the kernel KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, std::function prepare_reschedule_callback, u32 system_mode, - u32 num_cores) + u32 num_cores, u8 n3ds_mode) : memory(memory), timing(timing), prepare_reschedule_callback(std::move(prepare_reschedule_callback)) { - MemoryInit(system_mode); + MemoryInit(system_mode, n3ds_mode); resource_limits = std::make_unique(*this); for (u32 core_id = 0; core_id < num_cores; ++core_id) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index fd68cbf6d..c275d7ce4 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -86,7 +86,7 @@ class KernelSystem { public: explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, std::function prepare_reschedule_callback, u32 system_mode, - u32 num_cores); + u32 num_cores, u8 n3ds_mode); ~KernelSystem(); using PortPair = std::pair, std::shared_ptr>; @@ -263,7 +263,7 @@ public: Core::Timing& timing; private: - void MemoryInit(u32 mem_type); + void MemoryInit(u32 mem_type, u8 n3ds_mode); std::function prepare_reschedule_callback; diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index e4aae5d13..7b6425bbd 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -19,6 +19,7 @@ #include "core/hle/kernel/vm_manager.h" #include "core/hle/result.h" #include "core/memory.h" +#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -40,11 +41,32 @@ static const u32 memory_region_sizes[8][3] = { {0x0B200000, 0x02E00000, 0x02000000}, // 7 }; -void KernelSystem::MemoryInit(u32 mem_type) { - // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead. - ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!"); +namespace MemoryMode { +enum N3DSMode : u8 { + Mode6 = 1, + Mode7 = 2, + Mode6_2 = 3, +}; +} + +void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) { ASSERT(mem_type != 1); + const bool is_new_3ds = Settings::values.is_new_3ds; + u32 reported_mem_type = mem_type; + if (is_new_3ds) { + if (n3ds_mode == MemoryMode::Mode6 || n3ds_mode == MemoryMode::Mode6_2) { + mem_type = 6; + reported_mem_type = 6; + } else if (n3ds_mode == MemoryMode::Mode7) { + mem_type = 7; + reported_mem_type = 7; + } else { + // On the N3ds, all O3ds configurations (<=5) are forced to 6 instead. + mem_type = 6; + } + } + // The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with // the sizes specified in the memory_region_sizes table. VAddr base = 0; @@ -55,14 +77,12 @@ void KernelSystem::MemoryInit(u32 mem_type) { } // We must've allocated the entire FCRAM by the end - ASSERT(base == Memory::FCRAM_SIZE); + ASSERT(base == (is_new_3ds ? Memory::FCRAM_N3DS_SIZE : Memory::FCRAM_SIZE)); config_mem_handler = std::make_unique(); auto& config_mem = config_mem_handler->GetConfigMem(); - config_mem.app_mem_type = mem_type; - // app_mem_malloc does not always match the configured size for memory_region[0]: in case the - // n3DS type override is in effect it reports the size the game expects, not the real one. - config_mem.app_mem_alloc = memory_region_sizes[mem_type][0]; + config_mem.app_mem_type = reported_mem_type; + config_mem.app_mem_alloc = memory_region_sizes[reported_mem_type][0]; config_mem.sys_mem_alloc = memory_regions[1].size; config_mem.base_mem_alloc = memory_regions[2].size; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 0414f181c..f9a8e9296 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -105,13 +105,22 @@ public: * Loads the system mode that this application needs. * This function defaults to 2 (96MB allocated to the application) if it can't read the * information. - * @returns A pair with the optional system mode, and and the status. + * @returns A pair with the optional system mode, and the status. */ virtual std::pair, ResultStatus> LoadKernelSystemMode() { // 96MB allocated to the application. return std::make_pair(2, ResultStatus::Success); } + /** + * Loads the N3ds mode that this application uses. + * It defaults to 0 (O3DS default) if it can't read the information. + * @returns A pair with the optional N3ds mode, and the status. + */ + virtual std::pair, ResultStatus> LoadKernelN3dsMode() { + return std::make_pair(0, ResultStatus::Success); + } + /** * Get whether this application is executable. * @param out_executable Reference to store the executable flag into. diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 1a966da5e..81053524f 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -61,6 +61,19 @@ std::pair, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode ResultStatus::Success); } +std::pair, ResultStatus> AppLoader_NCCH::LoadKernelN3dsMode() { + if (!is_loaded) { + ResultStatus res = base_ncch.Load(); + if (res != ResultStatus::Success) { + return std::make_pair(std::optional{}, res); + } + } + + // Set the system mode as the one from the exheader. + return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.n3ds_mode, + ResultStatus::Success); +} + ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr& process) { using Kernel::CodeSet; diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 041cfddbd..6f680b063 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -41,6 +41,8 @@ public: */ std::pair, ResultStatus> LoadKernelSystemMode() override; + std::pair, ResultStatus> LoadKernelN3dsMode() override; + ResultStatus IsExecutable(bool& out_executable) override; ResultStatus ReadCode(std::vector& buffer) override; diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 0957c7c97..583459e7c 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -17,7 +17,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) timing = std::make_unique(1); memory = std::make_unique(); - kernel = std::make_unique(*memory, *timing, [] {}, 0, 1); + kernel = std::make_unique(*memory, *timing, [] {}, 0, 1, 0); kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index 59026afd6..d1aacd739 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -23,7 +23,7 @@ static std::shared_ptr MakeObject(Kernel::KernelSystem& kernel) { TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { Core::Timing timing(1); Memory::MemorySystem memory; - Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1); + Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0); auto [server, client] = kernel.CreateSessionPair(); HLERequestContext context(kernel, std::move(server), nullptr); @@ -235,7 +235,7 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { Core::Timing timing(1); Memory::MemorySystem memory; - Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1); + Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0); auto [server, client] = kernel.CreateSessionPair(); HLERequestContext context(kernel, std::move(server), nullptr); diff --git a/src/tests/core/memory/memory.cpp b/src/tests/core/memory/memory.cpp index 2e7c71434..8f08862a1 100644 --- a/src/tests/core/memory/memory.cpp +++ b/src/tests/core/memory/memory.cpp @@ -13,7 +13,7 @@ TEST_CASE("Memory::IsValidVirtualAddress", "[core][memory]") { Core::Timing timing(1); Memory::MemorySystem memory; - Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1); + Kernel::KernelSystem kernel(memory, timing, [] {}, 0, 1, 0); SECTION("these regions should not be mapped on an empty process") { auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0)); CHECK(Memory::IsValidVirtualAddress(*process, Memory::PROCESS_IMAGE_VADDR) == false);