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.
This commit is contained in:
parent
5d0a1e7efd
commit
6d2734a074
@ -137,7 +137,6 @@ void System::Reschedule() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||||
Memory::InitMemoryMap();
|
|
||||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||||
|
|
||||||
if (Settings::values.use_cpu_jit) {
|
if (Settings::values.use_cpu_jit) {
|
||||||
|
@ -56,6 +56,10 @@ void VMManager::Reset() {
|
|||||||
initial_vma.size = MAX_ADDRESS;
|
initial_vma.size = MAX_ADDRESS;
|
||||||
vma_map.emplace(initial_vma.base, initial_vma);
|
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);
|
UpdatePageTableForVMA(initial_vma);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
|
|||||||
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||||
switch (vma.type) {
|
switch (vma.type) {
|
||||||
case VMAType::Free:
|
case VMAType::Free:
|
||||||
Memory::UnmapRegion(vma.base, vma.size);
|
Memory::UnmapRegion(page_table, vma.base, vma.size);
|
||||||
break;
|
break;
|
||||||
case VMAType::AllocatedMemoryBlock:
|
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;
|
break;
|
||||||
case VMAType::BackingMemory:
|
case VMAType::BackingMemory:
|
||||||
Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory);
|
Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory);
|
||||||
break;
|
break;
|
||||||
case VMAType::MMIO:
|
case VMAType::MMIO:
|
||||||
Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler);
|
Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
#include "core/memory.h"
|
||||||
#include "core/mmio.h"
|
#include "core/mmio.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
@ -102,7 +103,6 @@ struct VirtualMemoryArea {
|
|||||||
* - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
|
* - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
|
||||||
*/
|
*/
|
||||||
class VMManager final {
|
class VMManager final {
|
||||||
// TODO(yuriks): Make page tables switchable to support multiple VMManagers
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* The maximum amount of address space managed by the kernel. Addresses above this are never
|
* 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
|
/// Dumps the address space layout to the log, for debugging
|
||||||
void LogLayout(Log::Level log_level) const;
|
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:
|
private:
|
||||||
using VMAIter = decltype(vma_map)::iterator;
|
using VMAIter = decltype(vma_map)::iterator;
|
||||||
|
|
||||||
|
@ -270,6 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() {
|
|||||||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
||||||
Kernel::g_current_process->svc_access_mask.set();
|
Kernel::g_current_process->svc_access_mask.set();
|
||||||
Kernel::g_current_process->address_mappings = default_address_mappings;
|
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
|
// Attach the default resource limit (APPLICATION) to the process
|
||||||
Kernel::g_current_process->resource_limit =
|
Kernel::g_current_process->resource_limit =
|
||||||
|
@ -397,6 +397,7 @@ ResultStatus AppLoader_ELF::Load() {
|
|||||||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
||||||
Kernel::g_current_process->svc_access_mask.set();
|
Kernel::g_current_process->svc_access_mask.set();
|
||||||
Kernel::g_current_process->address_mappings = default_address_mappings;
|
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
|
// Attach the default resource limit (APPLICATION) to the process
|
||||||
Kernel::g_current_process->resource_limit =
|
Kernel::g_current_process->resource_limit =
|
||||||
|
@ -172,6 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() {
|
|||||||
codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
|
codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
|
||||||
|
|
||||||
Kernel::g_current_process = Kernel::Process::Create(std::move(codeset));
|
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
|
// Attach a resource limit to the process based on the resource limit category
|
||||||
Kernel::g_current_process->resource_limit =
|
Kernel::g_current_process->resource_limit =
|
||||||
|
@ -11,75 +11,18 @@
|
|||||||
#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"
|
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
|
|
||||||
enum class PageType {
|
PageTable* current_page_table = nullptr;
|
||||||
/// 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;
|
|
||||||
|
|
||||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
|
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() {
|
||||||
return ¤t_page_table->pointers;
|
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,
|
LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE,
|
||||||
(base + size) * PAGE_SIZE);
|
(base + size) * PAGE_SIZE);
|
||||||
|
|
||||||
@ -90,9 +33,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
|
|||||||
while (base != end) {
|
while (base != end) {
|
||||||
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
|
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base);
|
||||||
|
|
||||||
current_page_table->attributes[base] = type;
|
page_table.attributes[base] = type;
|
||||||
current_page_table->pointers[base] = memory;
|
page_table.pointers[base] = memory;
|
||||||
current_page_table->cached_res_count[base] = 0;
|
page_table.cached_res_count[base] = 0;
|
||||||
|
|
||||||
base += 1;
|
base += 1;
|
||||||
if (memory != nullptr)
|
if (memory != nullptr)
|
||||||
@ -100,30 +43,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitMemoryMap() {
|
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) {
|
||||||
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) {
|
|
||||||
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, 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((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(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((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::Unmapped);
|
MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,8 +7,10 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "core/mmio.h"
|
||||||
|
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
|
|
||||||
@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1;
|
|||||||
const int PAGE_BITS = 12;
|
const int PAGE_BITS = 12;
|
||||||
const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS);
|
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
|
/// Physical memory regions as seen from the ARM11
|
||||||
enum : PAddr {
|
enum : PAddr {
|
||||||
/// IO register area
|
/// IO register area
|
||||||
@ -126,6 +181,9 @@ enum : VAddr {
|
|||||||
NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE,
|
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 IsValidVirtualAddress(const VAddr addr);
|
||||||
bool IsValidPhysicalAddress(const PAddr 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.
|
* retrieve the current page table for that purpose.
|
||||||
*/
|
*/
|
||||||
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
|
std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers();
|
||||||
}
|
} // namespace Memory
|
||||||
|
@ -9,24 +9,24 @@
|
|||||||
|
|
||||||
namespace Memory {
|
namespace Memory {
|
||||||
|
|
||||||
void InitMemoryMap();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps an allocated buffer onto a region of the emulated process address space.
|
* 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 base The address to start mapping at. Must be page-aligned.
|
||||||
* @param size The amount of bytes to map. 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`.
|
* @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.
|
* 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 base The address to start mapping at. Must be page-aligned.
|
||||||
* @param size The amount of bytes to map. 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.
|
* @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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user