aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/memory/higher_half_mapper.cpp
blob: abb54a37619801e0c161f06537268afd847bbec5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "arch/memory/higher_half_mapper.hpp"

#include "kapi/memory.hpp"
#include "kapi/system.hpp"

#include "arch/memory/page_table.hpp"
#include "arch/memory/page_utilities.hpp"

#include <algorithm>
#include <array>
#include <cstddef>
#include <memory>
#include <ranges>
#include <utility>

namespace arch::memory
{

  higher_half_mapper::higher_half_mapper(page_table * root)
      : m_root{root}
  {}

  auto higher_half_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte *
  {
    auto table = get_or_create_page_table(page);
    if (!table)
    {
      return nullptr;
    }

    auto const index = pml_index(1, page);
    auto & entry = (*table)[index];

    if (entry.present())
    {
      kapi::system::panic("[x86_64:MEM] Tried to map a page that is already mapped!");
    }

    entry.frame(frame, to_table_flags(flags) | page_table::entry::flags::present);

    return static_cast<std::byte *>(page.start_address());
  }

  auto higher_half_mapper::unmap(kapi::memory::page page) -> void
  {
    if (!try_unmap(page))
    {
      kapi::system::panic("[x86_64:MEM] Tried to unmap a page that is not mapped!");
    }
  }

  auto higher_half_mapper::try_unmap(kapi::memory::page page) noexcept -> bool
  {
    auto table_path = std::array<std::pair<page_table *, std::size_t>, 4>{};
    table_path[0] = std::pair{m_root, pml_index(4, page)};

    for (auto level = 4uz; level > 1uz; --level)
    {
      auto [table, index] = table_path[4 - level];
      auto & entry = (*table)[index];

      if (!entry.present())
      {
        return false;
      }

      auto next_table = to_higher_half_pointer<page_table>(entry.frame()->start_address());
      auto next_index = pml_index(4 - level - 1, page);
      table_path[4 - level - 1] = std::pair{next_table, next_index};
    }

    std::ranges::for_each(std::views::reverse(table_path), [previous_was_empty = true](auto & step) mutable {
      auto [table, index] = step;
      auto & entry = (*table)[index];
      auto frame = entry.frame();

      if (previous_was_empty)
      {
        entry.clear();
        previous_was_empty = table->empty();
        kapi::memory::get_frame_allocator().release(*frame);
      }
    });

    return true;
  }

  auto higher_half_mapper::get_or_create_page_table(kapi::memory::page page) noexcept -> page_table *
  {
    auto table = m_root;

    for (auto level = 4uz; level > 1uz; --level)
    {
      auto index = pml_index(level, page);
      auto & entry = (*table)[index];

      if (!entry.present())
      {
        auto table_frame = kapi::memory::allocate_frame();
        if (!table_frame)
        {
          return nullptr;
        }

        auto new_table = to_higher_half_pointer<page_table>(table_frame->start_address());
        std::construct_at(new_table);

        auto const flags = page_table::entry::flags::present | page_table::entry::flags::writable |
                           page_table::entry::flags::user_accessible;
        entry.frame(*table_frame, flags);
      }

      table = to_higher_half_pointer<page_table>(entry.frame()->start_address());
    }

    return table;
  }

}  // namespace arch::memory