mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	
						commit
						cd0b31fd73
					
				
					 6 changed files with 127 additions and 13 deletions
				
			
		|  | @ -250,6 +250,7 @@ set(HEADERS | |||
|             tracer/citrace.h | ||||
|             memory.h | ||||
|             memory_setup.h | ||||
|             mmio.h | ||||
|             settings.h | ||||
|             system.h | ||||
|             ) | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| 
 | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/memory_setup.h" | ||||
| #include "core/mmio.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
|  | @ -104,7 +105,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8 * m | |||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | ||||
| } | ||||
| 
 | ||||
| ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state) { | ||||
| ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler) { | ||||
|     // This is the appropriately sized VMA that will turn into our allocation.
 | ||||
|     CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); | ||||
|     VirtualMemoryArea& final_vma = vma_handle->second; | ||||
|  | @ -114,6 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3 | |||
|     final_vma.permissions = VMAPermission::ReadWrite; | ||||
|     final_vma.meminfo_state = state; | ||||
|     final_vma.paddr = paddr; | ||||
|     final_vma.mmio_handler = mmio_handler; | ||||
|     UpdatePageTableForVMA(final_vma); | ||||
| 
 | ||||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | ||||
|  | @ -330,8 +332,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | |||
|         Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); | ||||
|         break; | ||||
|     case VMAType::MMIO: | ||||
|         // TODO(yuriks): Add support for MMIO handlers.
 | ||||
|         Memory::MapIoRegion(vma.base, vma.size); | ||||
|         Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| #include "common/common_types.h" | ||||
| 
 | ||||
| #include "core/hle/result.h" | ||||
| #include "core/mmio.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
|  | @ -92,6 +93,7 @@ struct VirtualMemoryArea { | |||
|     // Settings for type = MMIO
 | ||||
|     /// Physical address of the register area this VMA maps to.
 | ||||
|     PAddr paddr = 0; | ||||
|     Memory::MMIORegionPointer mmio_handler = nullptr; | ||||
| 
 | ||||
|     /// Tests if this area can be merged to the right with `next`.
 | ||||
|     bool CanBeMergedWith(const VirtualMemoryArea& next) const; | ||||
|  | @ -168,8 +170,9 @@ public: | |||
|      * @param paddr The physical address where the registers are present. | ||||
|      * @param size Size of the mapping. | ||||
|      * @param state MemoryState tag to attach to the VMA. | ||||
|      * @param mmio_handler The handler that will implement read and write for this MMIO region. | ||||
|      */ | ||||
|     ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state); | ||||
|     ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler); | ||||
| 
 | ||||
|     /// Unmaps a range of addresses, splitting VMAs as necessary.
 | ||||
|     ResultCode UnmapRange(VAddr target, u32 size); | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/memory_setup.h" | ||||
| #include "core/mmio.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| 
 | ||||
|  | @ -25,6 +26,12 @@ enum class PageType { | |||
|     Special, | ||||
| }; | ||||
| 
 | ||||
| struct SpecialRegion { | ||||
|     VAddr base; | ||||
|     u32 size; | ||||
|     MMIORegionPointer handler; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely | ||||
|  * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and | ||||
|  | @ -40,9 +47,14 @@ struct PageTable { | |||
|      */ | ||||
|     std::array<u8*, NUM_ENTRIES> pointers; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of type `Special`. | ||||
|      */ | ||||
|     std::vector<SpecialRegion> special_regions; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Array of fine grained page attributes. If it is set to any value other than `Memory`, then | ||||
|      * the corresponding entry in `pointer` MUST be set to null. | ||||
|      * the corresponding entry in `pointers` MUST be set to null. | ||||
|      */ | ||||
|     std::array<PageType, NUM_ENTRIES> attributes; | ||||
| }; | ||||
|  | @ -80,10 +92,12 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target) { | |||
|     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); | ||||
| } | ||||
| 
 | ||||
| void MapIoRegion(VAddr base, u32 size) { | ||||
| void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { | ||||
|     ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); | ||||
|     ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); | ||||
|     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); | ||||
| 
 | ||||
|     current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); | ||||
| } | ||||
| 
 | ||||
| void UnmapRegion(VAddr base, u32 size) { | ||||
|  | @ -92,6 +106,22 @@ void UnmapRegion(VAddr base, u32 size) { | |||
|     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * This function should only be called for virtual addreses with attribute `PageType::Special`. | ||||
|  */ | ||||
| static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { | ||||
|     for (const auto& region : current_page_table->special_regions) { | ||||
|         if (vaddr >= region.base && vaddr < (region.base + region.size)) { | ||||
|             return region.handler; | ||||
|         } | ||||
|     } | ||||
|     ASSERT_MSG(false, "Mapped IO page without a handler @ %08X", vaddr); | ||||
|     return nullptr; // Should never happen
 | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); | ||||
| 
 | ||||
| template <typename T> | ||||
| T Read(const VAddr vaddr) { | ||||
|     const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||
|  | @ -108,14 +138,17 @@ T Read(const VAddr vaddr) { | |||
|         return 0; | ||||
|     case PageType::Memory: | ||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); | ||||
|         break; | ||||
|     case PageType::Special: | ||||
|         LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr); | ||||
|         return 0; | ||||
|         return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data); | ||||
| 
 | ||||
| template <typename T> | ||||
| void Write(const VAddr vaddr, const T data) { | ||||
|     u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||
|  | @ -131,9 +164,10 @@ void Write(const VAddr vaddr, const T data) { | |||
|         return; | ||||
|     case PageType::Memory: | ||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); | ||||
|         break; | ||||
|     case PageType::Special: | ||||
|         LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr); | ||||
|         return; | ||||
|         WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); | ||||
|         break; | ||||
|     default: | ||||
|         UNREACHABLE(); | ||||
|     } | ||||
|  | @ -191,6 +225,46 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) { | ||||
|     return mmio_handler->Read8(addr); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) { | ||||
|     return mmio_handler->Read16(addr); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) { | ||||
|     return mmio_handler->Read32(addr); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) { | ||||
|     return mmio_handler->Read64(addr); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) { | ||||
|     mmio_handler->Write8(addr, data); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) { | ||||
|     mmio_handler->Write16(addr, data); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) { | ||||
|     mmio_handler->Write32(addr, data); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) { | ||||
|     mmio_handler->Write64(addr, data); | ||||
| } | ||||
| 
 | ||||
| PAddr VirtualToPhysicalAddress(const VAddr addr) { | ||||
|     if (addr == 0) { | ||||
|         return 0; | ||||
|  |  | |||
|  | @ -23,10 +23,11 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target); | |||
| 
 | ||||
| /**
 | ||||
|  * Maps a region of the emulated process address space as a IO region. | ||||
|  * @note Currently this can only be used to mark a region as being IO, since actual memory-mapped | ||||
|  *       IO isn't yet supported. | ||||
|  * @param base The address to start mapping at. Must be page-aligned. | ||||
|  * @param size The amount of bytes to map. Must be page-aligned. | ||||
|  * @param mmio_handler The handler that backs the mapping. | ||||
|  */ | ||||
| void MapIoRegion(VAddr base, u32 size); | ||||
| void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); | ||||
| 
 | ||||
| void UnmapRegion(VAddr base, u32 size); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										34
									
								
								src/core/mmio.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/core/mmio.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Memory { | ||||
| 
 | ||||
| /**
 | ||||
|  * Represents a device with memory mapped IO. | ||||
|  * A device may be mapped to multiple regions of memory. | ||||
|  */ | ||||
| class MMIORegion { | ||||
| public: | ||||
|     virtual ~MMIORegion() = default; | ||||
| 
 | ||||
|     virtual u8 Read8(VAddr addr) = 0; | ||||
|     virtual u16 Read16(VAddr addr) = 0; | ||||
|     virtual u32 Read32(VAddr addr) = 0; | ||||
|     virtual u64 Read64(VAddr addr) = 0; | ||||
| 
 | ||||
|     virtual void Write8(VAddr addr, u8 data) = 0; | ||||
|     virtual void Write16(VAddr addr, u16 data) = 0; | ||||
|     virtual void Write32(VAddr addr, u32 data) = 0; | ||||
|     virtual void Write64(VAddr addr, u64 data) = 0; | ||||
| }; | ||||
| 
 | ||||
| using MMIORegionPointer = std::shared_ptr<MMIORegion>; | ||||
| 
 | ||||
| }; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue