diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 5 | ||||
| -rw-r--r-- | src/core/memory.cpp | 86 | ||||
| -rw-r--r-- | src/core/memory_setup.h | 7 | ||||
| -rw-r--r-- | src/core/mmio.h | 34 |
6 files changed, 127 insertions, 13 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 861b711c7..edf5fcc44 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -250,6 +250,7 @@ set(HEADERS | |||
| 250 | tracer/citrace.h | 250 | tracer/citrace.h |
| 251 | memory.h | 251 | memory.h |
| 252 | memory_setup.h | 252 | memory_setup.h |
| 253 | mmio.h | ||
| 253 | settings.h | 254 | settings.h |
| 254 | system.h | 255 | system.h |
| 255 | ) | 256 | ) |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 2610acf76..1e289f38a 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "core/hle/kernel/vm_manager.h" | 9 | #include "core/hle/kernel/vm_manager.h" |
| 10 | #include "core/memory_setup.h" | 10 | #include "core/memory_setup.h" |
| 11 | #include "core/mmio.h" | ||
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| @@ -104,7 +105,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8 * m | |||
| 104 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 105 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state) { | 108 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler) { |
| 108 | // This is the appropriately sized VMA that will turn into our allocation. | 109 | // This is the appropriately sized VMA that will turn into our allocation. |
| 109 | CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); | 110 | CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); |
| 110 | VirtualMemoryArea& final_vma = vma_handle->second; | 111 | VirtualMemoryArea& final_vma = vma_handle->second; |
| @@ -114,6 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3 | |||
| 114 | final_vma.permissions = VMAPermission::ReadWrite; | 115 | final_vma.permissions = VMAPermission::ReadWrite; |
| 115 | final_vma.meminfo_state = state; | 116 | final_vma.meminfo_state = state; |
| 116 | final_vma.paddr = paddr; | 117 | final_vma.paddr = paddr; |
| 118 | final_vma.mmio_handler = mmio_handler; | ||
| 117 | UpdatePageTableForVMA(final_vma); | 119 | UpdatePageTableForVMA(final_vma); |
| 118 | 120 | ||
| 119 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 121 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| @@ -330,8 +332,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | |||
| 330 | Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); | 332 | Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); |
| 331 | break; | 333 | break; |
| 332 | case VMAType::MMIO: | 334 | case VMAType::MMIO: |
| 333 | // TODO(yuriks): Add support for MMIO handlers. | 335 | Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); |
| 334 | Memory::MapIoRegion(vma.base, vma.size); | ||
| 335 | break; | 336 | break; |
| 336 | } | 337 | } |
| 337 | } | 338 | } |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 4e95f1f0c..91d40655b 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | 12 | ||
| 13 | #include "core/hle/result.h" | 13 | #include "core/hle/result.h" |
| 14 | #include "core/mmio.h" | ||
| 14 | 15 | ||
| 15 | namespace Kernel { | 16 | namespace Kernel { |
| 16 | 17 | ||
| @@ -92,6 +93,7 @@ struct VirtualMemoryArea { | |||
| 92 | // Settings for type = MMIO | 93 | // Settings for type = MMIO |
| 93 | /// Physical address of the register area this VMA maps to. | 94 | /// Physical address of the register area this VMA maps to. |
| 94 | PAddr paddr = 0; | 95 | PAddr paddr = 0; |
| 96 | Memory::MMIORegionPointer mmio_handler = nullptr; | ||
| 95 | 97 | ||
| 96 | /// Tests if this area can be merged to the right with `next`. | 98 | /// Tests if this area can be merged to the right with `next`. |
| 97 | bool CanBeMergedWith(const VirtualMemoryArea& next) const; | 99 | bool CanBeMergedWith(const VirtualMemoryArea& next) const; |
| @@ -168,8 +170,9 @@ public: | |||
| 168 | * @param paddr The physical address where the registers are present. | 170 | * @param paddr The physical address where the registers are present. |
| 169 | * @param size Size of the mapping. | 171 | * @param size Size of the mapping. |
| 170 | * @param state MemoryState tag to attach to the VMA. | 172 | * @param state MemoryState tag to attach to the VMA. |
| 173 | * @param mmio_handler The handler that will implement read and write for this MMIO region. | ||
| 171 | */ | 174 | */ |
| 172 | ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state); | 175 | ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler); |
| 173 | 176 | ||
| 174 | /// Unmaps a range of addresses, splitting VMAs as necessary. | 177 | /// Unmaps a range of addresses, splitting VMAs as necessary. |
| 175 | ResultCode UnmapRange(VAddr target, u32 size); | 178 | ResultCode UnmapRange(VAddr target, u32 size); |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index fc79c3ee9..4753c63a7 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| 14 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 15 | #include "core/memory_setup.h" | 15 | #include "core/memory_setup.h" |
| 16 | #include "core/mmio.h" | ||
| 16 | 17 | ||
| 17 | namespace Memory { | 18 | namespace Memory { |
| 18 | 19 | ||
| @@ -25,6 +26,12 @@ enum class PageType { | |||
| 25 | Special, | 26 | Special, |
| 26 | }; | 27 | }; |
| 27 | 28 | ||
| 29 | struct SpecialRegion { | ||
| 30 | VAddr base; | ||
| 31 | u32 size; | ||
| 32 | MMIORegionPointer handler; | ||
| 33 | }; | ||
| 34 | |||
| 28 | /** | 35 | /** |
| 29 | * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely | 36 | * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely |
| 30 | * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and | 37 | * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and |
| @@ -41,8 +48,13 @@ struct PageTable { | |||
| 41 | std::array<u8*, NUM_ENTRIES> pointers; | 48 | std::array<u8*, NUM_ENTRIES> pointers; |
| 42 | 49 | ||
| 43 | /** | 50 | /** |
| 51 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of type `Special`. | ||
| 52 | */ | ||
| 53 | std::vector<SpecialRegion> special_regions; | ||
| 54 | |||
| 55 | /** | ||
| 44 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then | 56 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then |
| 45 | * the corresponding entry in `pointer` MUST be set to null. | 57 | * the corresponding entry in `pointers` MUST be set to null. |
| 46 | */ | 58 | */ |
| 47 | std::array<PageType, NUM_ENTRIES> attributes; | 59 | std::array<PageType, NUM_ENTRIES> attributes; |
| 48 | }; | 60 | }; |
| @@ -80,10 +92,12 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target) { | |||
| 80 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); | 92 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); |
| 81 | } | 93 | } |
| 82 | 94 | ||
| 83 | void MapIoRegion(VAddr base, u32 size) { | 95 | void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { |
| 84 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); | 96 | ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); |
| 85 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); | 97 | ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); |
| 86 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); | 98 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); |
| 99 | |||
| 100 | current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); | ||
| 87 | } | 101 | } |
| 88 | 102 | ||
| 89 | void UnmapRegion(VAddr base, u32 size) { | 103 | void UnmapRegion(VAddr base, u32 size) { |
| @@ -92,6 +106,22 @@ void UnmapRegion(VAddr base, u32 size) { | |||
| 92 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); | 106 | MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); |
| 93 | } | 107 | } |
| 94 | 108 | ||
| 109 | /** | ||
| 110 | * This function should only be called for virtual addreses with attribute `PageType::Special`. | ||
| 111 | */ | ||
| 112 | static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { | ||
| 113 | for (const auto& region : current_page_table->special_regions) { | ||
| 114 | if (vaddr >= region.base && vaddr < (region.base + region.size)) { | ||
| 115 | return region.handler; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | ASSERT_MSG(false, "Mapped IO page without a handler @ %08X", vaddr); | ||
| 119 | return nullptr; // Should never happen | ||
| 120 | } | ||
| 121 | |||
| 122 | template<typename T> | ||
| 123 | T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); | ||
| 124 | |||
| 95 | template <typename T> | 125 | template <typename T> |
| 96 | T Read(const VAddr vaddr) { | 126 | T Read(const VAddr vaddr) { |
| 97 | const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 127 | const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| @@ -108,14 +138,17 @@ T Read(const VAddr vaddr) { | |||
| 108 | return 0; | 138 | return 0; |
| 109 | case PageType::Memory: | 139 | case PageType::Memory: |
| 110 | ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); | 140 | ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); |
| 141 | break; | ||
| 111 | case PageType::Special: | 142 | case PageType::Special: |
| 112 | LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr); | 143 | return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); |
| 113 | return 0; | ||
| 114 | default: | 144 | default: |
| 115 | UNREACHABLE(); | 145 | UNREACHABLE(); |
| 116 | } | 146 | } |
| 117 | } | 147 | } |
| 118 | 148 | ||
| 149 | template<typename T> | ||
| 150 | void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data); | ||
| 151 | |||
| 119 | template <typename T> | 152 | template <typename T> |
| 120 | void Write(const VAddr vaddr, const T data) { | 153 | void Write(const VAddr vaddr, const T data) { |
| 121 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 154 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| @@ -131,9 +164,10 @@ void Write(const VAddr vaddr, const T data) { | |||
| 131 | return; | 164 | return; |
| 132 | case PageType::Memory: | 165 | case PageType::Memory: |
| 133 | ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); | 166 | ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); |
| 167 | break; | ||
| 134 | case PageType::Special: | 168 | case PageType::Special: |
| 135 | LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr); | 169 | WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); |
| 136 | return; | 170 | break; |
| 137 | default: | 171 | default: |
| 138 | UNREACHABLE(); | 172 | UNREACHABLE(); |
| 139 | } | 173 | } |
| @@ -191,6 +225,46 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) { | |||
| 191 | } | 225 | } |
| 192 | } | 226 | } |
| 193 | 227 | ||
| 228 | template<> | ||
| 229 | u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) { | ||
| 230 | return mmio_handler->Read8(addr); | ||
| 231 | } | ||
| 232 | |||
| 233 | template<> | ||
| 234 | u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) { | ||
| 235 | return mmio_handler->Read16(addr); | ||
| 236 | } | ||
| 237 | |||
| 238 | template<> | ||
| 239 | u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) { | ||
| 240 | return mmio_handler->Read32(addr); | ||
| 241 | } | ||
| 242 | |||
| 243 | template<> | ||
| 244 | u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) { | ||
| 245 | return mmio_handler->Read64(addr); | ||
| 246 | } | ||
| 247 | |||
| 248 | template<> | ||
| 249 | void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) { | ||
| 250 | mmio_handler->Write8(addr, data); | ||
| 251 | } | ||
| 252 | |||
| 253 | template<> | ||
| 254 | void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) { | ||
| 255 | mmio_handler->Write16(addr, data); | ||
| 256 | } | ||
| 257 | |||
| 258 | template<> | ||
| 259 | void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) { | ||
| 260 | mmio_handler->Write32(addr, data); | ||
| 261 | } | ||
| 262 | |||
| 263 | template<> | ||
| 264 | void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) { | ||
| 265 | mmio_handler->Write64(addr, data); | ||
| 266 | } | ||
| 267 | |||
| 194 | PAddr VirtualToPhysicalAddress(const VAddr addr) { | 268 | PAddr VirtualToPhysicalAddress(const VAddr addr) { |
| 195 | if (addr == 0) { | 269 | if (addr == 0) { |
| 196 | return 0; | 270 | return 0; |
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h index 84ff30120..05f70a1fe 100644 --- a/src/core/memory_setup.h +++ b/src/core/memory_setup.h | |||
| @@ -23,10 +23,11 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target); | |||
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Maps a region of the emulated process address space as a IO region. | 25 | * Maps a region of the emulated process address space as a IO region. |
| 26 | * @note Currently this can only be used to mark a region as being IO, since actual memory-mapped | 26 | * @param base The address to start mapping at. Must be page-aligned. |
| 27 | * IO isn't yet supported. | 27 | * @param size The amount of bytes to map. Must be page-aligned. |
| 28 | * @param mmio_handler The handler that backs the mapping. | ||
| 28 | */ | 29 | */ |
| 29 | void MapIoRegion(VAddr base, u32 size); | 30 | void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); |
| 30 | 31 | ||
| 31 | void UnmapRegion(VAddr base, u32 size); | 32 | void UnmapRegion(VAddr base, u32 size); |
| 32 | 33 | ||
diff --git a/src/core/mmio.h b/src/core/mmio.h new file mode 100644 index 000000000..06b555e98 --- /dev/null +++ b/src/core/mmio.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Memory { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Represents a device with memory mapped IO. | ||
| 15 | * A device may be mapped to multiple regions of memory. | ||
| 16 | */ | ||
| 17 | class MMIORegion { | ||
| 18 | public: | ||
| 19 | virtual ~MMIORegion() = default; | ||
| 20 | |||
| 21 | virtual u8 Read8(VAddr addr) = 0; | ||
| 22 | virtual u16 Read16(VAddr addr) = 0; | ||
| 23 | virtual u32 Read32(VAddr addr) = 0; | ||
| 24 | virtual u64 Read64(VAddr addr) = 0; | ||
| 25 | |||
| 26 | virtual void Write8(VAddr addr, u8 data) = 0; | ||
| 27 | virtual void Write16(VAddr addr, u16 data) = 0; | ||
| 28 | virtual void Write32(VAddr addr, u32 data) = 0; | ||
| 29 | virtual void Write64(VAddr addr, u64 data) = 0; | ||
| 30 | }; | ||
| 31 | |||
| 32 | using MMIORegionPointer = std::shared_ptr<MMIORegion>; | ||
| 33 | |||
| 34 | }; | ||