aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86_64/CMakeLists.txt4
-rw-r--r--arch/x86_64/include/x86_64/memory/page_table.hpp139
-rw-r--r--arch/x86_64/include/x86_64/memory/paging_root.hpp27
-rw-r--r--arch/x86_64/src/kapi/memory.cpp10
-rw-r--r--arch/x86_64/src/memory/page_table.cpp57
-rw-r--r--arch/x86_64/src/memory/paging_root.cpp19
-rw-r--r--kapi/CMakeLists.txt4
-rw-r--r--kapi/include/kapi/memory.hpp1
-rw-r--r--kapi/include/kapi/memory/page.hpp59
9 files changed, 316 insertions, 4 deletions
diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt
index cf85b36..8451945 100644
--- a/arch/x86_64/CMakeLists.txt
+++ b/arch/x86_64/CMakeLists.txt
@@ -24,6 +24,8 @@ target_sources("x86_64" PRIVATE
# Memory management
"src/memory/mmu.cpp"
+ "src/memory/page_table.cpp"
+ "src/memory/paging_root.cpp"
"src/memory/region_allocator.cpp"
# VGA text mode
@@ -48,6 +50,8 @@ target_sources("x86_64" PRIVATE
# Memory management
"include/x86_64/memory/mmu.hpp"
+ "include/x86_64/memory/page_table.hpp"
+ "include/x86_64/memory/paging_root.hpp"
"include/x86_64/memory/region_allocator.hpp"
# VGA I/O
diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp
new file mode 100644
index 0000000..53af5d0
--- /dev/null
+++ b/arch/x86_64/include/x86_64/memory/page_table.hpp
@@ -0,0 +1,139 @@
+#ifndef TEACHOS_X86_64_PAGE_TABLE_HPP
+#define TEACHOS_X86_64_PAGE_TABLE_HPP
+
+#include "kapi/memory.hpp"
+
+#include <array>
+#include <bit>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <utility>
+
+namespace teachos::memory::x86_64
+{
+
+ //! A table containing paging entries.
+ struct page_table
+ {
+ //! The maximum number of entries in this table.
+ constexpr auto static entry_count{512};
+
+ //! A single page table entry
+ struct entry
+ {
+ //! Flags marking the state and configuration of an entry.
+ enum struct flags : std::uint64_t
+ {
+ empty = 0,
+ present = 1uz << 0,
+ writable = 1uz << 1,
+ user_accessible = 1uz << 2,
+ write_through = 1uz << 3,
+ disable_cache = 1uz << 4,
+ accessed = 1uz << 5,
+ dirty = 1uz << 6,
+ huge_page = 1uz << 7,
+ global = 1uz << 8,
+ no_execute = 1uz << 63,
+ };
+
+ entry() = default;
+
+ //! Clear this entry, ensuring all information is set to zero, marking the page represented by this entry as not
+ //! present.
+ auto clear() -> void;
+
+ //! Check if the page represented by this entry is present at the containing page table's level.
+ [[nodiscard]] auto present() const -> bool;
+
+ //! Check if the page represented by this entry is huge (2MiB, or 1GiB, depending on the containing page table's
+ //! level).
+ [[nodiscard]] auto huge() const -> bool;
+
+ //! Get all flags present in this entry.
+ [[nodiscard]] auto all_flags() const -> flags;
+
+ //! Get the frame number associated with this entry, if the referenced page is present.
+ [[nodiscard]] auto frame() const -> std::optional<frame>;
+
+ //! Set the entry to reference the given frame with the given flags.
+ auto frame(struct frame frame, flags flags) -> void;
+
+ private:
+ //! A mask to retrieve, or exclude, the frame number from the raw entry.
+ constexpr auto static frame_number_mask{0x000f'ffff'ffff'f000uz};
+
+ std::uint64_t m_raw{};
+ };
+
+ //! Get the entry at the given index.
+ [[nodiscard]] auto operator[](std::size_t index) -> entry &;
+ [[nodiscard]] auto operator[](std::size_t index) const -> entry const &;
+
+ //! Clear the entire page table, effectively evicting all entries.
+ auto clear() -> void;
+
+ private:
+ std::array<entry, entry_count> m_entries{};
+ };
+
+ constexpr auto operator|(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags
+ {
+ return std::bit_cast<page_table::entry::flags>(std::to_underlying(lhs) | std::to_underlying(rhs));
+ }
+
+ constexpr auto operator&(page_table::entry::flags lhs, page_table::entry::flags rhs) -> page_table::entry::flags
+ {
+ return std::bit_cast<page_table::entry::flags>(std::to_underlying(lhs) & std::to_underlying(rhs));
+ }
+
+ //! A recursively mapped page table.
+ template<std::size_t Level>
+ requires(Level > 0uz && Level < 5uz)
+ struct recursive_page_table : page_table
+ {
+ constexpr auto static next_level = Level - 1uz;
+ constexpr auto static recursive_index = 0776uz;
+
+ //! Get the next lower lever table.
+ [[nodiscard]] auto next(this auto && self, std::size_t index)
+ requires(next_level > 1)
+ {
+ return self.next_address(index).transform([](auto address) -> auto {
+ auto table_pointer = std::bit_cast<recursive_page_table<next_level> *>(address);
+ return &std::forward_like<decltype(self)>(*table_pointer);
+ });
+ }
+
+ //! Get the next lower lever table.
+ [[nodiscard]] auto next(this auto && self, std::size_t index)
+ requires(next_level == 1)
+ {
+ return self.next_address(index).transform([](auto address) -> auto {
+ auto table_pointer = std::bit_cast<page_table *>(address);
+ return &std::forward_like<decltype(self)>(*table_pointer);
+ });
+ }
+
+ private:
+ constexpr auto static level_bits = 9;
+ constexpr auto static high_bit = 48;
+ constexpr auto static offset_bits = 12;
+
+ [[nodiscard]] auto next_address(std::size_t index) const -> std::optional<std::uintptr_t>
+ {
+ if (auto entry = (*this)[index]; entry.present() && !entry.huge())
+ {
+ auto this_address = std::bit_cast<std::uintptr_t>(this);
+ auto next_address = (this_address << level_bits) | 1uz << high_bit | (index << offset_bits);
+ return next_address;
+ }
+
+ return std::nullopt;
+ }
+ };
+
+} // namespace teachos::memory::x86_64
+
+#endif \ No newline at end of file
diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp
new file mode 100644
index 0000000..365e180
--- /dev/null
+++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp
@@ -0,0 +1,27 @@
+#ifndef TEACHOS_X86_64_PAGING_ROOT_HPP
+#define TEACHOS_X86_64_PAGING_ROOT_HPP
+
+#include "kapi/memory.hpp"
+
+#include "x86_64/memory/page_table.hpp"
+
+#include <optional>
+
+namespace teachos::memory::x86_64
+{
+
+ //! The active, recursively mapped, root map (e.g. PML4)
+ struct paging_root : recursive_page_table<4>
+ {
+ auto static get() -> paging_root &;
+
+ [[nodiscard]] auto translate(linear_address address) const -> std::optional<physical_address>;
+ [[nodiscard]] auto translate(page page) const -> std::optional<frame>;
+
+ private:
+ paging_root() = default;
+ };
+
+} // namespace teachos::memory::x86_64
+
+#endif \ No newline at end of file
diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp
index 3848fb8..99dcb5c 100644
--- a/arch/x86_64/src/kapi/memory.cpp
+++ b/arch/x86_64/src/kapi/memory.cpp
@@ -12,6 +12,7 @@
#include <multiboot2/information.hpp>
#include <atomic>
+#include <span>
namespace teachos::memory
{
@@ -25,11 +26,12 @@ namespace teachos::memory
auto create_memory_information() -> x86_64::region_allocator::memory_information
{
auto const & mbi = boot::bootstrap_information.mbi;
+ auto mbi_span = std::span{std::bit_cast<std::byte *>(mbi), mbi->size_bytes()};
+ auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical};
- return {.image_range = std::make_pair(physical_address{&boot::x86_64::_start_physical},
- physical_address{&boot::x86_64::_end_physical}),
- .mbi_range = std::make_pair(physical_address{std::bit_cast<std::byte *>(mbi)},
- physical_address{std::bit_cast<std::byte *>(mbi) + mbi->size_bytes()}),
+ return {.image_range =
+ std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}),
+ .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}),
.memory_map = mbi->memory_map()};
};
diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp
new file mode 100644
index 0000000..c716c5c
--- /dev/null
+++ b/arch/x86_64/src/memory/page_table.cpp
@@ -0,0 +1,57 @@
+#include "x86_64/memory/page_table.hpp"
+
+#include <algorithm>
+
+namespace teachos::memory::x86_64
+{
+
+ auto page_table::entry::clear() -> void
+ {
+ m_raw = 0;
+ }
+
+ auto page_table::entry::present() const -> bool
+ {
+ return (flags() & flags::present) != flags::empty;
+ }
+
+ auto page_table::entry::huge() const -> bool
+ {
+ return (flags() & flags::huge_page) != flags::empty;
+ }
+
+ auto page_table::entry::all_flags() const -> flags
+ {
+ return std::bit_cast<flags>(m_raw & ~frame_number_mask);
+ }
+
+ auto page_table::entry::frame() const -> std::optional<struct frame>
+ {
+ if (present())
+ {
+ return frame::containing(physical_address{m_raw & frame_number_mask});
+ }
+ return std::nullopt;
+ }
+
+ auto page_table::entry::frame(struct frame frame, flags flags) -> void
+ {
+ m_raw = (frame.start_address().raw() | static_cast<std::uint64_t>(flags));
+ };
+
+ auto page_table::operator[](std::size_t index) -> entry &
+ {
+ return m_entries.at(index);
+ }
+
+ auto page_table::operator[](std::size_t index) const -> entry const &
+ {
+ return m_entries.at(index);
+ }
+
+ auto page_table::clear() -> void
+ {
+ std::ranges::for_each(m_entries, &page_table::entry::clear);
+ }
+
+} // namespace teachos::memory::x86_64
diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp
new file mode 100644
index 0000000..1234308
--- /dev/null
+++ b/arch/x86_64/src/memory/paging_root.cpp
@@ -0,0 +1,19 @@
+#include "x86_64/memory/paging_root.hpp"
+
+#include <cstdint>
+
+namespace teachos::memory::x86_64
+{
+
+ namespace
+ {
+ constexpr auto PML_RECURSIVE_BASE = std::uintptr_t{0177777'776'776'776'776'0000uz};
+ }
+
+ auto paging_root::get() -> paging_root &
+ {
+ auto pml4_address = std::bit_cast<paging_root *>(PML_RECURSIVE_BASE);
+ return *pml4_address;
+ }
+
+} // namespace teachos::memory::x86_64 \ No newline at end of file
diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt
index 553b9ba..ca26615 100644
--- a/kapi/CMakeLists.txt
+++ b/kapi/CMakeLists.txt
@@ -8,6 +8,10 @@ target_sources("kapi" PUBLIC
"include/kapi/boot.hpp"
"include/kapi/cio.hpp"
"include/kapi/memory.hpp"
+ "include/kapi/memory/address.hpp"
+ "include/kapi/memory/frame_allocator.hpp"
+ "include/kapi/memory/frame.hpp"
+ "include/kapi/memory/page.hpp"
"include/kapi/system.hpp"
)
diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp
index 3daaa86..4279274 100644
--- a/kapi/include/kapi/memory.hpp
+++ b/kapi/include/kapi/memory.hpp
@@ -4,6 +4,7 @@
#include "kapi/memory/address.hpp" // IWYU pragma: export
#include "kapi/memory/frame.hpp" // IWYU pragma: export
#include "kapi/memory/frame_allocator.hpp" // IWYU pragma: export
+#include "kapi/memory/page.hpp" // IWYU pragma: export
namespace teachos::memory
{
diff --git a/kapi/include/kapi/memory/page.hpp b/kapi/include/kapi/memory/page.hpp
new file mode 100644
index 0000000..2b8e52a
--- /dev/null
+++ b/kapi/include/kapi/memory/page.hpp
@@ -0,0 +1,59 @@
+#ifndef TEACHOS_KAPI_MEMORY_PAGE_HPP
+#define TEACHOS_KAPI_MEMORY_PAGE_HPP
+
+#include "kapi/memory/address.hpp"
+
+#include <compare>
+#include <cstddef>
+
+namespace teachos::memory
+{
+
+ extern std::size_t const PLATFORM_PAGE_SIZE;
+
+ struct page
+ {
+ constexpr page() = default;
+
+ explicit constexpr page(std::size_t number)
+ : m_number{number}
+ {}
+
+ /**
+ * @brief Returns the virtual page the given address is contained in.
+ *
+ * @param address Linear address we want to get the corresponding virtual page for.
+ * @return Page the given address is contained in.
+ */
+ constexpr auto static containing(linear_address address) noexcept -> page
+ {
+ return page{address.raw() / PLATFORM_PAGE_SIZE};
+ }
+
+ /**
+ * @brief Get the start address of this virtual page.
+ *
+ * @return Start address of the virtual page.
+ */
+ [[nodiscard]] constexpr auto start_address() const noexcept -> linear_address
+ {
+ return linear_address{m_number * PLATFORM_PAGE_SIZE};
+ }
+
+ /**
+ * @brief Check if this page refers to the same page as @p other.
+ */
+ constexpr auto operator==(page const & other) const noexcept -> bool = default;
+
+ /**
+ * @brief Lexicographically compare this page to @p other.
+ */
+ constexpr auto operator<=>(page const & other) const noexcept -> std::strong_ordering = default;
+
+ private:
+ std::size_t m_number{};
+ };
+
+} // namespace teachos::memory
+
+#endif \ No newline at end of file