diff options
| -rw-r--r-- | .vscode/settings.json | 1 | ||||
| -rw-r--r-- | arch/x86_64/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/kernel_mapper.hpp | 33 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/page_table.hpp | 16 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/paging_root.hpp | 8 | ||||
| -rw-r--r-- | arch/x86_64/src/kapi/memory.cpp | 20 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/kernel_mapper.cpp | 107 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/page_table.cpp | 13 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/paging_root.cpp | 55 |
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 |
