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/core_timing.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/memory.h" |  | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
| #include "core/hle/service/service.h" | #include "core/hle/service/service.h" | ||||||
| #include "core/hw/hw.h" | #include "core/hw/hw.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
|  | #include "core/memory_setup.h" | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
| 
 | 
 | ||||||
|  | @ -123,7 +123,8 @@ void System::Reschedule() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | 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) { |     if (Settings::values.use_cpu_jit) { | ||||||
|         cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); |         cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); | ||||||
|  |  | ||||||
|  | @ -2,11 +2,13 @@ | ||||||
| // Licensed under GPLv2 or any later version
 | // Licensed under GPLv2 or any later version
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <cinttypes> | ||||||
| #include <map> | #include <map> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "audio_core/audio_core.h" | #include "audio_core/audio_core.h" | ||||||
|  | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/hle/config_mem.h" | #include "core/hle/config_mem.h" | ||||||
|  | @ -92,57 +94,96 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { | ||||||
|         UNREACHABLE(); |         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 { |     struct MemoryArea { | ||||||
|     u32 base; |         VAddr vaddr_base; | ||||||
|     u32 size; |         PAddr paddr_base; | ||||||
|     const char* name; |         u32 size; | ||||||
| }; |     }; | ||||||
| 
 | 
 | ||||||
| // We don't declare the IO regions in here since its handled by other means.
 |     // The order of entries in this array is important. The VRAM and IO VAddr ranges overlap, and
 | ||||||
| static MemoryArea memory_areas[] = { |     // VRAM must be tried first.
 | ||||||
|     {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
 |     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() { |     VAddr mapping_limit = mapping.address + mapping.size; | ||||||
|     InitMemoryMap(); |     if (mapping_limit < mapping.address) { | ||||||
|     LOG_DEBUG(HW_Memory, "initialized OK"); |         LOG_CRITICAL(Loader, "Mapping size overflowed: address=0x%08" PRIX32 " size=0x%" PRIX32, | ||||||
| } |                      mapping.address, mapping.size); | ||||||
| 
 |         return; | ||||||
| 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(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     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 |     auto cfg_mem_vma = address_space | ||||||
|                            .MapBackingMemory(CONFIG_MEMORY_VADDR, (u8*)&ConfigMem::config_mem, |                            .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, | ||||||
|                                              CONFIG_MEMORY_SIZE, MemoryState::Shared) |                                              reinterpret_cast<u8*>(&ConfigMem::config_mem), | ||||||
|  |                                              Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) | ||||||
|                            .MoveFrom(); |                            .MoveFrom(); | ||||||
|     address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); |     address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); | ||||||
| 
 | 
 | ||||||
|     auto shared_page_vma = address_space |     auto shared_page_vma = address_space | ||||||
|                                .MapBackingMemory(SHARED_PAGE_VADDR, (u8*)&SharedPage::shared_page, |                                .MapBackingMemory(Memory::SHARED_PAGE_VADDR, | ||||||
|                                                  SHARED_PAGE_SIZE, MemoryState::Shared) |                                                  reinterpret_cast<u8*>(&SharedPage::shared_page), | ||||||
|  |                                                  Memory::SHARED_PAGE_SIZE, MemoryState::Shared) | ||||||
|                                .MoveFrom(); |                                .MoveFrom(); | ||||||
|     address_space.Reprotect(shared_page_vma, VMAPermission::Read); |     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 MemoryInit(u32 mem_type); | ||||||
| void MemoryShutdown(); | void MemoryShutdown(); | ||||||
| MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); | MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| namespace Memory { | void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); | ||||||
| 
 | void MapSharedPages(VMManager& address_space); | ||||||
| void Init(); | } // namespace Kernel
 | ||||||
| void InitLegacyAddressSpace(Kernel::VMManager& address_space); |  | ||||||
| 
 |  | ||||||
| } // namespace
 |  | ||||||
|  |  | ||||||
|  | @ -35,7 +35,6 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { | ||||||
|     process->codeset = std::move(code_set); |     process->codeset = std::move(code_set); | ||||||
|     process->flags.raw = 0; |     process->flags.raw = 0; | ||||||
|     process->flags.memory_region.Assign(MemoryRegion::APPLICATION); |     process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | ||||||
|     Memory::InitLegacyAddressSpace(process->vm_manager); |  | ||||||
| 
 | 
 | ||||||
|     return process; |     return process; | ||||||
| } | } | ||||||
|  | @ -78,8 +77,15 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | ||||||
| 
 | 
 | ||||||
|             AddressMapping mapping; |             AddressMapping mapping; | ||||||
|             mapping.address = descriptor << 12; |             mapping.address = descriptor << 12; | ||||||
|             mapping.size = (end_desc << 12) - mapping.address; |             VAddr end_address = end_desc << 12; | ||||||
|             mapping.writable = (descriptor & (1 << 20)) != 0; | 
 | ||||||
|  |             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; |             mapping.unk_flag = (end_desc & (1 << 20)) != 0; | ||||||
| 
 | 
 | ||||||
|             address_mappings.push_back(mapping); |             address_mappings.push_back(mapping); | ||||||
|  | @ -88,8 +94,10 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | ||||||
|             AddressMapping mapping; |             AddressMapping mapping; | ||||||
|             mapping.address = descriptor << 12; |             mapping.address = descriptor << 12; | ||||||
|             mapping.size = Memory::PAGE_SIZE; |             mapping.size = Memory::PAGE_SIZE; | ||||||
|             mapping.writable = true; // TODO: Not sure if correct
 |             mapping.read_only = false; | ||||||
|             mapping.unk_flag = false; |             mapping.unk_flag = false; | ||||||
|  | 
 | ||||||
|  |             address_mappings.push_back(mapping); | ||||||
|         } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
 |         } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
 | ||||||
|             // Kernel version
 |             // Kernel version
 | ||||||
|             kernel_version = descriptor & 0xFFFF; |             kernel_version = descriptor & 0xFFFF; | ||||||
|  | @ -131,6 +139,12 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { | ||||||
|     misc_memory_used += stack_size; |     misc_memory_used += stack_size; | ||||||
|     memory_region->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); |     vm_manager.LogLayout(Log::Level::Debug); | ||||||
|     Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); |     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 { | VAddr Process::GetLinearHeapAreaAddress() const { | ||||||
|     return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; |     return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; | ||||||
| } | } | ||||||
|  | 
 | ||||||
| VAddr Process::GetLinearHeapBase() const { | VAddr Process::GetLinearHeapBase() const { | ||||||
|     return GetLinearHeapAreaAddress() + memory_region->base; |     return GetLinearHeapAreaAddress() + memory_region->base; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ struct AddressMapping { | ||||||
|     // Address and size must be page-aligned
 |     // Address and size must be page-aligned
 | ||||||
|     VAddr address; |     VAddr address; | ||||||
|     u32 size; |     u32 size; | ||||||
|     bool writable; |     bool read_only; | ||||||
|     bool unk_flag; |     bool unk_flag; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue