summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/hle/kernel/vm_manager.cpp13
-rw-r--r--src/core/hle/kernel/vm_manager.h6
-rw-r--r--src/core/loader/3dsx.cpp1
-rw-r--r--src/core/loader/elf.cpp1
-rw-r--r--src/core/loader/ncch.cpp1
-rw-r--r--src/core/memory.cpp87
-rw-r--r--src/core/memory.h60
-rw-r--r--src/core/memory_setup.h10
9 files changed, 93 insertions, 87 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 5332318cf..59b8768e7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -137,7 +137,6 @@ void System::Reschedule() {
137} 137}
138 138
139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { 139System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
140 Memory::InitMemoryMap();
141 LOG_DEBUG(HW_Memory, "initialized OK"); 140 LOG_DEBUG(HW_Memory, "initialized OK");
142 141
143 if (Settings::values.use_cpu_jit) { 142 if (Settings::values.use_cpu_jit) {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index cef1f7fa8..7a007c065 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -56,6 +56,10 @@ void VMManager::Reset() {
56 initial_vma.size = MAX_ADDRESS; 56 initial_vma.size = MAX_ADDRESS;
57 vma_map.emplace(initial_vma.base, initial_vma); 57 vma_map.emplace(initial_vma.base, initial_vma);
58 58
59 page_table.pointers.fill(nullptr);
60 page_table.attributes.fill(Memory::PageType::Unmapped);
61 page_table.cached_res_count.fill(0);
62
59 UpdatePageTableForVMA(initial_vma); 63 UpdatePageTableForVMA(initial_vma);
60} 64}
61 65
@@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
328void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { 332void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
329 switch (vma.type) { 333 switch (vma.type) {
330 case VMAType::Free: 334 case VMAType::Free:
331 Memory::UnmapRegion(vma.base, vma.size); 335 Memory::UnmapRegion(page_table, vma.base, vma.size);
332 break; 336 break;
333 case VMAType::AllocatedMemoryBlock: 337 case VMAType::AllocatedMemoryBlock:
334 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); 338 Memory::MapMemoryRegion(page_table, vma.base, vma.size,
339 vma.backing_block->data() + vma.offset);
335 break; 340 break;
336 case VMAType::BackingMemory: 341 case VMAType::BackingMemory:
337 Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); 342 Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
338 break; 343 break;
339 case VMAType::MMIO: 344 case VMAType::MMIO:
340 Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); 345 Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
341 break; 346 break;
342 } 347 }
343} 348}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 38e0d74d0..1302527bb 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -9,6 +9,7 @@
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/hle/result.h" 11#include "core/hle/result.h"
12#include "core/memory.h"
12#include "core/mmio.h" 13#include "core/mmio.h"
13 14
14namespace Kernel { 15namespace Kernel {
@@ -102,7 +103,6 @@ struct VirtualMemoryArea {
102 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ 103 * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
103 */ 104 */
104class VMManager final { 105class VMManager final {
105 // TODO(yuriks): Make page tables switchable to support multiple VMManagers
106public: 106public:
107 /** 107 /**
108 * The maximum amount of address space managed by the kernel. Addresses above this are never 108 * The maximum amount of address space managed by the kernel. Addresses above this are never
@@ -184,6 +184,10 @@ public:
184 /// Dumps the address space layout to the log, for debugging 184 /// Dumps the address space layout to the log, for debugging
185 void LogLayout(Log::Level log_level) const; 185 void LogLayout(Log::Level log_level) const;
186 186
187 /// Each VMManager has its own page table, which is set as the main one when the owning process
188 /// is scheduled.
189 Memory::PageTable page_table;
190
187private: 191private:
188 using VMAIter = decltype(vma_map)::iterator; 192 using VMAIter = decltype(vma_map)::iterator;
189 193
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 74e336487..69cdc0867 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -270,6 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() {
270 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 270 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
271 Kernel::g_current_process->svc_access_mask.set(); 271 Kernel::g_current_process->svc_access_mask.set();
272 Kernel::g_current_process->address_mappings = default_address_mappings; 272 Kernel::g_current_process->address_mappings = default_address_mappings;
273 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
273 274
274 // Attach the default resource limit (APPLICATION) to the process 275 // Attach the default resource limit (APPLICATION) to the process
275 Kernel::g_current_process->resource_limit = 276 Kernel::g_current_process->resource_limit =
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index cfcde9167..2f27606a1 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -397,6 +397,7 @@ ResultStatus AppLoader_ELF::Load() {
397 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 397 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
398 Kernel::g_current_process->svc_access_mask.set(); 398 Kernel::g_current_process->svc_access_mask.set();
399 Kernel::g_current_process->address_mappings = default_address_mappings; 399 Kernel::g_current_process->address_mappings = default_address_mappings;
400 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
400 401
401 // Attach the default resource limit (APPLICATION) to the process 402 // Attach the default resource limit (APPLICATION) to the process
402 Kernel::g_current_process->resource_limit = 403 Kernel::g_current_process->resource_limit =
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 7aff7f29b..79ea50147 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -172,6 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() {
172 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); 172 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
173 173
174 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); 174 Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
175 Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table;
175 176
176 // Attach a resource limit to the process based on the resource limit category 177 // Attach a resource limit to the process based on the resource limit category
177 Kernel::g_current_process->resource_limit = 178 Kernel::g_current_process->resource_limit =
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 65649d9d7..ea46b6ead 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -11,75 +11,18 @@
11#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
12#include "core/memory.h" 12#include "core/memory.h"
13#include "core/memory_setup.h" 13#include "core/memory_setup.h"
14#include "core/mmio.h"
15#include "video_core/renderer_base.h" 14#include "video_core/renderer_base.h"
16#include "video_core/video_core.h" 15#include "video_core/video_core.h"
17 16
18namespace Memory { 17namespace Memory {
19 18
20enum class PageType { 19PageTable* current_page_table = nullptr;
21 /// Page is unmapped and should cause an access error.
22 Unmapped,
23 /// Page is mapped to regular memory. This is the only type you can get pointers to.
24 Memory,
25 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
26 /// invalidation
27 RasterizerCachedMemory,
28 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
29 Special,
30 /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
31 /// invalidation
32 RasterizerCachedSpecial,
33};
34
35struct SpecialRegion {
36 VAddr base;
37 u32 size;
38 MMIORegionPointer handler;
39};
40
41/**
42 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
43 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
44 * fetching requirements when accessing. In the usual case of an access to regular memory, it only
45 * requires an indexed fetch and a check for NULL.
46 */
47struct PageTable {
48 /**
49 * Array of memory pointers backing each page. An entry can only be non-null if the
50 * corresponding entry in the `attributes` array is of type `Memory`.
51 */
52 std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
53
54 /**
55 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
56 * type `Special`.
57 */
58 std::vector<SpecialRegion> special_regions;
59
60 /**
61 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
62 * the corresponding entry in `pointers` MUST be set to null.
63 */
64 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
65
66 /**
67 * Indicates the number of externally cached resources touching a page that should be
68 * flushed before the memory is accessed
69 */
70 std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
71};
72
73/// Singular page table used for the singleton process
74static PageTable main_page_table;
75/// Currently active page table
76static PageTable* current_page_table = &main_page_table;
77 20
78std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { 21std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
79 return &current_page_table->pointers; 22 return &current_page_table->pointers;
80} 23}
81 24
82static void MapPages(u32 base, u32 size, u8* memory, PageType type) { 25static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) {
83 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, 26 LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
84 (base + size) * PAGE_SIZE); 27 (base + size) * PAGE_SIZE);
85 28
@@ -90,9 +33,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
90 while (base != end) { 33 while (base != end) {
91 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); 34 ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
92 35
93 current_page_table->attributes[base] = type; 36 page_table.attributes[base] = type;
94 current_page_table->pointers[base] = memory; 37 page_table.pointers[base] = memory;
95 current_page_table->cached_res_count[base] = 0; 38 page_table.cached_res_count[base] = 0;
96 39
97 base += 1; 40 base += 1;
98 if (memory != nullptr) 41 if (memory != nullptr)
@@ -100,30 +43,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
100 } 43 }
101} 44}
102 45
103void InitMemoryMap() { 46void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) {
104 main_page_table.pointers.fill(nullptr);
105 main_page_table.attributes.fill(PageType::Unmapped);
106 main_page_table.cached_res_count.fill(0);
107}
108
109void MapMemoryRegion(VAddr base, u32 size, u8* target) {
110 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 47 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
111 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 48 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
112 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); 49 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
113} 50}
114 51
115void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { 52void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) {
116 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 53 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
117 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 54 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
118 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); 55 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
119 56
120 current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); 57 page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
121} 58}
122 59
123void UnmapRegion(VAddr base, u32 size) { 60void UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
124 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); 61 ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
125 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); 62 ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
126 MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); 63 MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
127} 64}
128 65
129/** 66/**
diff --git a/src/core/memory.h b/src/core/memory.h
index c8c56babd..859a14202 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -7,8 +7,10 @@
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string>
10#include <vector>
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include "common/common_types.h" 12#include "common/common_types.h"
13#include "core/mmio.h"
12 14
13namespace Memory { 15namespace Memory {
14 16
@@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1;
21const int PAGE_BITS = 12; 23const int PAGE_BITS = 12;
22const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); 24const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
23 25
26enum class PageType {
27 /// Page is unmapped and should cause an access error.
28 Unmapped,
29 /// Page is mapped to regular memory. This is the only type you can get pointers to.
30 Memory,
31 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
32 /// invalidation
33 RasterizerCachedMemory,
34 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
35 Special,
36 /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and
37 /// invalidation
38 RasterizerCachedSpecial,
39};
40
41struct SpecialRegion {
42 VAddr base;
43 u32 size;
44 MMIORegionPointer handler;
45};
46
47/**
48 * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
49 * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
50 * fetching requirements when accessing. In the usual case of an access to regular memory, it only
51 * requires an indexed fetch and a check for NULL.
52 */
53struct PageTable {
54 /**
55 * Array of memory pointers backing each page. An entry can only be non-null if the
56 * corresponding entry in the `attributes` array is of type `Memory`.
57 */
58 std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
59
60 /**
61 * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
62 * type `Special`.
63 */
64 std::vector<SpecialRegion> special_regions;
65
66 /**
67 * Array of fine grained page attributes. If it is set to any value other than `Memory`, then
68 * the corresponding entry in `pointers` MUST be set to null.
69 */
70 std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
71
72 /**
73 * Indicates the number of externally cached resources touching a page that should be
74 * flushed before the memory is accessed
75 */
76 std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count;
77};
78
24/// Physical memory regions as seen from the ARM11 79/// Physical memory regions as seen from the ARM11
25enum : PAddr { 80enum : PAddr {
26 /// IO register area 81 /// IO register area
@@ -126,6 +181,9 @@ enum : VAddr {
126 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, 181 NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
127}; 182};
128 183
184/// Currently active page table
185extern PageTable* current_page_table;
186
129bool IsValidVirtualAddress(const VAddr addr); 187bool IsValidVirtualAddress(const VAddr addr);
130bool IsValidPhysicalAddress(const PAddr addr); 188bool IsValidPhysicalAddress(const PAddr addr);
131 189
@@ -209,4 +267,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode);
209 * retrieve the current page table for that purpose. 267 * retrieve the current page table for that purpose.
210 */ 268 */
211std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); 269std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
212} 270} // namespace Memory
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 3fdf3a87d..c58baa50b 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -9,24 +9,24 @@
9 9
10namespace Memory { 10namespace Memory {
11 11
12void InitMemoryMap();
13
14/** 12/**
15 * Maps an allocated buffer onto a region of the emulated process address space. 13 * Maps an allocated buffer onto a region of the emulated process address space.
16 * 14 *
15 * @param page_table The page table of the emulated process.
17 * @param base The address to start mapping at. Must be page-aligned. 16 * @param base The address to start mapping at. Must be page-aligned.
18 * @param size The amount of bytes to map. Must be page-aligned. 17 * @param size The amount of bytes to map. Must be page-aligned.
19 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. 18 * @param target Buffer with the memory backing the mapping. Must be of length at least `size`.
20 */ 19 */
21void MapMemoryRegion(VAddr base, u32 size, u8* target); 20void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target);
22 21
23/** 22/**
24 * Maps a region of the emulated process address space as a IO region. 23 * Maps a region of the emulated process address space as a IO region.
24 * @param page_table The page table of the emulated process.
25 * @param base The address to start mapping at. Must be page-aligned. 25 * @param base The address to start mapping at. Must be page-aligned.
26 * @param size The amount of bytes to map. Must be page-aligned. 26 * @param size The amount of bytes to map. Must be page-aligned.
27 * @param mmio_handler The handler that backs the mapping. 27 * @param mmio_handler The handler that backs the mapping.
28 */ 28 */
29void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); 29void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
30 30
31void UnmapRegion(VAddr base, u32 size); 31void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
32} 32}