aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json1
-rw-r--r--arch/x86_64/CMakeLists.txt1
-rw-r--r--arch/x86_64/include/x86_64/memory/kernel_mapper.hpp33
-rw-r--r--arch/x86_64/include/x86_64/memory/page_table.hpp16
-rw-r--r--arch/x86_64/include/x86_64/memory/paging_root.hpp8
-rw-r--r--arch/x86_64/src/kapi/memory.cpp20
-rw-r--r--arch/x86_64/src/memory/kernel_mapper.cpp107
-rw-r--r--arch/x86_64/src/memory/page_table.cpp13
-rw-r--r--arch/x86_64/src/memory/paging_root.cpp55
9 files changed, 247 insertions, 7 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 72a3903..c0f3e5d 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -30,6 +30,7 @@
"iwyu",
"kapi",
"kstd",
+ "memcmp",
"multiboot",
"nolintnextline",
"rdmsr",
diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt
index 7f01744..0b9009c 100644
--- a/arch/x86_64/CMakeLists.txt
+++ b/arch/x86_64/CMakeLists.txt
@@ -23,6 +23,7 @@ target_sources("x86_64" PRIVATE
"src/kapi/memory.cpp"
# Memory management
+ "src/memory/kernel_mapper.cpp"
"src/memory/mmu.cpp"
"src/memory/page_table.cpp"
"src/memory/paging_root.cpp"
diff --git a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp
new file mode 100644
index 0000000..4b681ae
--- /dev/null
+++ b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp
@@ -0,0 +1,33 @@
+#ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP
+#define TEACHOS_X86_64_KERNEL_MAPPER_HPP
+
+#include "kapi/memory.hpp"
+
+#include <elf/format.hpp>
+#include <elf/section_header.hpp>
+#include <multiboot2/information.hpp>
+
+#include <string_view>
+
+namespace teachos::memory::x86_64
+{
+
+ struct kernel_mapper
+ {
+ using section_header_type = elf::section_header<elf::format::elf64>;
+
+ kernel_mapper(frame_allocator & allocator, multiboot2::information_view const * mbi);
+
+ auto remap_kernel() -> void;
+
+ private:
+ auto map_section(section_header_type const & section, std::string_view name) -> void;
+
+ frame_allocator * m_allocator;
+ multiboot2::information_view const * m_mbi;
+ std::uintptr_t m_kernel_load_base;
+ };
+
+} // namespace teachos::memory::x86_64
+
+#endif \ No newline at end of file
diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp
index c5102a2..ce34e23 100644
--- a/arch/x86_64/include/x86_64/memory/page_table.hpp
+++ b/arch/x86_64/include/x86_64/memory/page_table.hpp
@@ -51,6 +51,12 @@ namespace teachos::memory::x86_64
//! Get all flags present in this entry.
[[nodiscard]] auto all_flags() const -> flags;
+ //! Set the flags of this entry to the given set.
+ auto all_flags(flags flags) -> void;
+
+ //! Additively set the given flags in this entry.
+ auto operator|=(flags rhs) -> entry &;
+
//! Get the frame number associated with this entry, if the referenced page is present.
[[nodiscard]] auto frame() const -> std::optional<frame>;
@@ -86,11 +92,21 @@ namespace teachos::memory::x86_64
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 lhs = lhs | 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));
}
+ constexpr auto operator~(page_table::entry::flags flags)
+ {
+ return std::bit_cast<page_table::entry::flags>(~std::to_underlying(flags));
+ }
+
//! A recursively mapped page table.
template<std::size_t Level>
requires(Level > 0uz && Level < 5uz)
diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp
index a9ab6a6..d4aa023 100644
--- a/arch/x86_64/include/x86_64/memory/paging_root.hpp
+++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp
@@ -25,6 +25,14 @@ namespace teachos::memory::x86_64
[[nodiscard]] auto translate(linear_address address) const -> std::optional<physical_address>;
[[nodiscard]] auto translate(page page) const -> std::optional<frame>;
+ //! Map the given page into the given frame using the given flags.
+ //!
+ //! @param page A page to map.
+ //! @param frame The frame into which to map the page.
+ //! @param flags The flags to apply to the mapping.
+ //! @param allocator The frame allocator used to allocate any required page tables.
+ auto map(page page, frame frame, entry::flags flags, frame_allocator & allocator) -> std::optional<std::byte *>;
+
private:
paging_root() = default;
};
diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp
index d09a4c1..ed3edef 100644
--- a/arch/x86_64/src/kapi/memory.cpp
+++ b/arch/x86_64/src/kapi/memory.cpp
@@ -1,11 +1,14 @@
#include "kapi/memory.hpp"
+#include "kapi/cio.hpp"
#include "kapi/system.hpp"
#include "x86_64/boot/boot.hpp"
#include "x86_64/boot/ld.hpp"
+#include "x86_64/cpu/impl/control_registers.hpp"
#include "x86_64/cpu/registers.hpp"
#include "x86_64/memory/buffered_allocator.hpp"
+#include "x86_64/memory/kernel_mapper.hpp"
#include "x86_64/memory/mmu.hpp"
#include "x86_64/memory/page_table.hpp"
#include "x86_64/memory/page_utilities.hpp"
@@ -60,7 +63,7 @@ namespace teachos::memory
}
//! Inject, or graft, a faux recursive PML4 into the active page mapping structure.
- auto inject_faux_pml4(frame_allocator & allocator) -> void
+ auto inject_faux_pml4(frame_allocator & allocator)
{
using namespace x86_64;
using entry_flags = page_table::entry::flags;
@@ -105,6 +108,8 @@ namespace teachos::memory
auto new_pml1 = (**new_pml2).next(pml2_index);
(**new_pml1)[pml1_index] = pml1_entry;
+
+ return *new_pml4_frame;
}
} // namespace
@@ -131,12 +136,15 @@ namespace teachos::memory
enable_cpu_protections();
auto allocation_buffer = x86_64::buffered_allocator<4>{&allocator};
- inject_faux_pml4(allocation_buffer);
+ auto new_pml4_frame = inject_faux_pml4(allocation_buffer);
- // paging::kernel_mapper kernel(allocator, memory_information);
- // kernel.remap_kernel();
- // video::vga::text::write("Kernel remapping successful", video::vga::text::common_attributes::green_on_black);
- // video::vga::text::newline();
+ auto mapper = x86_64::kernel_mapper{allocation_buffer, boot::bootstrap_information.mbi};
+ mapper.remap_kernel();
+ cio::println("[x86_64:MEM] prepared new kernel image page maps.");
+
+ auto cr3 = cpu::x86_64::cr3::read();
+ cr3.address(new_pml4_frame.start_address());
+ cpu::x86_64::cr3::write(cr3);
// remap_heap(heap::KERNEL_HEAP_START, heap::KERNEL_HEAP_SIZE);
// video::vga::text::write("Heap remapping successful", video::vga::text::common_attributes::green_on_black);
diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp
new file mode 100644
index 0000000..b1d12a4
--- /dev/null
+++ b/arch/x86_64/src/memory/kernel_mapper.cpp
@@ -0,0 +1,107 @@
+#include "x86_64/memory/kernel_mapper.hpp"
+
+#include "kapi/cio.hpp"
+#include "kapi/memory.hpp"
+#include "kapi/system.hpp"
+
+#include "x86_64/boot/ld.hpp"
+#include "x86_64/memory/page_table.hpp"
+#include "x86_64/memory/paging_root.hpp"
+
+#include <elf/format.hpp>
+#include <elf/section_header.hpp>
+
+#include <algorithm>
+#include <ranges>
+
+namespace teachos::memory::x86_64
+{
+
+ inline namespace
+ {
+ using namespace std::string_view_literals;
+
+ constexpr auto static ignored_section_prefixes = std::array{
+ ".boot_"sv,
+ };
+
+ constexpr auto static user_accessible_prefixes = std::array{
+ ".user"sv,
+ ".stl"sv,
+ };
+
+ } // namespace
+
+ kernel_mapper::kernel_mapper(frame_allocator & allocator, multiboot2::information_view const * mbi)
+ : m_allocator{&allocator}
+ , m_mbi(std::move(mbi))
+ , m_kernel_load_base{std::bit_cast<std::uintptr_t>(&boot::x86_64::TEACHOS_VMA)}
+ {}
+
+ auto kernel_mapper::remap_kernel() -> void
+ {
+ auto elf_information = m_mbi->maybe_elf_symbols<elf::format::elf64>();
+ if (!elf_information)
+ {
+ system::panic("[x86_64:MEM] ELF section information is not available.");
+ }
+
+ auto sections = *elf_information;
+ auto allocated_sections =
+ std::views::all(sections) | std::views::filter(&elf::section_header<elf::format::elf64>::allocated) |
+ std::views::filter([&](auto const & section) -> auto {
+ auto name = sections.name(section);
+ return !std::ranges::any_of(ignored_section_prefixes,
+ [&](auto const & prefix) -> auto { return name.starts_with(prefix); });
+ });
+
+ if (allocated_sections.empty())
+ {
+ system::panic("[x86_64:MEM] No allocated ELF sections were found.");
+ }
+
+ std::ranges::for_each(allocated_sections,
+ [&](auto const & section) -> auto { map_section(section, sections.name(section)); });
+ }
+
+ auto kernel_mapper::map_section(section_header_type const & section, std::string_view name) -> void
+ {
+ cio::print("[x86_64:MEM] mapping ");
+ cio::println(name);
+
+ auto number_of_pages = (section.size + (PLATFORM_PAGE_SIZE - 1)) / PLATFORM_PAGE_SIZE;
+
+ auto linear_start_address = linear_address{section.virtual_load_address};
+ auto physical_start_address = physical_address{section.virtual_load_address & ~m_kernel_load_base};
+
+ auto first_page = page::containing(linear_start_address);
+ auto first_frame = frame::containing(physical_start_address);
+
+ auto page_flags = page_table::entry::flags::empty;
+
+ if (section.writable())
+ {
+ page_flags |= page_table::entry::flags::writable;
+ }
+
+ if (!section.executable())
+ {
+ page_flags |= page_table::entry::flags::no_execute;
+ }
+
+ auto is_prefix_of_name = [=](auto prefix) -> bool {
+ return name.starts_with(prefix);
+ };
+
+ if (std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name))
+ {
+ page_flags |= page_table::entry::flags::user_accessible;
+ }
+
+ for (auto i = 0uz; i < number_of_pages; ++i)
+ {
+ paging_root::get().map(page{first_page.number() + i}, frame{first_frame.number() + i}, page_flags, *m_allocator);
+ }
+ }
+
+} // namespace teachos::memory::x86_64 \ No newline at end of file
diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp
index 60bf94d..e797b22 100644
--- a/arch/x86_64/src/memory/page_table.cpp
+++ b/arch/x86_64/src/memory/page_table.cpp
@@ -1,6 +1,7 @@
#include "x86_64/memory/page_table.hpp"
#include <algorithm>
+#include <utility>
namespace teachos::memory::x86_64
{
@@ -25,6 +26,18 @@ namespace teachos::memory::x86_64
return std::bit_cast<flags>(m_raw & ~frame_number_mask);
}
+ auto page_table::entry::all_flags(flags flags) -> void
+ {
+ m_raw = (m_raw & ~frame_number_mask) | std::to_underlying(flags);
+ }
+
+ auto page_table::entry::operator|=(flags rhs) -> page_table::entry &
+ {
+ auto raw_flags = std::to_underlying(rhs) & ~frame_number_mask;
+ m_raw |= raw_flags;
+ return *this;
+ }
+
auto page_table::entry::frame() const -> std::optional<struct frame>
{
if (present())
diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp
index 6b8e1ab..5ca2bf0 100644
--- a/arch/x86_64/src/memory/paging_root.cpp
+++ b/arch/x86_64/src/memory/paging_root.cpp
@@ -1,10 +1,15 @@
#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
@@ -13,7 +18,45 @@ 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,
+ frame_allocator & allocator)
+ {
+ 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 = allocator.allocate();
+ auto mapping = scoped_mapping{page, allocator};
+ (*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 &
{
@@ -71,4 +114,14 @@ namespace teachos::memory::x86_64
.or_else(handle_huge_page);
}
+ auto paging_root::map(page page, frame frame, page_table::entry::flags flags, frame_allocator & allocator)
+ -> std::optional<std::byte *>
+ {
+ return std::optional{this}
+ .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); })
+ .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); })
+ .and_then([&](auto pml) -> auto { return do_map(pml, page, flags, allocator); })
+ .and_then([&](auto pml) -> auto { return do_map(pml, page, frame, flags); });
+ }
+
} // namespace teachos::memory::x86_64 \ No newline at end of file