aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/memory/paging_root.cpp
blob: c458093b1c39fb0238469fab3e30543d61ce1f14 (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include "x86_64/memory/paging_root.hpp"

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

#include "x86_64/memory/page_table.hpp"
#include "x86_64/memory/page_utilities.hpp"
#include "x86_64/memory/scoped_mapping.hpp"

#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>

namespace teachos::memory::x86_64
{

  namespace
  {
    constexpr auto PML_RECURSIVE_BASE = std::uintptr_t{0177777'776'776'776'776'0000uz};

    //! Perform the actual mapping of the page, via the recursive page map.
    //!
    //! On any level above PML1, the entries need to not be no_execute, because the image is densely packed. The entries
    //! also need to be writable, since the mapping is being performed through the recursive page map hierarchy. When
    //! setting the final entry in the PML1, the actually desired flags are set as is, with the present bit added, thus
    //! still enforcing non-writability and non-execution of the affected page.
    template<std::size_t Level>
      requires(Level > 1uz && Level < 5uz)
    auto do_map(recursive_page_table<Level> * pml, page page, page_table::entry::flags flags)
    {
      auto index = pml_index<Level>(page);
      flags = flags & ~page_table::entry::flags::no_execute;
      flags = flags | page_table::entry::flags::writable;
      if (!(*pml)[index].present())
      {
        auto new_table_frame = active_allocator().allocate();
        auto mapping = scoped_mapping{page};
        (*pml)[index].frame(new_table_frame.value(), page_table::entry::flags::present | flags);
        auto new_table = std::optional{std::construct_at(*pml->next(index))};
        return new_table;
      }
      (*pml)[index] |= flags;
      return pml->next(index);
    }

    //! Perform the actual PML1 update.
    auto do_map(page_table * pml, page page, frame frame, page_table::entry::flags flags) -> std::optional<std::byte *>
    {
      auto index = pml_index<1>(page);
      if ((*pml)[index].present())
      {
        system::panic("[x86_64:MEM] Tried to map a page that is already mapped");
      }
      (*pml)[index].frame(frame, page_table::entry::flags::present | flags);
      return std::optional{static_cast<std::byte *>(page.start_address())};
    }
  }  // namespace

  auto paging_root::get() -> paging_root &
  {
    auto pml4_address = std::bit_cast<paging_root *>(PML_RECURSIVE_BASE);
    return *pml4_address;
  }

  auto paging_root::translate(linear_address address) const -> std::optional<physical_address>
  {
    auto offset = address.raw() % page::size;
    return translate(page::containing(address)).transform([offset](auto frame) -> auto {
      return physical_address{frame.start_address().raw() + offset};
    });
  }

  auto paging_root::translate(page page) const -> std::optional<frame>
  {
    auto pml3 = next(pml_index<4>(page));

    if (!pml3)
    {
      return std::nullopt;
    }

    auto handle_huge_page = [&] -> std::optional<frame> {
      auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; });
      if (!pml3_entry)
      {
        return std::nullopt;
      }
      else if (pml3_entry->huge())
      {
        auto pml3_entry_frame = *pml3_entry->frame();
        return frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)};
      }

      auto pml2 = (*pml3)->next(pml_index<3>(page));
      auto pml2_entry = pml2.transform([&](auto pml2) -> auto { return (*pml2)[pml_index<2>(page)]; });
      if (!pml2_entry)
      {
        return std::nullopt;
      }
      else if (pml2_entry->huge())
      {
        auto pml2_entry_frame = *pml2_entry->frame();
        return frame{pml2_entry_frame.number() + pml_index<1>(page)};
      }

      return std::nullopt;
    };

    return pml3.and_then([&](auto pml3) -> auto { return pml3->next(pml_index<3>(page)); })
        .and_then([&](auto pml2) -> auto { return pml2->next(pml_index<2>(page)); })
        .and_then([&](auto pml1) -> auto { return (*pml1)[pml_index<1>(page)].frame(); })
        .or_else(handle_huge_page);
  }

  auto paging_root::map(page page, frame frame, page_table::entry::flags flags) -> std::optional<std::byte *>
  {
    return std::optional{this}
        .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); })
        .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); })
        .and_then([&](auto pml) -> auto { return do_map(pml, page, flags); })
        .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); });
  }

  auto paging_root::unmap(page page) -> void
  {
    if (!this->translate(page))
    {
      system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped.");
    }

    auto pml4 = this;
    auto pml3 = pml4->next(pml_index<4>(page)).value();
    auto pml2 = pml3->next(pml_index<3>(page)).value();
    auto pml1 = pml2->next(pml_index<2>(page)).value();

    (*pml1)[pml_index<1>(page)].clear();

    if (pml1->empty())
    {
      auto pml1_frame = (*pml2)[pml_index<2>(page)].frame().value();
      active_allocator().release(pml1_frame);
      (*pml2)[pml_index<2>(page)].clear();
    }

    if (pml2->empty())
    {
      auto pml2_frame = (*pml3)[pml_index<3>(page)].frame().value();
      active_allocator().release(pml2_frame);
      (*pml3)[pml_index<3>(page)].clear();
    }

    if (pml3->empty())
    {
      auto pml3_frame = (*pml4)[pml_index<4>(page)].frame().value();
      active_allocator().release(pml3_frame);
      (*pml4)[pml_index<4>(page)].clear();
    }
  }

}  // namespace teachos::memory::x86_64