[hle] reuse previous pagetable when initializing new processes on the same KProcessPageTable

Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie 2026-04-26 09:32:33 +00:00
parent 91058d7383
commit 6af305a10b
5 changed files with 48 additions and 91 deletions

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@ -142,10 +142,8 @@ struct PageTable {
VirtualBuffer<PageEntryData> entries; VirtualBuffer<PageEntryData> entries;
static_assert(sizeof(PageEntryData) == 32); static_assert(sizeof(PageEntryData) == 32);
std::size_t current_address_space_width_in_bits{};
u8* fastmem_arena{}; u8* fastmem_arena{};
std::size_t current_address_space_width_in_bits{};
std::size_t page_size{}; std::size_t page_size{};
}; };

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -21,7 +24,9 @@ public:
// "with the current allocator"); // "with the current allocator");
constexpr VirtualBuffer() = default; constexpr VirtualBuffer() = default;
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { explicit VirtualBuffer(std::size_t count) noexcept
: alloc_size{count * sizeof(T)}
{
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size)); base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
} }
@ -33,8 +38,8 @@ public:
VirtualBuffer& operator=(const VirtualBuffer&) = delete; VirtualBuffer& operator=(const VirtualBuffer&) = delete;
VirtualBuffer(VirtualBuffer&& other) noexcept VirtualBuffer(VirtualBuffer&& other) noexcept
: alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr), nullptr}
nullptr} {} {}
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept { VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
alloc_size = std::exchange(other.alloc_size, 0); alloc_size = std::exchange(other.alloc_size, 0);
@ -42,35 +47,31 @@ public:
return *this; return *this;
} }
void resize(std::size_t count) { void resize(std::size_t count) noexcept {
const auto new_size = count * sizeof(T); if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
if (new_size == alloc_size) { FreeMemoryPages(base_ptr, alloc_size);
return; alloc_size = new_size;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
} }
FreeMemoryPages(base_ptr, alloc_size);
alloc_size = new_size;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
} }
[[nodiscard]] constexpr const T& operator[](std::size_t index) const { [[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
return base_ptr[index]; return base_ptr[index];
} }
[[nodiscard]] constexpr T& operator[](std::size_t index) { [[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
return base_ptr[index]; return base_ptr[index];
} }
[[nodiscard]] constexpr T* data() { [[nodiscard]] constexpr T* data() noexcept {
return base_ptr; return base_ptr;
} }
[[nodiscard]] constexpr const T* data() const { [[nodiscard]] constexpr const T* data() const noexcept {
return base_ptr; return base_ptr;
} }
[[nodiscard]] constexpr std::size_t size() const { [[nodiscard]] constexpr std::size_t size() const noexcept {
return alloc_size / sizeof(T); return alloc_size / sizeof(T);
} }

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -191,8 +191,7 @@ Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start
m_cached_physical_heap_region = nullptr; m_cached_physical_heap_region = nullptr;
// Initialize our implementation. // Initialize our implementation.
m_impl = std::make_unique<Common::PageTable>(); m_impl.Resize(m_address_space_width, PageBits);
m_impl->Resize(m_address_space_width, PageBits);
// Set the tracking memory. // Set the tracking memory.
m_memory = std::addressof(memory); m_memory = std::addressof(memory);
@ -202,13 +201,7 @@ Result KPageTableBase::InitializeForKernel(bool is_64_bit, KVirtualAddress start
m_memory_block_slab_manager)); m_memory_block_slab_manager));
} }
Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool enable_aslr, bool enable_das_merge, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KSystemResource* system_resource, KResourceLimit* resource_limit, Core::Memory::Memory& memory, KProcessAddress aslr_space_start) {
bool enable_das_merge, bool from_back,
KMemoryManager::Pool pool, KProcessAddress code_address,
size_t code_size, KSystemResource* system_resource,
KResourceLimit* resource_limit,
Core::Memory::Memory& memory,
KProcessAddress aslr_space_start) {
// Calculate region extents. // Calculate region extents.
const size_t as_width = GetAddressSpaceWidth(as_type); const size_t as_width = GetAddressSpaceWidth(as_type);
const KProcessAddress start = 0; const KProcessAddress start = 0;
@ -319,14 +312,10 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
// Determine random placements for each region. // Determine random placements for each region.
size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0; size_t alias_rnd = 0, heap_rnd = 0, stack_rnd = 0, kmap_rnd = 0;
if (enable_aslr) { if (enable_aslr) {
alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * alias_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
RegionAlignment; heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
heap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
RegionAlignment; kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * RegionAlignment;
stack_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
RegionAlignment;
kmap_rnd = KSystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) *
RegionAlignment;
} }
// Setup heap and alias regions. // Setup heap and alias regions.
@ -445,15 +434,13 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
ASSERT(heap_last < kmap_start || kmap_last < heap_start); ASSERT(heap_last < kmap_start || kmap_last < heap_start);
// Initialize our implementation. // Initialize our implementation.
m_impl = std::make_unique<Common::PageTable>(); m_impl.Resize(m_address_space_width, PageBits);
m_impl->Resize(m_address_space_width, PageBits);
// Set the tracking memory. // Set the tracking memory.
m_memory = std::addressof(memory); m_memory = std::addressof(memory);
// Initialize our memory block manager. // Initialize our memory block manager.
R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, R_RETURN(m_memory_block_manager.Initialize(m_address_space_start, m_address_space_end, m_memory_block_slab_manager));
m_memory_block_slab_manager));
} }
Result KPageTableBase::FinalizeProcess() { Result KPageTableBase::FinalizeProcess() {
@ -476,7 +463,7 @@ void KPageTableBase::Finalize() {
this->FinalizeProcess(); this->FinalizeProcess();
auto BlockCallback = [&](KProcessAddress addr, u64 size) { auto BlockCallback = [&](KProcessAddress addr, u64 size) {
if (m_impl->fastmem_arena) { if (m_impl.fastmem_arena) {
m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false); m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
} }
@ -514,9 +501,6 @@ void KPageTableBase::Finalize() {
m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax, m_resource_limit->Release(Svc::LimitableResource::PhysicalMemoryMax,
m_mapped_ipc_server_memory); m_mapped_ipc_server_memory);
} }
// Close the backing page table, as the destructor is not called for guest objects.
m_impl.reset();
} }
KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const { KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
@ -2349,7 +2333,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
TraversalContext context; TraversalContext context;
TraversalEntry next_entry; TraversalEntry next_entry;
bool traverse_valid = bool traverse_valid =
m_impl->BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr); m_impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), virt_addr);
R_UNLESS(traverse_valid, ResultInvalidCurrentMemory); R_UNLESS(traverse_valid, ResultInvalidCurrentMemory);
// Set tracking variables. // Set tracking variables.
@ -2360,7 +2344,7 @@ Result KPageTableBase::QueryPhysicalAddress(Svc::lp64::PhysicalMemoryInfo* out,
while (true) { while (true) {
// Continue the traversal. // Continue the traversal.
traverse_valid = traverse_valid =
m_impl->ContinueTraversal(std::addressof(next_entry), std::addressof(context)); m_impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context));
if (!traverse_valid) { if (!traverse_valid) {
break; break;
} }
@ -5730,14 +5714,14 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
this->MakePageGroup(pages_to_close, virt_addr, num_pages); this->MakePageGroup(pages_to_close, virt_addr, num_pages);
// Unmap. // Unmap.
m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap); m_memory->UnmapRegion(m_impl, virt_addr, num_pages * PageSize, separate_heap);
R_SUCCEED(); R_SUCCEED();
} }
case OperationType::Map: { case OperationType::Map: {
ASSERT(virt_addr != 0); ASSERT(virt_addr != 0);
ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, m_memory->MapMemoryRegion(m_impl, virt_addr, num_pages * PageSize, phys_addr,
ConvertToMemoryPermission(properties.perm), false); ConvertToMemoryPermission(properties.perm), false);
// Open references to pages, if we should. // Open references to pages, if we should.
@ -5754,7 +5738,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
case OperationType::ChangePermissions: case OperationType::ChangePermissions:
case OperationType::ChangePermissionsAndRefresh: case OperationType::ChangePermissionsAndRefresh:
case OperationType::ChangePermissionsAndRefreshAndFlush: { case OperationType::ChangePermissionsAndRefreshAndFlush: {
m_memory->ProtectRegion(*m_impl, virt_addr, num_pages * PageSize, m_memory->ProtectRegion(m_impl, virt_addr, num_pages * PageSize,
ConvertToMemoryPermission(properties.perm)); ConvertToMemoryPermission(properties.perm));
R_SUCCEED(); R_SUCCEED();
} }
@ -5788,7 +5772,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
const size_t size{node.GetNumPages() * PageSize}; const size_t size{node.GetNumPages() * PageSize};
// Map the pages. // Map the pages.
m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), m_memory->MapMemoryRegion(m_impl, virt_addr, size, node.GetAddress(),
ConvertToMemoryPermission(properties.perm), separate_heap); ConvertToMemoryPermission(properties.perm), separate_heap);
virt_addr += size; virt_addr += size;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -215,7 +215,7 @@ private:
mutable KLightLock m_general_lock; mutable KLightLock m_general_lock;
mutable KLightLock m_map_physical_memory_lock; mutable KLightLock m_map_physical_memory_lock;
KLightLock m_device_map_lock; KLightLock m_device_map_lock;
std::unique_ptr<Common::PageTable> m_impl{}; Common::PageTable m_impl{};
Core::Memory::Memory* m_memory{}; Core::Memory::Memory* m_memory{};
KMemoryBlockManager m_memory_block_manager{}; KMemoryBlockManager m_memory_block_manager{};
u32 m_allocate_option{}; u32 m_allocate_option{};
@ -300,26 +300,11 @@ public:
} }
public: public:
Core::Memory::Memory& GetMemory() { [[nodiscard]] Core::Memory::Memory& GetMemory() noexcept { return *m_memory; }
return *m_memory; [[nodiscard]] Core::Memory::Memory const& GetMemory() const noexcept { return *m_memory; }
} [[nodiscard]] Common::PageTable& GetImpl() noexcept { return m_impl; }
[[nodiscard]] Common::PageTable const& GetImpl() const noexcept { return m_impl; }
Core::Memory::Memory& GetMemory() const { [[nodiscard]] size_t GetNumGuardPages() const noexcept { return this->IsKernel() ? 1 : 4; }
return *m_memory;
}
Common::PageTable& GetImpl() {
return *m_impl;
}
Common::PageTable& GetImpl() const {
return *m_impl;
}
size_t GetNumGuardPages() const {
return this->IsKernel() ? 1 : 4;
}
protected: protected:
// NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions // NOTE: These three functions (Operate, Operate, FinalizeUpdate) are virtual functions
// in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived // in Nintendo's kernel. We devirtualize them, since KPageTable is the only derived

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@ -33,21 +33,10 @@ public:
m_page_table.Finalize(); m_page_table.Finalize();
} }
Core::Memory::Memory& GetMemory() { [[nodiscard]] Core::Memory::Memory& GetMemory() noexcept { return m_page_table.GetMemory(); }
return m_page_table.GetMemory(); [[nodiscard]] Core::Memory::Memory const& GetMemory() const noexcept { return m_page_table.GetMemory(); }
} [[nodiscard]] Common::PageTable& GetImpl() noexcept { return m_page_table.GetImpl(); }
[[nodiscard]] Common::PageTable const& GetImpl() const noexcept { return m_page_table.GetImpl(); }
Core::Memory::Memory& GetMemory() const {
return m_page_table.GetMemory();
}
Common::PageTable& GetImpl() {
return m_page_table.GetImpl();
}
Common::PageTable& GetImpl() const {
return m_page_table.GetImpl();
}
size_t GetNumGuardPages() const { size_t GetNumGuardPages() const {
return m_page_table.GetNumGuardPages(); return m_page_table.GetNumGuardPages();