Common: implement MultiLevelPageTable.
This commit is contained in:
		@@ -81,6 +81,8 @@ add_library(common STATIC
 | 
			
		||||
    microprofile.cpp
 | 
			
		||||
    microprofile.h
 | 
			
		||||
    microprofileui.h
 | 
			
		||||
    multi_level_page_table.cpp
 | 
			
		||||
    multi_level_page_table.h
 | 
			
		||||
    nvidia_flags.cpp
 | 
			
		||||
    nvidia_flags.h
 | 
			
		||||
    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
 | 
			
		||||
		Reference in New Issue
	
	Block a user