summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/multi_level_page_table.cpp7
-rw-r--r--src/common/multi_level_page_table.h79
-rw-r--r--src/common/multi_level_page_table.inc83
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
3namespace Common {
4template class Common::MultiLevelPageTable<GPUVAddr>;
5template class Common::MultiLevelPageTable<VAddr>;
6template 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
13namespace Common {
14
15template <typename BaseAddr>
16class MultiLevelPageTable final {
17public:
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
66private:
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
14namespace Common {
15
16template <typename BaseAddr>
17MultiLevelPageTable<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
41template <typename BaseAddr>
42MultiLevelPageTable<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
53template <typename BaseAddr>
54void 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
65template <typename BaseAddr>
66void 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