Common: implement MultiLevelPageTable.
This commit is contained in:
parent
3f8e7a5585
commit
b617874724
|
@ -81,6 +81,8 @@ add_library(common STATIC
|
||||||
microprofile.cpp
|
microprofile.cpp
|
||||||
microprofile.h
|
microprofile.h
|
||||||
microprofileui.h
|
microprofileui.h
|
||||||
|
multi_level_page_table.cpp
|
||||||
|
multi_level_page_table.h
|
||||||
nvidia_flags.cpp
|
nvidia_flags.cpp
|
||||||
nvidia_flags.h
|
nvidia_flags.h
|
||||||
page_table.cpp
|
page_table.cpp
|
||||||
|
|
7
src/common/multi_level_page_table.cpp
Normal file
7
src/common/multi_level_page_table.cpp
Normal file
|
@ -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
|
79
src/common/multi_level_page_table.h
Normal file
79
src/common/multi_level_page_table.h
Normal file
|
@ -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
|
83
src/common/multi_level_page_table.inc
Normal file
83
src/common/multi_level_page_table.inc
Normal file
|
@ -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
|
Loading…
Reference in a new issue