aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatteo Gmür <matteo.gmuer1@ost.ch>2024-11-01 12:56:22 +0000
committerMatteo Gmür <matteo.gmuer1@ost.ch>2024-11-01 12:56:22 +0000
commitdefb727b2d0ac902e10e9736440779495b8b51a9 (patch)
tree9a3e0992a227eaa6624e69c858a0c18421dae32d
parent1154d641797e8bb51814fad2a618e9e30c3d0685 (diff)
downloadteachos-defb727b2d0ac902e10e9736440779495b8b51a9.tar.xz
teachos-defb727b2d0ac902e10e9736440779495b8b51a9.zip
Move methods into seperate class.
-rw-r--r--arch/x86_64/include/arch/memory/paging/page_mapper.hpp314
-rw-r--r--arch/x86_64/src/memory/paging/page_mapper.cpp21
2 files changed, 184 insertions, 151 deletions
diff --git a/arch/x86_64/include/arch/memory/paging/page_mapper.hpp b/arch/x86_64/include/arch/memory/paging/page_mapper.hpp
index 26b8171..20d9afc 100644
--- a/arch/x86_64/include/arch/memory/paging/page_mapper.hpp
+++ b/arch/x86_64/include/arch/memory/paging/page_mapper.hpp
@@ -11,16 +11,181 @@
namespace teachos::arch::memory::paging
{
- namespace
+ /**
+ * @brief Currently active level 4 page table, is used to ensure there is only ever one valid instance and it cannot
+ * be copied or constructed again.
+ */
+ struct active_page_table
{
/**
+ * @brief Creates a single instance of an active level 4 page table table and returns the created instance or
+ * alternatively returns the previously created instance instead. The instance is owned by this method and is
+ * static, meaning it lives on for the complete lifetime of the program.
+ *
+ * @return Active single unique instance of the level 4 page table.
+ */
+ static auto create_or_get() -> active_page_table &;
+
+ /**
+ * @brief Translates virtual address into corresponding physical address. Calls translate_page under the hood.
+ *
+ * @param address Virtual address we want to translate into physical one.
+ * @return Physical address corresponding to the provided virtual address.
+ */
+ auto translate_address(virtual_address address) -> std::optional<allocator::physical_address>;
+
+ /**
+ * @brief Translates page into physical frame, will first attempt to parse normally using default page size and if
+ * it failed attempt to parse using huge pages.
+ *
+ * @param page Page to translate into physical frame.
+ * @return Physical frame corresponding to the provided virtual page.
+ */
+ auto translate_page(virtual_page page) -> std::optional<allocator::physical_frame>;
+
+ /**
+ * @brief Translates huge page into actual physical frame.
+ *
+ * @param page Page to translate into physical frame.
+ * @return Physical frame corresponding to the provided virtual page.
+ */
+ auto translate_huge_page(virtual_page page) -> std::optional<allocator::physical_frame>;
+
+ /**
+ * @brief Maps a virtual page to a physical frame in the page table with the specified flags.
+ *
+ * @note Allocates and maps an entry in every page level if it does not exists yet down to level 1. If the level 1
+ * page table already exists it halts execution instead.
+ *
+ * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and
+ * deallocate method.
+ * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate
+ * entries when a new page table is required.
+ * @param page Virtual page that is being mapped.
+ * @param frame Physical frame that the virtual page will be mapped to.
+ * @param flags A bitset of flags that configure the page table entry for this mapping.
+ */
+ template<allocator::FrameAllocator T>
+ auto map_page_to_frame(T & allocator, virtual_page page, allocator::physical_frame frame,
+ std::bitset<64U> flags) -> void
+ {
+ auto current_handle = active_handle;
+
+ for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
+ {
+ current_handle = current_handle.next_table_or_create(allocator, page.get_level_index(level));
+ }
+
+ auto & level1_entry = current_handle[page.get_level_index(page_table_handle::LEVEL1)];
+ arch::exception_handling::assert(!level1_entry.contains_flags(entry::HUGE_PAGE),
+ "[Page Mapper] Unable to map huge pages");
+ arch::exception_handling::assert(level1_entry.is_unused(), "[Page Mapper] Page table entry is already used");
+ level1_entry.set_entry(frame, flags.to_ulong() | entry::PRESENT);
+ }
+
+ /**
+ * @brief Allocates the next free frame and then uses that frame to call map_page_to_frame.
+ *
+ * @see map_page_to_frame
+ */
+ template<allocator::FrameAllocator T>
+ auto map_next_free_page_to_frame(T & allocator, virtual_page page, std::bitset<64U> flags) -> void
+ {
+ auto const frame = allocator.allocate_frame();
+ exception_handling::assert(frame.has_value(), "[Page mapper] Out of memory exception");
+ map_page_to_frame(allocator, page, frame.value(), flags);
+ }
+
+ /**
+ * @brief Gets the corresponding page the given frame has to be contained in and uses that to call
+ * map_page_to_frame.
+ *
+ * @see map_page_to_frame
+ */
+ template<allocator::FrameAllocator T>
+ auto identity_map(T & allocator, allocator::physical_frame frame, std::bitset<64U> flags) -> void
+ {
+ auto const page = virtual_page::containing_address(frame.start_address());
+ map_page_to_frame(allocator, page, frame, flags);
+ }
+
+ /**
+ * @brief Unmaps the virtual page from the previously mapped to physical frame and resets the flags.
+ *
+ * @note Deallocates and unmaps the entry in every page level if this page was the last one up to level 4 and
+ * ensures to clear the Translation Lookaside Buffer, so that the unmapped value is removed from cache as well.
+ *
+ * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and
+ * deallocate method.
+ * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate
+ * entries when a new page table is required.
+ * @param page Virtual page that is being unmapped.
+ */
+ template<allocator::FrameAllocator T>
+ auto unmap_page(T & allocator, virtual_page page) -> void
+ {
+ exception_handling::assert(translate_page(page).has_value(),
+ "[Page Mapper] Attempted to unmap page, which has not been mapped previously");
+
+ auto current_handle = active_handle;
+ std::array<page_table_handle, 4U> handles{current_handle, current_handle, current_handle, current_handle};
+
+ for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
+ {
+ auto const level_index = page.get_level_index(level);
+ auto const next_handle = current_handle.next_table(level_index);
+ // The next table method failed even tough the page has to be mapped already, because translate_page did not
+ // fail. This can only mean that we attempted to unmap a huge page, which is not supported in the first place.
+ exception_handling::assert(next_handle.has_value(), "[Page Mapper] Unable to unmap huge pages");
+ current_handle = next_handle.value();
+ // The level is used as the index, because it ensures the first level is the lowest one and then the remaining
+ // levels in ascending order. This is required, because we first have to clear the Level 1 page entry to check
+ // if the Level 1 page is now empty, to clear the Level 2 page entry, which will then also check if the Level 2
+ // page is empty and clear the Level 3 page entry and so on.
+ handles.at(level - 1U) = current_handle;
+ }
+
+ // Unmaps all entries starting from the Level 1 page table, and unmaps higher levels as well if that entry was the
+ // last one. We check if it was the last one using is empty on the page table handle, when we have removed the
+ // page to be unmapped.
+ for (auto & handle : handles)
+ {
+ unmap_page_table_entry(allocator, page, handle);
+ if (!handle.is_empty())
+ {
+ break;
+ }
+ }
+ invalidate_page_cache(page.start_address());
+ }
+
+ private:
+ /**
+ * @brief Private constructor should only be used by create or get method, which ensures to create only ever one
+ * instance.
+ *
+ * @param active_handle Handle to the underlying currently active level 4 page table.
+ */
+ active_page_table(page_table_handle active_handle);
+
+ /**
+ * @brief Deleted copy constructor.
+ */
+ active_page_table(active_page_table const &) = delete;
+
+ /**
+ * @brief Deleted copy assignment operator.
+ */
+ active_page_table & operator=(active_page_table const &) = delete;
+
+ /**
* @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained
* in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction.
*
* @param address Memory address, which will be used to determine the contained page and flush the TLB entry for
* that page.
*/
- auto invalidate_page_cache(virtual_address address) -> void
+ static auto invalidate_page_cache(virtual_address address) -> void
{
asm volatile("invlpg (%0)" ::"r"(address) : "memory");
}
@@ -36,7 +201,7 @@ namespace teachos::arch::memory::paging
* @param handle Page Table handle we want to access the entry that should be cleared on.
*/
template<allocator::FrameAllocator T>
- auto unmap_page_table_entry(T & allocator, virtual_page page, page_table_handle & handle) -> void
+ static auto unmap_page_table_entry(T & allocator, virtual_page page, page_table_handle & handle) -> void
{
auto level_index = page.get_level_index(handle.get_level());
auto & entry = handle[level_index];
@@ -46,148 +211,9 @@ namespace teachos::arch::memory::paging
entry.set_unused();
allocator.deallocate_frame(frame.value());
}
- } // namespace
-
- /**
- * @brief Creates a single instance of the level 4 page table table and returns handle to it or alternatively returns
- * the previously created handle instead. The instance is owned by this method and is static, meaning it lives on for
- * the complete lifetime of the program.
- *
- * @return Handle to the single unique instance of the level 4 page table.
- */
- auto create_or_get() -> page_table_handle;
-
- /**
- * @brief Translates virtual address into corresponding physical address. Calls translate_page under the hood.
- *
- * @param address Virtual address we want to translate into physical one.
- * @return Physical address corresponding to the provided virtual address.
- */
- auto translate_address(virtual_address address) -> std::optional<allocator::physical_address>;
-
- /**
- * @brief Translates page into physical frame, will first attempt to parse normally using default page size and if
- * it failed attempt to parse using huge pages.
- *
- * @param page Page to translate into physical frame.
- * @return Physical frame corresponding to the provided virtual page.
- */
- auto translate_page(virtual_page page) -> std::optional<allocator::physical_frame>;
-
- /**
- * @brief Translates huge page into actual physical frame.
- *
- * @param page Page to translate into physical frame.
- * @return Physical frame corresponding to the provided virtual page.
- */
- auto translate_huge_page(virtual_page page) -> std::optional<allocator::physical_frame>;
-
- /**
- * @brief Maps a virtual page to a physical frame in the page table with the specified flags.
- *
- * @note Allocates and maps an entry in every page level if it does not exists yet down to level 1. If the level 1
- * page table already exists it halts execution instead.
- *
- * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and
- * deallocate method.
- * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate entries
- * when a new page table is required.
- * @param page Virtual page that is being mapped.
- * @param frame Physical frame that the virtual page will be mapped to.
- * @param flags A bitset of flags that configure the page table entry for this mapping.
- */
- template<allocator::FrameAllocator T>
- auto map_page_to_frame(T & allocator, virtual_page page, allocator::physical_frame frame,
- std::bitset<64U> flags) -> void
- {
- auto current_handle = create_or_get();
-
- for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
- {
- current_handle = current_handle.next_table_or_create(allocator, page.get_level_index(level));
- }
- auto & level1_entry = current_handle[page.get_level_index(page_table_handle::LEVEL1)];
- arch::exception_handling::assert(!level1_entry.contains_flags(entry::HUGE_PAGE),
- "[Page Mapper] Unable to map huge pages");
- arch::exception_handling::assert(level1_entry.is_unused(), "[Page Mapper] Page table entry is already used");
- level1_entry.set_entry(frame, flags.to_ulong() | entry::PRESENT);
- }
-
- /**
- * @brief Allocates the next free frame and then uses that frame to call map_page_to_frame.
- *
- * @see map_page_to_frame
- */
- template<allocator::FrameAllocator T>
- auto map_next_free_page_to_frame(T & allocator, virtual_page page, std::bitset<64U> flags) -> void
- {
- auto const frame = allocator.allocate_frame();
- exception_handling::assert(frame.has_value(), "[Page mapper] Out of memory exception");
- map_page_to_frame(allocator, page, frame.value(), flags);
- }
-
- /**
- * @brief Gets the corresponding page the given frame has to be contained in and uses that to call map_page_to_frame.
- *
- * @see map_page_to_frame
- */
- template<allocator::FrameAllocator T>
- auto identity_map(T & allocator, allocator::physical_frame frame, std::bitset<64U> flags) -> void
- {
- auto const page = virtual_page::containing_address(frame.start_address());
- map_page_to_frame(allocator, page, frame, flags);
- }
-
- /**
- * @brief Unmaps the virtual page from the previously mapped to physical frame and resets the flags.
- *
- * @note Deallocates and unmaps the entry in every page level if this page was the last one up to level 4 and ensures
- * to clear the Translation Lookaside Buffer, so that the unmapped value is removed from cache as well.
- *
- * @tparam T Type constraint of the allocator, being that is follows the given concept and contains an allocate and
- * deallocate method.
- * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate entries
- * when a new page table is required.
- * @param page Virtual page that is being unmapped.
- */
- template<allocator::FrameAllocator T>
- auto unmap_page(T & allocator, virtual_page page) -> void
- {
- exception_handling::assert(translate_page(page).has_value(),
- "[Page Mapper] Attempted to unmap page, which has not been mapped previously");
-
- auto current_handle = create_or_get();
- std::array<page_table_handle, 4U> handles{current_handle, current_handle, current_handle, current_handle};
-
- for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
- {
- auto const level_index = page.get_level_index(level);
- auto const next_handle = current_handle.next_table(level_index);
- // The next table method failed even tough the page has to be mapped already, because translate_page did not fail.
- // This can only mean that we attempted to unmap a huge page, which is not supported in the first place.
- exception_handling::assert(next_handle.has_value(), "[Page Mapper] Unable to unmap huge pages");
- current_handle = next_handle.value();
- // The level is used as the index, because it ensures the first level is the lowest one and then the remaining
- // levels in ascending order. This is required, because we first have to clear the Level 1 page entry to check if
- // the Level 1 page is now empty, to clear the Level 2 page entry, which will then also check if the Level 2 page
- // is empty and clear the Level 3 page entry and so on.
- handles.at(level - 1U) = current_handle;
- }
-
- // Unmaps all entries starting from the Level 1 page table, and unmaps higher levels as well if that entry was the
- // last one. We check if it was the last one using is empty on the page table handle, when we have removed the page
- // to be unmapped.
- for (auto & handle : handles)
- {
- unmap_page_table_entry(allocator, page, handle);
- if (!handle.is_empty())
- {
- break;
- }
- }
- invalidate_page_cache(page.start_address());
- }
+ page_table_handle active_handle;
+ };
} // namespace teachos::arch::memory::paging
#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_MAPPER_HPP
diff --git a/arch/x86_64/src/memory/paging/page_mapper.cpp b/arch/x86_64/src/memory/paging/page_mapper.cpp
index 2f2ee6a..30055e8 100644
--- a/arch/x86_64/src/memory/paging/page_mapper.cpp
+++ b/arch/x86_64/src/memory/paging/page_mapper.cpp
@@ -7,14 +7,15 @@ namespace teachos::arch::memory::paging
std::size_t constexpr PAGE_TABLE_LEVEL_4_ADDRESS = 0xffffffff'fffff000;
} // namespace
- auto create_or_get() -> page_table_handle
+ auto active_page_table::create_or_get() -> active_page_table &
{
static page_table_handle active_handle{reinterpret_cast<page_table *>(PAGE_TABLE_LEVEL_4_ADDRESS),
page_table_handle::LEVEL4};
- return active_handle;
+ static active_page_table active_page{active_handle};
+ return active_page;
}
- auto translate_address(virtual_address address) -> std::optional<allocator::physical_address>
+ auto active_page_table::translate_address(virtual_address address) -> std::optional<allocator::physical_address>
{
auto const offset = address % allocator::PAGE_FRAME_SIZE;
auto const page = virtual_page::containing_address(address);
@@ -28,9 +29,9 @@ namespace teachos::arch::memory::paging
return std::nullopt;
}
- auto translate_page(virtual_page page) -> std::optional<allocator::physical_frame>
+ auto active_page_table::translate_page(virtual_page page) -> std::optional<allocator::physical_frame>
{
- auto current_handle = create_or_get();
+ auto current_handle = active_handle;
for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level)
{
@@ -49,9 +50,9 @@ namespace teachos::arch::memory::paging
return level1_entry.calculate_pointed_to_frame();
}
- auto translate_huge_page(virtual_page page) -> std::optional<allocator::physical_frame>
+ auto active_page_table::translate_huge_page(virtual_page page) -> std::optional<allocator::physical_frame>
{
- auto current_handle = create_or_get();
+ auto current_handle = active_handle;
auto level3_handle = current_handle.next_table(page.get_level_index(page_table_handle::LEVEL4));
if (!level3_handle.has_value())
@@ -86,4 +87,10 @@ namespace teachos::arch::memory::paging
}
return std::nullopt;
}
+
+ active_page_table::active_page_table(page_table_handle active_handle)
+ : active_handle(active_handle)
+ {
+ // Nothing to do
+ }
} // namespace teachos::arch::memory::paging