multi_level_page_table.inc 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #ifdef _WIN32
  5. #include <windows.h>
  6. #else
  7. #include <sys/mman.h>
  8. #endif
  9. #include "common/assert.h"
  10. #include "common/multi_level_page_table.h"
  11. namespace Common {
  12. template <typename BaseAddr>
  13. MultiLevelPageTable<BaseAddr>::MultiLevelPageTable(std::size_t address_space_bits_,
  14. std::size_t first_level_bits_,
  15. std::size_t page_bits_)
  16. : address_space_bits{address_space_bits_},
  17. first_level_bits{first_level_bits_}, page_bits{page_bits_} {
  18. first_level_shift = address_space_bits - first_level_bits;
  19. first_level_chunk_size = (1ULL << (first_level_shift - page_bits)) * sizeof(BaseAddr);
  20. alloc_size = (1ULL << (address_space_bits - page_bits)) * sizeof(BaseAddr);
  21. std::size_t first_level_size = 1ULL << first_level_bits;
  22. first_level_map.resize(first_level_size, nullptr);
  23. #ifdef _WIN32
  24. void* base{VirtualAlloc(nullptr, alloc_size, MEM_RESERVE, PAGE_READWRITE)};
  25. #else
  26. void* base{mmap(nullptr, alloc_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)};
  27. if (base == MAP_FAILED) {
  28. base = nullptr;
  29. }
  30. #endif
  31. ASSERT(base);
  32. base_ptr = reinterpret_cast<BaseAddr*>(base);
  33. }
  34. template <typename BaseAddr>
  35. MultiLevelPageTable<BaseAddr>::~MultiLevelPageTable() noexcept {
  36. if (!base_ptr) {
  37. return;
  38. }
  39. #ifdef _WIN32
  40. ASSERT(VirtualFree(base_ptr, 0, MEM_RELEASE));
  41. #else
  42. ASSERT(munmap(base_ptr, alloc_size) == 0);
  43. #endif
  44. }
  45. template <typename BaseAddr>
  46. void MultiLevelPageTable<BaseAddr>::ReserveRange(u64 start, std::size_t size) {
  47. const u64 new_start = start >> first_level_shift;
  48. const u64 new_end = (start + size) >> first_level_shift;
  49. for (u64 i = new_start; i <= new_end; i++) {
  50. if (!first_level_map[i]) {
  51. AllocateLevel(i);
  52. }
  53. }
  54. }
  55. template <typename BaseAddr>
  56. void MultiLevelPageTable<BaseAddr>::AllocateLevel(u64 level) {
  57. void* ptr = reinterpret_cast<char *>(base_ptr) + level * first_level_chunk_size;
  58. #ifdef _WIN32
  59. void* base{VirtualAlloc(ptr, first_level_chunk_size, MEM_COMMIT, PAGE_READWRITE)};
  60. #else
  61. void* base{mmap(ptr, first_level_chunk_size, PROT_READ | PROT_WRITE,
  62. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)};
  63. if (base == MAP_FAILED) {
  64. base = nullptr;
  65. }
  66. #endif
  67. ASSERT(base);
  68. first_level_map[level] = base;
  69. }
  70. } // namespace Common