summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hle/kernel/vm_manager.cpp7
-rw-r--r--src/core/hle/kernel/vm_manager.h5
-rw-r--r--src/core/memory.cpp86
-rw-r--r--src/core/memory_setup.h7
-rw-r--r--src/core/mmio.h34
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
12namespace Kernel { 13namespace 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
107ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state) { 108ResultVal<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
15namespace Kernel { 16namespace 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
17namespace Memory { 18namespace Memory {
18 19
@@ -25,6 +26,12 @@ enum class PageType {
25 Special, 26 Special,
26}; 27};
27 28
29struct 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
83void MapIoRegion(VAddr base, u32 size) { 95void 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
89void UnmapRegion(VAddr base, u32 size) { 103void 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 */
112static 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
122template<typename T>
123T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
124
95template <typename T> 125template <typename T>
96T Read(const VAddr vaddr) { 126T 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
149template<typename T>
150void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
151
119template <typename T> 152template <typename T>
120void Write(const VAddr vaddr, const T data) { 153void 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
228template<>
229u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
230 return mmio_handler->Read8(addr);
231}
232
233template<>
234u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) {
235 return mmio_handler->Read16(addr);
236}
237
238template<>
239u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) {
240 return mmio_handler->Read32(addr);
241}
242
243template<>
244u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) {
245 return mmio_handler->Read64(addr);
246}
247
248template<>
249void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) {
250 mmio_handler->Write8(addr, data);
251}
252
253template<>
254void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) {
255 mmio_handler->Write16(addr, data);
256}
257
258template<>
259void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) {
260 mmio_handler->Write32(addr, data);
261}
262
263template<>
264void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) {
265 mmio_handler->Write64(addr, data);
266}
267
194PAddr VirtualToPhysicalAddress(const VAddr addr) { 268PAddr 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 */
29void MapIoRegion(VAddr base, u32 size); 30void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler);
30 31
31void UnmapRegion(VAddr base, u32 size); 32void 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
11namespace Memory {
12
13/**
14 * Represents a device with memory mapped IO.
15 * A device may be mapped to multiple regions of memory.
16 */
17class MMIORegion {
18public:
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
32using MMIORegionPointer = std::shared_ptr<MMIORegion>;
33
34};