diff options
author | Subv <subv2112@gmail.com> | 2017-07-21 21:17:57 -0500 |
---|---|---|
committer | Subv <subv2112@gmail.com> | 2017-09-10 15:13:41 -0500 |
commit | 6d2734a074f44a24129db850339677d8d7b436aa (patch) | |
tree | 418be08a059813466e7ed4495fd6198b16aa4ddc /src | |
parent | 5d0a1e7efddf234234d54fe97395f6975f8d1a28 (diff) |
Kernel/Memory: Give each Process its own page table.
The loader is in charge of setting the newly created process's page table as the main one during the loading process.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/core.cpp | 1 | ||||
-rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 13 | ||||
-rw-r--r-- | src/core/hle/kernel/vm_manager.h | 6 | ||||
-rw-r--r-- | src/core/loader/3dsx.cpp | 1 | ||||
-rw-r--r-- | src/core/loader/elf.cpp | 1 | ||||
-rw-r--r-- | src/core/loader/ncch.cpp | 1 | ||||
-rw-r--r-- | src/core/memory.cpp | 87 | ||||
-rw-r--r-- | src/core/memory.h | 60 | ||||
-rw-r--r-- | src/core/memory_setup.h | 10 |
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() { } System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { - Memory::InitMemoryMap(); LOG_DEBUG(HW_Memory, "initialized OK"); 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() { initial_vma.size = MAX_ADDRESS; vma_map.emplace(initial_vma.base, initial_vma); + page_table.pointers.fill(nullptr); + page_table.attributes.fill(Memory::PageType::Unmapped); + page_table.cached_res_count.fill(0); + UpdatePageTableForVMA(initial_vma); } @@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: - Memory::UnmapRegion(vma.base, vma.size); + Memory::UnmapRegion(page_table, vma.base, vma.size); break; case VMAType::AllocatedMemoryBlock: - Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); + Memory::MapMemoryRegion(page_table, vma.base, vma.size, + vma.backing_block->data() + vma.offset); break; case VMAType::BackingMemory: - Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); + Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); + Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); break; } } 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 @@ #include <vector> #include "common/common_types.h" #include "core/hle/result.h" +#include "core/memory.h" #include "core/mmio.h" namespace Kernel { @@ -102,7 +103,6 @@ struct VirtualMemoryArea { * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ */ class VMManager final { - // TODO(yuriks): Make page tables switchable to support multiple VMManagers public: /** * The maximum amount of address space managed by the kernel. Addresses above this are never @@ -184,6 +184,10 @@ public: /// Dumps the address space layout to the log, for debugging void LogLayout(Log::Level log_level) const; + /// Each VMManager has its own page table, which is set as the main one when the owning process + /// is scheduled. + Memory::PageTable page_table; + private: using VMAIter = decltype(vma_map)::iterator; 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() { Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // Attach the default resource limit (APPLICATION) to the process 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() { Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // Attach the default resource limit (APPLICATION) to the process 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() { codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // Attach a resource limit to the process based on the resource limit category 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 @@ #include "core/hle/kernel/process.h" #include "core/memory.h" #include "core/memory_setup.h" -#include "core/mmio.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Memory { -enum class PageType { - /// Page is unmapped and should cause an access error. - Unmapped, - /// Page is mapped to regular memory. This is the only type you can get pointers to. - Memory, - /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and - /// invalidation - RasterizerCachedMemory, - /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. - Special, - /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and - /// invalidation - RasterizerCachedSpecial, -}; - -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 - * fetching requirements when accessing. In the usual case of an access to regular memory, it only - * requires an indexed fetch and a check for NULL. - */ -struct PageTable { - /** - * Array of memory pointers backing each page. An entry can only be non-null if the - * corresponding entry in the `attributes` array is of type `Memory`. - */ - std::array<u8*, PAGE_TABLE_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 `pointers` MUST be set to null. - */ - std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; - - /** - * Indicates the number of externally cached resources touching a page that should be - * flushed before the memory is accessed - */ - std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; -}; - -/// Singular page table used for the singleton process -static PageTable main_page_table; -/// Currently active page table -static PageTable* current_page_table = &main_page_table; +PageTable* current_page_table = nullptr; std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { return ¤t_page_table->pointers; } -static void MapPages(u32 base, u32 size, u8* memory, PageType type) { +static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); @@ -90,9 +33,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); - current_page_table->attributes[base] = type; - current_page_table->pointers[base] = memory; - current_page_table->cached_res_count[base] = 0; + page_table.attributes[base] = type; + page_table.pointers[base] = memory; + page_table.cached_res_count[base] = 0; base += 1; if (memory != nullptr) @@ -100,30 +43,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { } } -void InitMemoryMap() { - main_page_table.pointers.fill(nullptr); - main_page_table.attributes.fill(PageType::Unmapped); - main_page_table.cached_res_count.fill(0); -} - -void MapMemoryRegion(VAddr base, u32 size, u8* target) { +void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) { 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, target, PageType::Memory); + MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); } -void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { +void MapIoRegion(PageTable& page_table, 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); + MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); - current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); + page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); } -void UnmapRegion(VAddr base, u32 size) { +void UnmapRegion(PageTable& page_table, VAddr base, u32 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); - MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); + MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); } /** 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 @@ #include <array> #include <cstddef> #include <string> +#include <vector> #include <boost/optional.hpp> #include "common/common_types.h" +#include "core/mmio.h" namespace Memory { @@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1; const int PAGE_BITS = 12; const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); +enum class PageType { + /// Page is unmapped and should cause an access error. + Unmapped, + /// Page is mapped to regular memory. This is the only type you can get pointers to. + Memory, + /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and + /// invalidation + RasterizerCachedMemory, + /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. + Special, + /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and + /// invalidation + RasterizerCachedSpecial, +}; + +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 + * fetching requirements when accessing. In the usual case of an access to regular memory, it only + * requires an indexed fetch and a check for NULL. + */ +struct PageTable { + /** + * Array of memory pointers backing each page. An entry can only be non-null if the + * corresponding entry in the `attributes` array is of type `Memory`. + */ + std::array<u8*, PAGE_TABLE_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 `pointers` MUST be set to null. + */ + std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; + + /** + * Indicates the number of externally cached resources touching a page that should be + * flushed before the memory is accessed + */ + std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; +}; + /// Physical memory regions as seen from the ARM11 enum : PAddr { /// IO register area @@ -126,6 +181,9 @@ enum : VAddr { NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, }; +/// Currently active page table +extern PageTable* current_page_table; + bool IsValidVirtualAddress(const VAddr addr); bool IsValidPhysicalAddress(const PAddr addr); @@ -209,4 +267,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); * retrieve the current page table for that purpose. */ std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); -} +} // 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 @@ namespace Memory { -void InitMemoryMap(); - /** * Maps an allocated buffer onto a region of the emulated process address space. * + * @param page_table The page table of the emulated process. * @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 target Buffer with the memory backing the mapping. Must be of length at least `size`. */ -void MapMemoryRegion(VAddr base, u32 size, u8* target); +void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target); /** * Maps a region of the emulated process address space as a IO region. + * @param page_table The page table of the emulated process. * @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, MMIORegionPointer mmio_handler); +void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler); -void UnmapRegion(VAddr base, u32 size); +void UnmapRegion(PageTable& page_table, VAddr base, u32 size); } |