mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Kernel: Map special regions according to ExHeader
This replaces the hardcoded VRAM/DSP mappings with ones made based on the ExHeader ARM11 Kernel caps list. While this has no visible effect for most applications (since they use a standard set of mappings) it does improve support for system modules and n3DS exclusives.
This commit is contained in:
		
							parent
							
								
									b4a93cfdde
								
							
						
					
					
						commit
						f18d454eb6
					
				
					 5 changed files with 105 additions and 52 deletions
				
			
		|  | @ -13,11 +13,11 @@ | |||
| #include "core/core_timing.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "core/hw/hw.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory_setup.h" | ||||
| #include "core/settings.h" | ||||
| #include "video_core/video_core.h" | ||||
| 
 | ||||
|  | @ -123,7 +123,8 @@ void System::Reschedule() { | |||
| } | ||||
| 
 | ||||
| System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||
|     Memory::Init(); | ||||
|     Memory::InitMemoryMap(); | ||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| 
 | ||||
|     if (Settings::values.use_cpu_jit) { | ||||
|         cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); | ||||
|  |  | |||
|  | @ -2,11 +2,13 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <cinttypes> | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| #include "audio_core/audio_core.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/config_mem.h" | ||||
|  | @ -92,57 +94,96 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { | |||
|         UNREACHABLE(); | ||||
|     } | ||||
| } | ||||
| } | ||||
| 
 | ||||
| namespace Memory { | ||||
| std::array<u8, Memory::VRAM_SIZE> vram; | ||||
| std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; | ||||
| 
 | ||||
| namespace { | ||||
| void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { | ||||
|     using namespace Memory; | ||||
| 
 | ||||
| struct MemoryArea { | ||||
|     u32 base; | ||||
|     u32 size; | ||||
|     const char* name; | ||||
| }; | ||||
|     struct MemoryArea { | ||||
|         VAddr vaddr_base; | ||||
|         PAddr paddr_base; | ||||
|         u32 size; | ||||
|     }; | ||||
| 
 | ||||
| // We don't declare the IO regions in here since its handled by other means.
 | ||||
| static MemoryArea memory_areas[] = { | ||||
|     {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
 | ||||
| }; | ||||
| } | ||||
|     // The order of entries in this array is important. The VRAM and IO VAddr ranges overlap, and
 | ||||
|     // VRAM must be tried first.
 | ||||
|     static constexpr MemoryArea memory_areas[] = { | ||||
|         {VRAM_VADDR, VRAM_PADDR, VRAM_SIZE}, | ||||
|         {IO_AREA_VADDR, IO_AREA_PADDR, IO_AREA_SIZE}, | ||||
|         {DSP_RAM_VADDR, DSP_RAM_PADDR, DSP_RAM_SIZE}, | ||||
|         {N3DS_EXTRA_RAM_VADDR, N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE - 0x20000}, | ||||
|     }; | ||||
| 
 | ||||
| void Init() { | ||||
|     InitMemoryMap(); | ||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); | ||||
| } | ||||
| 
 | ||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space) { | ||||
|     using namespace Kernel; | ||||
| 
 | ||||
|     for (MemoryArea& area : memory_areas) { | ||||
|         auto block = std::make_shared<std::vector<u8>>(area.size); | ||||
|         address_space | ||||
|             .MapMemoryBlock(area.base, std::move(block), 0, area.size, MemoryState::Private) | ||||
|             .Unwrap(); | ||||
|     VAddr mapping_limit = mapping.address + mapping.size; | ||||
|     if (mapping_limit < mapping.address) { | ||||
|         LOG_CRITICAL(Loader, "Mapping size overflowed: address=0x%08" PRIX32 " size=0x%" PRIX32, | ||||
|                      mapping.address, mapping.size); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto area = | ||||
|         std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { | ||||
|             return mapping.address >= area.vaddr_base && | ||||
|                    mapping_limit <= area.vaddr_base + area.size; | ||||
|         }); | ||||
|     if (area == std::end(memory_areas)) { | ||||
|         LOG_ERROR(Loader, "Unhandled special mapping: address=0x%08" PRIX32 " size=0x%" PRIX32 | ||||
|                           " read_only=%d unk_flag=%d", | ||||
|                   mapping.address, mapping.size, mapping.read_only, mapping.unk_flag); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     u32 offset_into_region = mapping.address - area->vaddr_base; | ||||
|     if (area->paddr_base == IO_AREA_PADDR) { | ||||
|         LOG_ERROR(Loader, "MMIO mappings are not supported yet. phys_addr=0x%08" PRIX32, | ||||
|                   area->paddr_base + offset_into_region); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual
 | ||||
|     // mappings.
 | ||||
|     u8* target_pointer = nullptr; | ||||
|     switch (area->paddr_base) { | ||||
|     case VRAM_PADDR: | ||||
|         target_pointer = vram.data(); | ||||
|         break; | ||||
|     case DSP_RAM_PADDR: | ||||
|         target_pointer = AudioCore::GetDspMemory().data(); | ||||
|         break; | ||||
|     case N3DS_EXTRA_RAM_PADDR: | ||||
|         target_pointer = n3ds_extra_ram.data(); | ||||
|         break; | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| 
 | ||||
|     // TODO(yuriks): This flag seems to have some other effect, but it's unknown what
 | ||||
|     MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; | ||||
| 
 | ||||
|     auto vma = address_space | ||||
|                    .MapBackingMemory(mapping.address, target_pointer + offset_into_region, | ||||
|                                      mapping.size, memory_state) | ||||
|                    .MoveFrom(); | ||||
|     address_space.Reprotect(vma, | ||||
|                             mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); | ||||
| } | ||||
| 
 | ||||
| void MapSharedPages(VMManager& address_space) { | ||||
|     auto cfg_mem_vma = address_space | ||||
|                            .MapBackingMemory(CONFIG_MEMORY_VADDR, (u8*)&ConfigMem::config_mem, | ||||
|                                              CONFIG_MEMORY_SIZE, MemoryState::Shared) | ||||
|                            .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, | ||||
|                                              reinterpret_cast<u8*>(&ConfigMem::config_mem), | ||||
|                                              Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) | ||||
|                            .MoveFrom(); | ||||
|     address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); | ||||
| 
 | ||||
|     auto shared_page_vma = address_space | ||||
|                                .MapBackingMemory(SHARED_PAGE_VADDR, (u8*)&SharedPage::shared_page, | ||||
|                                                  SHARED_PAGE_SIZE, MemoryState::Shared) | ||||
|                                .MapBackingMemory(Memory::SHARED_PAGE_VADDR, | ||||
|                                                  reinterpret_cast<u8*>(&SharedPage::shared_page), | ||||
|                                                  Memory::SHARED_PAGE_SIZE, MemoryState::Shared) | ||||
|                                .MoveFrom(); | ||||
|     address_space.Reprotect(shared_page_vma, VMAPermission::Read); | ||||
| 
 | ||||
|     auto& dsp_ram = AudioCore::GetDspMemory(); | ||||
|     auto dsp_vma = address_space | ||||
|                        .MapBackingMemory(DSP_RAM_VADDR, dsp_ram.data(), dsp_ram.size(), | ||||
|                                          Kernel::MemoryState::IO) | ||||
|                        .MoveFrom(); | ||||
|     address_space.Reprotect(dsp_vma, Kernel::VMAPermission::ReadWrite); | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -23,11 +23,7 @@ struct MemoryRegionInfo { | |||
| void MemoryInit(u32 mem_type); | ||||
| void MemoryShutdown(); | ||||
| MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); | ||||
| } | ||||
| 
 | ||||
| namespace Memory { | ||||
| 
 | ||||
| void Init(); | ||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space); | ||||
| 
 | ||||
| } // namespace
 | ||||
| void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); | ||||
| void MapSharedPages(VMManager& address_space); | ||||
| } // namespace Kernel
 | ||||
|  |  | |||
|  | @ -35,7 +35,6 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { | |||
|     process->codeset = std::move(code_set); | ||||
|     process->flags.raw = 0; | ||||
|     process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | ||||
|     Memory::InitLegacyAddressSpace(process->vm_manager); | ||||
| 
 | ||||
|     return process; | ||||
| } | ||||
|  | @ -78,8 +77,15 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | |||
| 
 | ||||
|             AddressMapping mapping; | ||||
|             mapping.address = descriptor << 12; | ||||
|             mapping.size = (end_desc << 12) - mapping.address; | ||||
|             mapping.writable = (descriptor & (1 << 20)) != 0; | ||||
|             VAddr end_address = end_desc << 12; | ||||
| 
 | ||||
|             if (mapping.address < end_address) { | ||||
|                 mapping.size = end_address - mapping.address; | ||||
|             } else { | ||||
|                 mapping.size = 0; | ||||
|             } | ||||
| 
 | ||||
|             mapping.read_only = (descriptor & (1 << 20)) != 0; | ||||
|             mapping.unk_flag = (end_desc & (1 << 20)) != 0; | ||||
| 
 | ||||
|             address_mappings.push_back(mapping); | ||||
|  | @ -88,8 +94,10 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | |||
|             AddressMapping mapping; | ||||
|             mapping.address = descriptor << 12; | ||||
|             mapping.size = Memory::PAGE_SIZE; | ||||
|             mapping.writable = true; // TODO: Not sure if correct
 | ||||
|             mapping.read_only = false; | ||||
|             mapping.unk_flag = false; | ||||
| 
 | ||||
|             address_mappings.push_back(mapping); | ||||
|         } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
 | ||||
|             // Kernel version
 | ||||
|             kernel_version = descriptor & 0xFFFF; | ||||
|  | @ -131,6 +139,12 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | |||
|     misc_memory_used += stack_size; | ||||
|     memory_region->used += stack_size; | ||||
| 
 | ||||
|     // Map special address mappings
 | ||||
|     MapSharedPages(vm_manager); | ||||
|     for (const auto& mapping : address_mappings) { | ||||
|         HandleSpecialMapping(vm_manager, mapping); | ||||
|     } | ||||
| 
 | ||||
|     vm_manager.LogLayout(Log::Level::Debug); | ||||
|     Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); | ||||
| } | ||||
|  | @ -138,6 +152,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | |||
| VAddr Process::GetLinearHeapAreaAddress() const { | ||||
|     return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; | ||||
| } | ||||
| 
 | ||||
| VAddr Process::GetLinearHeapBase() const { | ||||
|     return GetLinearHeapAreaAddress() + memory_region->base; | ||||
| } | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ struct AddressMapping { | |||
|     // Address and size must be page-aligned
 | ||||
|     VAddr address; | ||||
|     u32 size; | ||||
|     bool writable; | ||||
|     bool read_only; | ||||
|     bool unk_flag; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue