mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	core: Add support for N3DS memory mappings (#5103)
* core: Add support for N3DS memory mappings * Address review comments
This commit is contained in:
		
							parent
							
								
									ab8cb17ab7
								
							
						
					
					
						commit
						6d3d9f7a8a
					
				
					 12 changed files with 69 additions and 22 deletions
				
			
		|  | @ -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<u32>(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<Timing>(num_cores); | ||||
| 
 | ||||
|     kernel = std::make_unique<Kernel::KernelSystem>( | ||||
|         *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 | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -19,10 +19,10 @@ namespace Kernel { | |||
| /// Initialize the kernel
 | ||||
| KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, | ||||
|                            std::function<void()> 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<ResourceLimitList>(*this); | ||||
|     for (u32 core_id = 0; core_id < num_cores; ++core_id) { | ||||
|  |  | |||
|  | @ -86,7 +86,7 @@ class KernelSystem { | |||
| public: | ||||
|     explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, | ||||
|                           std::function<void()> prepare_reschedule_callback, u32 system_mode, | ||||
|                           u32 num_cores); | ||||
|                           u32 num_cores, u8 n3ds_mode); | ||||
|     ~KernelSystem(); | ||||
| 
 | ||||
|     using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>; | ||||
|  | @ -263,7 +263,7 @@ public: | |||
|     Core::Timing& timing; | ||||
| 
 | ||||
| private: | ||||
|     void MemoryInit(u32 mem_type); | ||||
|     void MemoryInit(u32 mem_type, u8 n3ds_mode); | ||||
| 
 | ||||
|     std::function<void()> prepare_reschedule_callback; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<ConfigMem::Handler>(); | ||||
|     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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<std::optional<u32>, 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<std::optional<u8>, 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. | ||||
|  |  | |||
|  | @ -61,6 +61,19 @@ std::pair<std::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode | |||
|                           ResultStatus::Success); | ||||
| } | ||||
| 
 | ||||
| std::pair<std::optional<u8>, ResultStatus> AppLoader_NCCH::LoadKernelN3dsMode() { | ||||
|     if (!is_loaded) { | ||||
|         ResultStatus res = base_ncch.Load(); | ||||
|         if (res != ResultStatus::Success) { | ||||
|             return std::make_pair(std::optional<u8>{}, 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<Kernel::Process>& process) { | ||||
|     using Kernel::CodeSet; | ||||
| 
 | ||||
|  |  | |||
|  | @ -41,6 +41,8 @@ public: | |||
|      */ | ||||
|     std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() override; | ||||
| 
 | ||||
|     std::pair<std::optional<u8>, ResultStatus> LoadKernelN3dsMode() override; | ||||
| 
 | ||||
|     ResultStatus IsExecutable(bool& out_executable) override; | ||||
| 
 | ||||
|     ResultStatus ReadCode(std::vector<u8>& buffer) override; | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) | |||
| 
 | ||||
|     timing = std::make_unique<Core::Timing>(1); | ||||
|     memory = std::make_unique<Memory::MemorySystem>(); | ||||
|     kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1); | ||||
|     kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, [] {}, 0, 1, 0); | ||||
| 
 | ||||
|     kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0))); | ||||
|     page_table = &kernel->GetCurrentProcess()->vm_manager.page_table; | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ static std::shared_ptr<Object> 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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue