4 changed files with 171 additions and 0 deletions
-
2src/common/CMakeLists.txt
-
7src/common/multi_level_page_table.cpp
-
79src/common/multi_level_page_table.h
-
83src/common/multi_level_page_table.inc
@ -0,0 +1,7 @@ |
|||||
|
#include "common/multi_level_page_table.inc"
|
||||
|
|
||||
|
namespace Common { |
||||
|
template class Common::MultiLevelPageTable<GPUVAddr>; |
||||
|
template class Common::MultiLevelPageTable<VAddr>; |
||||
|
template class Common::MultiLevelPageTable<PAddr>; |
||||
|
} // namespace Common
|
||||
@ -0,0 +1,79 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <type_traits> |
||||
|
#include <utility> |
||||
|
#include <vector> |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
template <typename BaseAddr> |
||||
|
class MultiLevelPageTable final { |
||||
|
public: |
||||
|
constexpr MultiLevelPageTable() = default; |
||||
|
explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits, |
||||
|
std::size_t page_bits); |
||||
|
|
||||
|
~MultiLevelPageTable() noexcept; |
||||
|
|
||||
|
MultiLevelPageTable(const MultiLevelPageTable&) = delete; |
||||
|
MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete; |
||||
|
|
||||
|
MultiLevelPageTable(MultiLevelPageTable&& other) noexcept |
||||
|
: address_space_bits{std::exchange(other.address_space_bits, 0)}, |
||||
|
first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange( |
||||
|
other.page_bits, 0)}, |
||||
|
first_level_shift{std::exchange(other.first_level_shift, 0)}, |
||||
|
first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)}, |
||||
|
first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr, |
||||
|
nullptr)} {} |
||||
|
|
||||
|
MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept { |
||||
|
address_space_bits = std::exchange(other.address_space_bits, 0); |
||||
|
first_level_bits = std::exchange(other.first_level_bits, 0); |
||||
|
page_bits = std::exchange(other.page_bits, 0); |
||||
|
first_level_shift = std::exchange(other.first_level_shift, 0); |
||||
|
first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0); |
||||
|
alloc_size = std::exchange(other.alloc_size, 0); |
||||
|
first_level_map = std::move(other.first_level_map); |
||||
|
base_ptr = std::exchange(other.base_ptr, nullptr); |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
void ReserveRange(u64 start, std::size_t size); |
||||
|
|
||||
|
[[nodiscard]] constexpr const BaseAddr& operator[](std::size_t index) const { |
||||
|
return base_ptr[index]; |
||||
|
} |
||||
|
|
||||
|
[[nodiscard]] constexpr BaseAddr& operator[](std::size_t index) { |
||||
|
return base_ptr[index]; |
||||
|
} |
||||
|
|
||||
|
[[nodiscard]] constexpr BaseAddr* data() { |
||||
|
return base_ptr; |
||||
|
} |
||||
|
|
||||
|
[[nodiscard]] constexpr const BaseAddr* data() const { |
||||
|
return base_ptr; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
void AllocateLevel(u64 level); |
||||
|
|
||||
|
std::size_t address_space_bits{}; |
||||
|
std::size_t first_level_bits{}; |
||||
|
std::size_t page_bits{}; |
||||
|
std::size_t first_level_shift{}; |
||||
|
std::size_t first_level_chunk_size{}; |
||||
|
std::size_t alloc_size{}; |
||||
|
std::vector<void*> first_level_map{}; |
||||
|
BaseAddr* base_ptr{}; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Common |
||||
@ -0,0 +1,83 @@ |
|||||
|
// Copyright 2021 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#ifdef _WIN32 |
||||
|
#include <windows.h> |
||||
|
#else |
||||
|
#include <sys/mman.h> |
||||
|
#endif |
||||
|
|
||||
|
#include "common/assert.h" |
||||
|
#include "common/multi_level_page_table.h" |
||||
|
|
||||
|
namespace Common { |
||||
|
|
||||
|
template <typename BaseAddr> |
||||
|
MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_, |
||||
|
std::size_t first_level_bits_, |
||||
|
std::size_t page_bits_) |
||||
|
: address_space_bits{address_space_bits_}, |
||||
|
first_level_bits{first_level_bits_}, page_bits{page_bits_} { |
||||
|
first_level_shift = address_space_bits - first_level_bits; |
||||
|
first_level_chunk_size = 1ULL << (first_level_shift - page_bits); |
||||
|
alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr); |
||||
|
std::size_t first_level_size = 1ULL << first_level_bits; |
||||
|
first_level_map.resize(first_level_size, nullptr); |
||||
|
#ifdef _WIN32 |
||||
|
void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)}; |
||||
|
#else |
||||
|
void* base{mmap(nullptr, alloc_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; |
||||
|
|
||||
|
if (base == MAP_FAILED) { |
||||
|
base = nullptr; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
ASSERT(base); |
||||
|
base_ptr = reinterpret_cast<BaseAddr*>(base); |
||||
|
} |
||||
|
|
||||
|
template <typename BaseAddr> |
||||
|
MultiLevelPageTable<BaseAddr>::~MultiLevelPageTable() noexcept { |
||||
|
if (!base_ptr) { |
||||
|
return; |
||||
|
} |
||||
|
#ifdef _WIN32 |
||||
|
ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE)); |
||||
|
#else |
||||
|
ASSERT(munmap(base_ptr, alloc_size) == 0); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
template <typename BaseAddr> |
||||
|
void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) { |
||||
|
const u64 new_start = start >> first_level_shift; |
||||
|
const u64 new_end = |
||||
|
(start + size + (first_level_chunk_size << page_bits) - 1) >> first_level_shift; |
||||
|
for (u64 i = new_start; i <= new_end; i++) { |
||||
|
if (!first_level_map[i]) { |
||||
|
AllocateLevel(i); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <typename BaseAddr> |
||||
|
void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) { |
||||
|
void* ptr = reinterpret_cast<char*>(base_ptr) + level * first_level_chunk_size; |
||||
|
#ifdef _WIN32 |
||||
|
void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)}; |
||||
|
#else |
||||
|
void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE, |
||||
|
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)}; |
||||
|
|
||||
|
if (base == MAP_FAILED) { |
||||
|
base = nullptr; |
||||
|
} |
||||
|
#endif |
||||
|
ASSERT(base); |
||||
|
|
||||
|
first_level_map[level] = base; |
||||
|
} |
||||
|
|
||||
|
} // namespace Common |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue