diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/common/multi_level_page_table.cpp | 7 | ||||
| -rw-r--r-- | src/common/multi_level_page_table.h | 79 | ||||
| -rw-r--r-- | src/common/multi_level_page_table.inc | 83 |
4 files changed, 171 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3447fabd8..2db414819 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -81,6 +81,8 @@ add_library(common STATIC | |||
| 81 | microprofile.cpp | 81 | microprofile.cpp |
| 82 | microprofile.h | 82 | microprofile.h |
| 83 | microprofileui.h | 83 | microprofileui.h |
| 84 | multi_level_page_table.cpp | ||
| 85 | multi_level_page_table.h | ||
| 84 | nvidia_flags.cpp | 86 | nvidia_flags.cpp |
| 85 | nvidia_flags.h | 87 | nvidia_flags.h |
| 86 | page_table.cpp | 88 | page_table.cpp |
diff --git a/src/common/multi_level_page_table.cpp b/src/common/multi_level_page_table.cpp new file mode 100644 index 000000000..561785ca7 --- /dev/null +++ b/src/common/multi_level_page_table.cpp | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #include "common/multi_level_page_table.inc" | ||
| 2 | |||
| 3 | namespace Common { | ||
| 4 | template class Common::MultiLevelPageTable<GPUVAddr>; | ||
| 5 | template class Common::MultiLevelPageTable<VAddr>; | ||
| 6 | template class Common::MultiLevelPageTable<PAddr>; | ||
| 7 | } // namespace Common | ||
diff --git a/src/common/multi_level_page_table.h b/src/common/multi_level_page_table.h new file mode 100644 index 000000000..dde1cc962 --- /dev/null +++ b/src/common/multi_level_page_table.h | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <type_traits> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Common { | ||
| 14 | |||
| 15 | template <typename BaseAddr> | ||
| 16 | class MultiLevelPageTable final { | ||
| 17 | public: | ||
| 18 | constexpr MultiLevelPageTable() = default; | ||
| 19 | explicit MultiLevelPageTable(std::size_t address_space_bits, std::size_t first_level_bits, | ||
| 20 | std::size_t page_bits); | ||
| 21 | |||
| 22 | ~MultiLevelPageTable() noexcept; | ||
| 23 | |||
| 24 | MultiLevelPageTable(const MultiLevelPageTable&) = delete; | ||
| 25 | MultiLevelPageTable& operator=(const MultiLevelPageTable&) = delete; | ||
| 26 | |||
| 27 | MultiLevelPageTable(MultiLevelPageTable&& other) noexcept | ||
| 28 | : address_space_bits{std::exchange(other.address_space_bits, 0)}, | ||
| 29 | first_level_bits{std::exchange(other.first_level_bits, 0)}, page_bits{std::exchange( | ||
| 30 | other.page_bits, 0)}, | ||
| 31 | first_level_shift{std::exchange(other.first_level_shift, 0)}, | ||
| 32 | first_level_chunk_size{std::exchange(other.first_level_chunk_size, 0)}, | ||
| 33 | first_level_map{std::move(other.first_level_map)}, base_ptr{std::exchange(other.base_ptr, | ||
| 34 | nullptr)} {} | ||
| 35 | |||
| 36 | MultiLevelPageTable& operator=(MultiLevelPageTable&& other) noexcept { | ||
| 37 | address_space_bits = std::exchange(other.address_space_bits, 0); | ||
| 38 | first_level_bits = std::exchange(other.first_level_bits, 0); | ||
| 39 | page_bits = std::exchange(other.page_bits, 0); | ||
| 40 | first_level_shift = std::exchange(other.first_level_shift, 0); | ||
| 41 | first_level_chunk_size = std::exchange(other.first_level_chunk_size, 0); | ||
| 42 | alloc_size = std::exchange(other.alloc_size, 0); | ||
| 43 | first_level_map = std::move(other.first_level_map); | ||
| 44 | base_ptr = std::exchange(other.base_ptr, nullptr); | ||
| 45 | return *this; | ||
| 46 | } | ||
| 47 | |||
| 48 | void ReserveRange(u64 start, std::size_t size); | ||
| 49 | |||
| 50 | [[nodiscard]] constexpr const BaseAddr& operator[](std::size_t index) const { | ||
| 51 | return base_ptr[index]; | ||
| 52 | } | ||
| 53 | |||
| 54 | [[nodiscard]] constexpr BaseAddr& operator[](std::size_t index) { | ||
| 55 | return base_ptr[index]; | ||
| 56 | } | ||
| 57 | |||
| 58 | [[nodiscard]] constexpr BaseAddr* data() { | ||
| 59 | return base_ptr; | ||
| 60 | } | ||
| 61 | |||
| 62 | [[nodiscard]] constexpr const BaseAddr* data() const { | ||
| 63 | return base_ptr; | ||
| 64 | } | ||
| 65 | |||
| 66 | private: | ||
| 67 | void AllocateLevel(u64 level); | ||
| 68 | |||
| 69 | std::size_t address_space_bits{}; | ||
| 70 | std::size_t first_level_bits{}; | ||
| 71 | std::size_t page_bits{}; | ||
| 72 | std::size_t first_level_shift{}; | ||
| 73 | std::size_t first_level_chunk_size{}; | ||
| 74 | std::size_t alloc_size{}; | ||
| 75 | std::vector<void*> first_level_map{}; | ||
| 76 | BaseAddr* base_ptr{}; | ||
| 77 | }; | ||
| 78 | |||
| 79 | } // namespace Common | ||
diff --git a/src/common/multi_level_page_table.inc b/src/common/multi_level_page_table.inc new file mode 100644 index 000000000..a75e61f9d --- /dev/null +++ b/src/common/multi_level_page_table.inc | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifdef _WIN32 | ||
| 6 | #include <windows.h> | ||
| 7 | #else | ||
| 8 | #include <sys/mman.h> | ||
| 9 | #endif | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/multi_level_page_table.h" | ||
| 13 | |||
| 14 | namespace Common { | ||
| 15 | |||
| 16 | template <typename BaseAddr> | ||
| 17 | MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_, | ||
| 18 | std::size_t first_level_bits_, | ||
| 19 | std::size_t page_bits_) | ||
| 20 | : address_space_bits{address_space_bits_}, | ||
| 21 | first_level_bits{first_level_bits_}, page_bits{page_bits_} { | ||
| 22 | first_level_shift = address_space_bits - first_level_bits; | ||
| 23 | first_level_chunk_size = 1ULL << (first_level_shift - page_bits); | ||
| 24 | alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr); | ||
| 25 | std::size_t first_level_size = 1ULL << first_level_bits; | ||
| 26 | first_level_map.resize(first_level_size, nullptr); | ||
| 27 | #ifdef _WIN32 | ||
| 28 | void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)}; | ||
| 29 | #else | ||
| 30 | void* base{mmap(nullptr, alloc_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)}; | ||
| 31 | |||
| 32 | if (base == MAP_FAILED) { | ||
| 33 | base = nullptr; | ||
| 34 | } | ||
| 35 | #endif | ||
| 36 | |||
| 37 | ASSERT(base); | ||
| 38 | base_ptr = reinterpret_cast<BaseAddr*>(base); | ||
| 39 | } | ||
| 40 | |||
| 41 | template <typename BaseAddr> | ||
| 42 | MultiLevelPageTable<BaseAddr>::~MultiLevelPageTable() noexcept { | ||
| 43 | if (!base_ptr) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | #ifdef _WIN32 | ||
| 47 | ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE)); | ||
| 48 | #else | ||
| 49 | ASSERT(munmap(base_ptr, alloc_size) == 0); | ||
| 50 | #endif | ||
| 51 | } | ||
| 52 | |||
| 53 | template <typename BaseAddr> | ||
| 54 | void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) { | ||
| 55 | const u64 new_start = start >> first_level_shift; | ||
| 56 | const u64 new_end = | ||
| 57 | (start + size + (first_level_chunk_size << page_bits) - 1) >> first_level_shift; | ||
| 58 | for (u64 i = new_start; i <= new_end; i++) { | ||
| 59 | if (!first_level_map[i]) { | ||
| 60 | AllocateLevel(i); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | template <typename BaseAddr> | ||
| 66 | void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) { | ||
| 67 | void* ptr = reinterpret_cast<char*>(base_ptr) + level * first_level_chunk_size; | ||
| 68 | #ifdef _WIN32 | ||
| 69 | void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)}; | ||
| 70 | #else | ||
| 71 | void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE, | ||
| 72 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)}; | ||
| 73 | |||
| 74 | if (base == MAP_FAILED) { | ||
| 75 | base = nullptr; | ||
| 76 | } | ||
| 77 | #endif | ||
| 78 | ASSERT(base); | ||
| 79 | |||
| 80 | first_level_map[level] = base; | ||
| 81 | } | ||
| 82 | |||
| 83 | } // namespace Common | ||