mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Memory: Implement MMIO
This commit is contained in:
		
							parent
							
								
									2c663fbc3e
								
							
						
					
					
						commit
						2b93313348
					
				
					 6 changed files with 127 additions and 13 deletions
				
			
		|  | @ -250,6 +250,7 @@ set(HEADERS | ||||||
|             tracer/citrace.h |             tracer/citrace.h | ||||||
|             memory.h |             memory.h | ||||||
|             memory_setup.h |             memory_setup.h | ||||||
|  |             mmio.h | ||||||
|             settings.h |             settings.h | ||||||
|             system.h |             system.h | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "core/hle/kernel/vm_manager.h" | #include "core/hle/kernel/vm_manager.h" | ||||||
| #include "core/memory_setup.h" | #include "core/memory_setup.h" | ||||||
|  | #include "core/mmio.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | @ -104,7 +105,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8 * m | ||||||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |     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.
 |     // This is the appropriately sized VMA that will turn into our allocation.
 | ||||||
|     CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); |     CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); | ||||||
|     VirtualMemoryArea& final_vma = vma_handle->second; |     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.permissions = VMAPermission::ReadWrite; | ||||||
|     final_vma.meminfo_state = state; |     final_vma.meminfo_state = state; | ||||||
|     final_vma.paddr = paddr; |     final_vma.paddr = paddr; | ||||||
|  |     final_vma.mmio_handler = mmio_handler; | ||||||
|     UpdatePageTableForVMA(final_vma); |     UpdatePageTableForVMA(final_vma); | ||||||
| 
 | 
 | ||||||
|     return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |     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); |         Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); | ||||||
|         break; |         break; | ||||||
|     case VMAType::MMIO: |     case VMAType::MMIO: | ||||||
|         // TODO(yuriks): Add support for MMIO handlers.
 |         Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); | ||||||
|         Memory::MapIoRegion(vma.base, vma.size); |  | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | 
 | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
|  | #include "core/mmio.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | @ -92,6 +93,7 @@ struct VirtualMemoryArea { | ||||||
|     // Settings for type = MMIO
 |     // Settings for type = MMIO
 | ||||||
|     /// Physical address of the register area this VMA maps to.
 |     /// Physical address of the register area this VMA maps to.
 | ||||||
|     PAddr paddr = 0; |     PAddr paddr = 0; | ||||||
|  |     Memory::MMIORegionPointer mmio_handler = nullptr; | ||||||
| 
 | 
 | ||||||
|     /// Tests if this area can be merged to the right with `next`.
 |     /// Tests if this area can be merged to the right with `next`.
 | ||||||
|     bool CanBeMergedWith(const VirtualMemoryArea& next) const; |     bool CanBeMergedWith(const VirtualMemoryArea& next) const; | ||||||
|  | @ -168,8 +170,9 @@ public: | ||||||
|      * @param paddr The physical address where the registers are present. |      * @param paddr The physical address where the registers are present. | ||||||
|      * @param size Size of the mapping. |      * @param size Size of the mapping. | ||||||
|      * @param state MemoryState tag to attach to the VMA. |      * @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.
 |     /// Unmaps a range of addresses, splitting VMAs as necessary.
 | ||||||
|     ResultCode UnmapRange(VAddr target, u32 size); |     ResultCode UnmapRange(VAddr target, u32 size); | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/memory_setup.h" | #include "core/memory_setup.h" | ||||||
|  | #include "core/mmio.h" | ||||||
| 
 | 
 | ||||||
| namespace Memory { | namespace Memory { | ||||||
| 
 | 
 | ||||||
|  | @ -25,6 +26,12 @@ enum class PageType { | ||||||
|     Special, |     Special, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct SpecialRegion { | ||||||
|  |     VAddr base; | ||||||
|  |     u32 size; | ||||||
|  |     MMIORegionPointer handler; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely |  * 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 |  * 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; |     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 |      * 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; |     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); |     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((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); | ||||||
|     ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); |     ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); | ||||||
|     MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); |     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) { | 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); |     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> | template <typename T> | ||||||
| T Read(const VAddr vaddr) { | T Read(const VAddr vaddr) { | ||||||
|     const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |     const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||||
|  | @ -108,14 +138,17 @@ T Read(const VAddr vaddr) { | ||||||
|         return 0; |         return 0; | ||||||
|     case PageType::Memory: |     case PageType::Memory: | ||||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); |         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); | ||||||
|  |         break; | ||||||
|     case PageType::Special: |     case PageType::Special: | ||||||
|         LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr); |         return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); | ||||||
|         return 0; |  | ||||||
|     default: |     default: | ||||||
|         UNREACHABLE(); |         UNREACHABLE(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template<typename T> | ||||||
|  | void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data); | ||||||
|  | 
 | ||||||
| template <typename T> | template <typename T> | ||||||
| void Write(const VAddr vaddr, const T data) { | void Write(const VAddr vaddr, const T data) { | ||||||
|     u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |     u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | ||||||
|  | @ -131,9 +164,10 @@ void Write(const VAddr vaddr, const T data) { | ||||||
|         return; |         return; | ||||||
|     case PageType::Memory: |     case PageType::Memory: | ||||||
|         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); |         ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); | ||||||
|  |         break; | ||||||
|     case PageType::Special: |     case PageType::Special: | ||||||
|         LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr); |         WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); | ||||||
|         return; |         break; | ||||||
|     default: |     default: | ||||||
|         UNREACHABLE(); |         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) { | PAddr VirtualToPhysicalAddress(const VAddr addr) { | ||||||
|     if (addr == 0) { |     if (addr == 0) { | ||||||
|         return 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. |  * 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 |  * @param base The address to start mapping at. Must be page-aligned. | ||||||
|  *       IO isn't yet supported. |  * @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); | 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