From fde3f969c5c45d4cdbd5ec92c4d07fd157194300 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 19 Nov 2024 09:38:44 +0100 Subject: memory: fix kernel remapping --- .../arch/memory/paging/active_page_table.hpp | 23 ++-------------------- .../include/arch/memory/paging/kernel_mapper.hpp | 23 +++++++++++----------- 2 files changed, 14 insertions(+), 32 deletions(-) (limited to 'arch') diff --git a/arch/x86_64/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp index a60b3ad..8b12800 100644 --- a/arch/x86_64/include/arch/memory/paging/active_page_table.hpp +++ b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp @@ -136,7 +136,6 @@ namespace teachos::arch::memory::paging "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); auto current_handle = active_handle; - std::array handles{current_handle, current_handle, current_handle}; for (auto level = page_table_handle::LEVEL4; level != page_table_handle::LEVEL1; --level) { @@ -146,28 +145,10 @@ namespace teachos::arch::memory::paging // 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; - } - } - // Uses flush all instead of cpu::tlb_flush(page.start_address());, because when we unmap the active page and - // only flush one page, the rest of the page which is huge still exist in the cache and - // can therefore still be accessed. But because they are huge pages the new mapping can not be accessed correctly. - cpu::tlb_flush_all(); + unmap_page_table_entry(allocator, page, current_handle); + cpu::tlb_flush(page.start_address()); } private: diff --git a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp index bcc3eba..f980451 100644 --- a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp +++ b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp @@ -40,17 +40,17 @@ namespace teachos::arch::memory::paging */ auto remap_kernel() -> active_page_table & { - /*temporary_page temporary_page{virtual_page{0xCAFEBABE}, allocator};*/ + temporary_page temporary_page{virtual_page{0xCAFEBABE}, allocator}; auto & active_table = active_page_table::create_or_get(); - /*auto const frame = allocator.allocate_frame(); + auto const frame = allocator.allocate_frame(); exception_handling::assert(frame.has_value(), "[Kernel Mapper] Frame could not be allocated and therefore kernel not mapped"); - inactive_page_table new_table{frame.value(), active_table, temporary_page};*/ - remap_elf_kernel_sections(active_table); - /*auto const old_table = switch_active_page_table(new_table); + inactive_page_table new_table{frame.value(), active_table, temporary_page}; + remap_elf_kernel_sections(new_table, temporary_page, active_table); + auto const old_table = switch_active_page_table(new_table); auto const old_level_4_page = virtual_page::containing_address(old_table.page_table_level_4_frame.start_address()); - active_table.unmap_page(allocator, old_level_4_page);*/ + active_table.unmap_page(allocator, old_level_4_page); return active_table; } @@ -72,19 +72,20 @@ namespace teachos::arch::memory::paging * @param active_table Active level 4 page table that has its recursive mapping overwritten temporarily and then * restored once the process is finished. */ - auto remap_elf_kernel_sections(active_page_table & active_table) -> void + auto remap_elf_kernel_sections(inactive_page_table & inactive_table, temporary_page & temporary_page, + active_page_table & active_table) -> void { - /*auto const backup = + auto const backup = allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3)); auto page_table_level4 = temporary_page.map_table_frame(backup, active_table); - active_table[511].set_entry(inactive_table.page_table_level_4_frame, entry::PRESENT | entry::WRITABLE);*/ + active_table[511].set_entry(inactive_table.page_table_level_4_frame, entry::PRESENT | entry::WRITABLE); cpu::tlb_flush_all(); map_elf_kernel_sections(active_table); - /*page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE);*/ + page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE); cpu::tlb_flush_all(); - /*temporary_page.unmap_page(active_table);*/ + temporary_page.unmap_page(active_table); } /** -- cgit v1.2.3 From 27cd88e4d39489a845483e574f79fd67ef3d4fcd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 19 Nov 2024 17:30:53 +0100 Subject: runtime: catch pure virtual function calls --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/src/exception_handling/pure_virtual.cpp | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 arch/x86_64/src/exception_handling/pure_virtual.cpp (limited to 'arch') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index f868b4e..1444054 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -65,6 +65,7 @@ target_sources("_exception" PRIVATE "src/exception_handling/assert.cpp" "src/exception_handling/abort.cpp" "src/exception_handling/panic.cpp" + "src/exception_handling/pure_virtual.cpp" ) #[============================================================================[ diff --git a/arch/x86_64/src/exception_handling/pure_virtual.cpp b/arch/x86_64/src/exception_handling/pure_virtual.cpp new file mode 100644 index 0000000..67772f7 --- /dev/null +++ b/arch/x86_64/src/exception_handling/pure_virtual.cpp @@ -0,0 +1,6 @@ +#include "arch/exception_handling/panic.hpp" + +extern "C" auto __cxa_pure_virtual() -> void +{ + teachos::arch::exception_handling::panic("Runtime", "Tried to call a pure virtual function!"); +} -- cgit v1.2.3 From 1cd666241b59b800818812220e28b8b8572e4263 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 19 Nov 2024 17:32:02 +0100 Subject: paging: de-templetize implementation --- arch/x86_64/CMakeLists.txt | 1 + .../arch/memory/allocator/area_frame_allocator.hpp | 7 +- .../include/arch/memory/allocator/concept.hpp | 22 ----- .../arch/memory/allocator/frame_allocator.hpp | 19 +++++ .../arch/memory/allocator/tiny_frame_allocator.hpp | 11 ++- .../arch/memory/paging/active_page_table.hpp | 83 +++---------------- .../include/arch/memory/paging/kernel_mapper.hpp | 94 +++------------------- .../include/arch/memory/paging/page_table.hpp | 23 +----- .../include/arch/memory/paging/temporary_page.hpp | 8 +- arch/x86_64/src/kernel/main.cpp | 1 + .../src/memory/allocator/area_frame_allocator.cpp | 2 +- .../src/memory/allocator/tiny_frame_allocator.cpp | 4 +- .../x86_64/src/memory/paging/active_page_table.cpp | 67 +++++++++++++++ arch/x86_64/src/memory/paging/kernel_mapper.cpp | 94 ++++++++++++++++++++++ arch/x86_64/src/memory/paging/page_table.cpp | 21 +++++ arch/x86_64/src/memory/paging/temporary_page.cpp | 9 ++- 16 files changed, 249 insertions(+), 217 deletions(-) delete mode 100644 arch/x86_64/include/arch/memory/allocator/concept.hpp create mode 100644 arch/x86_64/include/arch/memory/allocator/frame_allocator.hpp create mode 100644 arch/x86_64/src/memory/paging/kernel_mapper.cpp (limited to 'arch') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 1444054..7f3a203 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -52,6 +52,7 @@ target_sources("_memory" PRIVATE "src/memory/paging/virtual_page.cpp" "src/memory/paging/active_page_table.cpp" "src/memory/paging/inactive_page_table.cpp" + "src/memory/paging/kernel_mapper.cpp" "src/memory/cpu/tlb.cpp" "src/memory/cpu/control_register.cpp" "src/memory/cpu/msr.cpp" diff --git a/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp index f2b77f8..f1a3a64 100644 --- a/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp +++ b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp @@ -1,6 +1,7 @@ #ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP #define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP +#include "arch/memory/allocator/frame_allocator.hpp" #include "arch/memory/allocator/physical_frame.hpp" #include "arch/memory/multiboot/reader.hpp" @@ -11,7 +12,7 @@ namespace teachos::arch::memory::allocator /** * @brief Allocates memory using memory areas read from the multiboot2 information pointer. */ - struct area_frame_allocator + struct area_frame_allocator : frame_allocator { /** * @brief Constructor @@ -32,7 +33,7 @@ namespace teachos::arch::memory::allocator * * @return next free physical frame or nullopt if none was found. */ - auto allocate_frame() -> std::optional; + auto allocate_frame() -> std::optional override; /** * @brief Deallocates a previously allocated physical frame. @@ -43,7 +44,7 @@ namespace teachos::arch::memory::allocator * * @param physical_frame Previously allocated physical_frame that should be deallocated. */ - auto deallocate_frame(physical_frame physical_frame) -> void; + auto deallocate_frame(physical_frame const & physical_frame) -> void override; private: /** diff --git a/arch/x86_64/include/arch/memory/allocator/concept.hpp b/arch/x86_64/include/arch/memory/allocator/concept.hpp deleted file mode 100644 index 4a7ab72..0000000 --- a/arch/x86_64/include/arch/memory/allocator/concept.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP - -#include "arch/memory/allocator/physical_frame.hpp" - -#include - -namespace teachos::arch::memory::allocator -{ - /** - * @brief Frame allocator concept - * - * @tparam T - */ - template - concept FrameAllocator = requires(T t, physical_frame a) { - { t.allocate_frame() } -> std::same_as>; - { t.deallocate_frame(a) } -> std::same_as; - }; -} // namespace teachos::arch::memory::allocator - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_CONCEPT_HPP diff --git a/arch/x86_64/include/arch/memory/allocator/frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/frame_allocator.hpp new file mode 100644 index 0000000..9316ca9 --- /dev/null +++ b/arch/x86_64/include/arch/memory/allocator/frame_allocator.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_FRAME_ALLOCATOR_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_FRAME_ALLOCATOR_HPP + +#include "arch/memory/allocator/physical_frame.hpp" + +#include + +namespace teachos::arch::memory::allocator +{ + + struct frame_allocator + { + auto virtual allocate_frame() -> std::optional = 0; + auto virtual deallocate_frame(physical_frame const & frame) -> void = 0; + }; // namespace teachos::arch::memory::allocator + +} // namespace teachos::arch::memory::allocator + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp b/arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp index a96b743..c449ac8 100644 --- a/arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp +++ b/arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp @@ -1,8 +1,7 @@ #ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP #define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_TINY_FRAME_ALLOCATOR_HPP -#include "arch/memory/allocator/area_frame_allocator.hpp" -#include "arch/memory/allocator/concept.hpp" +#include "arch/memory/allocator/frame_allocator.hpp" #include "arch/memory/allocator/physical_frame.hpp" #include @@ -17,7 +16,7 @@ namespace teachos::arch::memory::allocator /** * @brief Allocates memory using memory areas read from the multiboot2 information pointer. */ - struct tiny_frame_allocator + struct tiny_frame_allocator : frame_allocator { /** * @brief Constructor. @@ -27,7 +26,7 @@ namespace teachos::arch::memory::allocator * @param allocator Reference to an allocator following the FrameAllocator concept, which is used to allocate * entries *when a new page table is required. */ - tiny_frame_allocator(area_frame_allocator & allocator); + tiny_frame_allocator(frame_allocator & allocator); /** * @brief Allocate memory by finding and returning one of the three free physical frames. @@ -35,7 +34,7 @@ namespace teachos::arch::memory::allocator * @return First free physical frames of the three frames held by this allocator or nullopt if we used up all three * frames already. */ - auto allocate_frame() -> std::optional; + auto allocate_frame() -> std::optional override; /** * @brief Deallocates one of the three previously allocated physical frames. @@ -45,7 +44,7 @@ namespace teachos::arch::memory::allocator * * @param physical_frame Previously allocated physical_frame that should be deallocated. */ - auto deallocate_frame(physical_frame physical_frame) -> void; + auto deallocate_frame(physical_frame const & physical_frame) -> void override; private: std::array, TINY_ALLOCATOR_FRAMES_COUNT> frames = diff --git a/arch/x86_64/include/arch/memory/paging/active_page_table.hpp b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp index 8b12800..e558fa5 100644 --- a/arch/x86_64/include/arch/memory/paging/active_page_table.hpp +++ b/arch/x86_64/include/arch/memory/paging/active_page_table.hpp @@ -1,12 +1,13 @@ #ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP #define TEACHOS_ARCH_X86_64_MEMORY_PAGING_ACTIVE_PAGE_TABLE_HPP -#include "arch/exception_handling/assert.hpp" -#include "arch/memory/allocator/concept.hpp" -#include "arch/memory/cpu/tlb.hpp" +#include "arch/memory/allocator/frame_allocator.hpp" +#include "arch/memory/allocator/physical_frame.hpp" +#include "arch/memory/paging/page_table.hpp" #include "arch/memory/paging/virtual_page.hpp" #include +#include #include namespace teachos::arch::memory::paging @@ -65,44 +66,21 @@ namespace teachos::arch::memory::paging * @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 - 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); - } - + auto map_page_to_frame(allocator::frame_allocator & allocator, virtual_page page, allocator::physical_frame frame, + std::bitset<64U> flags) -> void; /** * @brief Allocates the next free frame and then uses that frame to call map_page_to_frame. * * @see map_page_to_frame */ - template - 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); - } + auto map_next_free_page_to_frame(allocator::frame_allocator & allocator, virtual_page page, + std::bitset<64U> flags) -> void; /** * @brief Gets the corresponding page the given frame has to be contained in and uses that to call @@ -110,12 +88,8 @@ namespace teachos::arch::memory::paging * * @see map_page_to_frame */ - template - 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); - } + auto identity_map(allocator::frame_allocator & allocator, allocator::physical_frame frame, + std::bitset<64U> flags) -> void; /** * @brief Unmaps the virtual page from the previously mapped to physical frame and resets the flags. @@ -129,27 +103,7 @@ namespace teachos::arch::memory::paging * entries when a new page table is required. * @param page Virtual page that is being unmapped. */ - template - 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; - - 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(); - } - - unmap_page_table_entry(allocator, page, current_handle); - cpu::tlb_flush(page.start_address()); - } + auto unmap_page(allocator::frame_allocator & allocator, virtual_page page) -> void; private: /** @@ -173,24 +127,13 @@ namespace teachos::arch::memory::paging /** * @brief Unmaps specific page at the current internal handle level. * - * @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. * @param handle Page Table handle we want to access the entry that should be cleared on. */ - template - 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]; - auto const frame = entry.calculate_pointed_to_frame(); - exception_handling::assert(frame.has_value(), - "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); - entry.set_unused(); - allocator.deallocate_frame(frame.value()); - } + static auto unmap_page_table_entry(allocator::frame_allocator & allocator, virtual_page page, + page_table_handle & handle) -> void; public: page_table_handle active_handle; ///< Underlying active level 4 page table diff --git a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp index f980451..2f55a13 100644 --- a/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp +++ b/arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP #define TEACHOS_ARCH_X86_64_MEMORY_PAGING_KERNEL_MAPPER_HPP -#include "arch/memory/cpu/control_register.hpp" +#include "arch/memory/allocator/frame_allocator.hpp" +#include "arch/memory/multiboot/reader.hpp" #include "arch/memory/paging/active_page_table.hpp" #include "arch/memory/paging/inactive_page_table.hpp" #include "arch/memory/paging/temporary_page.hpp" -#include "arch/video/vga/text.hpp" namespace teachos::arch::memory::paging { @@ -14,7 +14,6 @@ namespace teachos::arch::memory::paging * * @tparam T Contract the allocator that should be used to allocate frames for the remapping process has to fulfill. */ - template struct kernel_mapper { /** @@ -23,12 +22,7 @@ namespace teachos::arch::memory::paging * @param allocator Allocator that should be used to allocate frames for the remapping process. * @param mem_info Information about elf kernel sections required for remapping process. */ - kernel_mapper(T & allocator, multiboot::memory_information const & mem_info) - : allocator(allocator) - , mem_info(mem_info) - { - // Nothing to do - } + kernel_mapper(allocator::frame_allocator & allocator, multiboot::memory_information const & mem_info); /** * @brief Remap the kernel, meaning we map the entire kernel and all of it's elf sections with the correct flags @@ -38,21 +32,7 @@ namespace teachos::arch::memory::paging * inactive page table, that is not used by the CPU to ensure we are not changign memory that we are using. Because * remapping active kernel memory in the kernel wouldn't work. */ - auto remap_kernel() -> active_page_table & - { - temporary_page temporary_page{virtual_page{0xCAFEBABE}, allocator}; - auto & active_table = active_page_table::create_or_get(); - auto const frame = allocator.allocate_frame(); - exception_handling::assert(frame.has_value(), - "[Kernel Mapper] Frame could not be allocated and therefore kernel not mapped"); - inactive_page_table new_table{frame.value(), active_table, temporary_page}; - remap_elf_kernel_sections(new_table, temporary_page, active_table); - auto const old_table = switch_active_page_table(new_table); - auto const old_level_4_page = - virtual_page::containing_address(old_table.page_table_level_4_frame.start_address()); - active_table.unmap_page(allocator, old_level_4_page); - return active_table; - } + auto remap_kernel() -> active_page_table &; private: /** @@ -63,8 +43,8 @@ namespace teachos::arch::memory::paging * Once the remapping process is done we can restore the original recursive mapping with the complete remapped * kernel. * - * @note Because we change the entries we also have to ensure we flush the translation lookaside buffer, before we - * map the entries. + * @note Because we change the entries we also have to ensure we flush the translation lookaside buffer, before + * we map the entries. * * @param inactive_table Level 4 page table we temporarily map the kernel into. * @param temporary_page Temporary page that should be used for the mapping process and then @@ -73,20 +53,7 @@ namespace teachos::arch::memory::paging * restored once the process is finished. */ auto remap_elf_kernel_sections(inactive_page_table & inactive_table, temporary_page & temporary_page, - active_page_table & active_table) -> void - { - auto const backup = - allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3)); - auto page_table_level4 = temporary_page.map_table_frame(backup, active_table); - - active_table[511].set_entry(inactive_table.page_table_level_4_frame, entry::PRESENT | entry::WRITABLE); - cpu::tlb_flush_all(); - map_elf_kernel_sections(active_table); - - page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE); - cpu::tlb_flush_all(); - temporary_page.unmap_page(active_table); - } + active_page_table & active_table) -> void; /** * @brief Switches the current active table pointed to by the CR3 register with another page table that is currently @@ -95,16 +62,7 @@ namespace teachos::arch::memory::paging * @param new_table Inactive page table that should now be made active and replace the current active one. * @return The previous active page table. */ - auto switch_active_page_table(inactive_page_table new_table) -> inactive_page_table - { - auto const backup = - allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3)); - auto const old_table = inactive_page_table{backup}; - - auto const new_address = new_table.page_table_level_4_frame.start_address(); - cpu::write_control_register(cpu::control_register::CR3, new_address); - return old_table; - } + auto switch_active_page_table(inactive_page_table new_table) -> inactive_page_table; /** * @brief Maps the required entries according to every elf section and it's contained frames. Additionally each of @@ -113,41 +71,9 @@ namespace teachos::arch::memory::paging * @param active_table Active level 4 page table that should be used to map the required elf sections into entries. * Has had its recursive mapping temporarily replaced and points to unmapped place in memory. */ - auto map_elf_kernel_sections(active_page_table & active_table) -> void - { - exception_handling::assert(!mem_info.sections.empty(), "[Kernel Mapper] Kernel elf sections empty"); - for (auto const & section : mem_info.sections) - { - if (!section.flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY)) - { - continue; - } - exception_handling::assert(section.physical_address % allocator::PAGE_FRAME_SIZE == 0U, - "[Kernel Mapper] Section must be page aligned"); - auto const start_frame = allocator::physical_frame::containing_address(section.physical_address); - // End address is exclusive, so that it is not part of the section anymore (one past the last frame of this - // section). But end frame would now point to the actual last frame and not one past the last frame, therefore - // we increment by one to get one past the last frame of this section. - auto const end_frame = - ++(allocator::physical_frame::containing_address(section.physical_address + section.section_size - 1)); - - allocator::frame_container::iterator const begin{start_frame}; - allocator::frame_container::iterator const end{end_frame}; - allocator::frame_container const frames{begin, end}; - entry const entry{section.flags}; - - for (auto const & frame : frames) - { - active_table.identity_map(allocator, frame, entry.get_flags()); - } - } - - auto const vga_buffer_frame = - allocator::physical_frame::containing_address(video::vga::text::DEFAULT_VGA_TEXT_BUFFER_ADDRESS); - active_table.identity_map(allocator, vga_buffer_frame, entry::WRITABLE); - } + auto map_elf_kernel_sections(active_page_table & active_table) -> void; - T & allocator; + allocator::frame_allocator & allocator; multiboot::memory_information const & mem_info; ///< Information about elf kernel sections required for remapping process. }; diff --git a/arch/x86_64/include/arch/memory/paging/page_table.hpp b/arch/x86_64/include/arch/memory/paging/page_table.hpp index 7a15875..24ff8f4 100644 --- a/arch/x86_64/include/arch/memory/paging/page_table.hpp +++ b/arch/x86_64/include/arch/memory/paging/page_table.hpp @@ -2,7 +2,7 @@ #define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP #include "arch/exception_handling/assert.hpp" -#include "arch/memory/allocator/concept.hpp" +#include "arch/memory/allocator/frame_allocator.hpp" #include "arch/memory/paging/page_entry.hpp" namespace teachos::arch::memory::paging @@ -82,26 +82,7 @@ namespace teachos::arch::memory::paging * entries when a new page table is required. * @param table_index Index of this page table in the page table one level lower. */ - template - auto next_table_or_create(T & allocator, std::size_t table_index) -> page_table_handle - { - auto next_handle = next_table(table_index); - // If the next table method failed then it means that the page level of the frame we want allocate has not yet - // been created itself. So we have to do that before we are able to allocate the wanted frame. This has to be done - // for every level, meaning we potenitally create a level 4, level 3 and level 2 page entry, each pointing to a - // page table one level below. - if (!next_handle.has_value()) - { - auto const allocated_frame = allocator.allocate_frame(); - exception_handling::assert(allocated_frame.has_value(), "[Page mapper] Unable to allocate frame"); - this->operator[](table_index).set_entry(allocated_frame.value(), entry::PRESENT | entry::WRITABLE); - // There should now be an entry at the previously not existent index, therefore we can simply access it again. - next_handle = next_table(table_index); - exception_handling::assert(next_handle.has_value(), "[Page mapper] Unable to create new entry into page table"); - next_handle.value().zero_entries(); - } - return next_handle.value(); - } + auto next_table_or_create(allocator::frame_allocator & allocator, std::size_t table_index) -> page_table_handle; /** * @brief Index operator overload to access specific mutable entry directy. diff --git a/arch/x86_64/include/arch/memory/paging/temporary_page.hpp b/arch/x86_64/include/arch/memory/paging/temporary_page.hpp index a850879..3c4301a 100644 --- a/arch/x86_64/include/arch/memory/paging/temporary_page.hpp +++ b/arch/x86_64/include/arch/memory/paging/temporary_page.hpp @@ -20,13 +20,7 @@ namespace teachos::arch::memory::paging * @param page Page to turn into temporary page * @param allocator Frame allocator used to fill page */ - template - temporary_page(virtual_page page, T & allocator) - : page{page} - , allocator{allocator} - { - // Nothing to do - } + temporary_page(virtual_page page, allocator::frame_allocator & allocator); /** * @brief Unmap the current page. diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp index 5b0f2c3..8c68a49 100644 --- a/arch/x86_64/src/kernel/main.cpp +++ b/arch/x86_64/src/kernel/main.cpp @@ -2,6 +2,7 @@ #include "arch/exception_handling/assert.hpp" #include "arch/memory/allocator/area_frame_allocator.hpp" +#include "arch/memory/cpu/control_register.hpp" #include "arch/memory/cpu/msr.hpp" #include "arch/memory/multiboot/reader.hpp" #include "arch/memory/paging/kernel_mapper.hpp" diff --git a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp index 3bc9676..cb4fefa 100644 --- a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp +++ b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp @@ -81,5 +81,5 @@ namespace teachos::arch::memory::allocator return allocate_frame(); } - auto area_frame_allocator::deallocate_frame(physical_frame physical_frame) -> void { (void)physical_frame; } + auto area_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { (void)physical_frame; } } // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp index d37864e..ed000a0 100644 --- a/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp +++ b/arch/x86_64/src/memory/allocator/tiny_frame_allocator.cpp @@ -4,7 +4,7 @@ namespace teachos::arch::memory::allocator { - tiny_frame_allocator::tiny_frame_allocator(area_frame_allocator & allocator) + tiny_frame_allocator::tiny_frame_allocator(frame_allocator & allocator) : frames{} { // Has to be done this way, because constructing the constructor with the data from allocator.allocate_frames(), @@ -34,7 +34,7 @@ namespace teachos::arch::memory::allocator return std::nullopt; } - auto tiny_frame_allocator::deallocate_frame(physical_frame physical_frame) -> void + auto tiny_frame_allocator::deallocate_frame(physical_frame const & physical_frame) -> void { for (auto & frame_option : frames) { diff --git a/arch/x86_64/src/memory/paging/active_page_table.cpp b/arch/x86_64/src/memory/paging/active_page_table.cpp index 0113869..d2bf683 100644 --- a/arch/x86_64/src/memory/paging/active_page_table.cpp +++ b/arch/x86_64/src/memory/paging/active_page_table.cpp @@ -1,5 +1,7 @@ #include "arch/memory/paging/active_page_table.hpp" +#include "arch/memory/cpu/tlb.hpp" + namespace teachos::arch::memory::paging { namespace @@ -15,6 +17,71 @@ namespace teachos::arch::memory::paging return active_page; } + auto active_page_table::map_page_to_frame(allocator::frame_allocator & 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); + } + + auto active_page_table::identity_map(allocator::frame_allocator & 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); + } + + auto active_page_table::map_next_free_page_to_frame(allocator::frame_allocator & 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); + } + + auto active_page_table::unmap_page(allocator::frame_allocator & 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; + + 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(); + } + + unmap_page_table_entry(allocator, page, current_handle); + cpu::tlb_flush(page.start_address()); + } + + auto active_page_table::unmap_page_table_entry(allocator::frame_allocator & allocator, virtual_page page, + page_table_handle & handle) -> void + { + auto level_index = page.get_level_index(handle.get_level()); + auto & entry = handle[level_index]; + auto const frame = entry.calculate_pointed_to_frame(); + exception_handling::assert(frame.has_value(), + "[Page Mapper] Attempted to unmap page, which has not been mapped previously"); + entry.set_unused(); + allocator.deallocate_frame(frame.value()); + } + auto active_page_table::operator[](std::size_t index) -> entry & { return active_handle[index]; } auto active_page_table::translate_address(virtual_address address) -> std::optional diff --git a/arch/x86_64/src/memory/paging/kernel_mapper.cpp b/arch/x86_64/src/memory/paging/kernel_mapper.cpp new file mode 100644 index 0000000..f938cb3 --- /dev/null +++ b/arch/x86_64/src/memory/paging/kernel_mapper.cpp @@ -0,0 +1,94 @@ +#include "arch/memory/paging/kernel_mapper.hpp" + +#include "arch/memory/cpu/control_register.hpp" +#include "arch/memory/cpu/tlb.hpp" +#include "arch/memory/paging/temporary_page.hpp" +#include "arch/video/vga/text.hpp" + +namespace teachos::arch::memory::paging +{ + + kernel_mapper::kernel_mapper(allocator::frame_allocator & allocator, multiboot::memory_information const & mem_info) + : allocator(allocator) + , mem_info(mem_info) + { + // Nothing to do + } + + auto kernel_mapper::remap_kernel() -> active_page_table & + { + temporary_page temporary_page{virtual_page{0xCAFEBABE}, allocator}; + auto & active_table = active_page_table::create_or_get(); + auto const frame = allocator.allocate_frame(); + exception_handling::assert(frame.has_value(), + "[Kernel Mapper] Frame could not be allocated and therefore kernel not mapped"); + inactive_page_table new_table{frame.value(), active_table, temporary_page}; + remap_elf_kernel_sections(new_table, temporary_page, active_table); + auto const old_table = switch_active_page_table(new_table); + auto const old_level_4_page = virtual_page::containing_address(old_table.page_table_level_4_frame.start_address()); + active_table.unmap_page(allocator, old_level_4_page); + return active_table; + } + + auto kernel_mapper::remap_elf_kernel_sections(inactive_page_table & inactive_table, temporary_page & temporary_page, + active_page_table & active_table) -> void + { + auto const backup = + allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3)); + auto page_table_level4 = temporary_page.map_table_frame(backup, active_table); + + active_table[511].set_entry(inactive_table.page_table_level_4_frame, entry::PRESENT | entry::WRITABLE); + cpu::tlb_flush_all(); + map_elf_kernel_sections(active_table); + + page_table_level4[511].set_entry(backup, entry::PRESENT | entry::WRITABLE); + cpu::tlb_flush_all(); + temporary_page.unmap_page(active_table); + } + + auto kernel_mapper::switch_active_page_table(inactive_page_table new_table) -> inactive_page_table + { + auto const backup = + allocator::physical_frame::containing_address(cpu::read_control_register(cpu::control_register::CR3)); + auto const old_table = inactive_page_table{backup}; + + auto const new_address = new_table.page_table_level_4_frame.start_address(); + cpu::write_control_register(cpu::control_register::CR3, new_address); + return old_table; + } + + auto kernel_mapper::map_elf_kernel_sections(active_page_table & active_table) -> void + { + exception_handling::assert(!mem_info.sections.empty(), "[Kernel Mapper] Kernel elf sections empty"); + for (auto const & section : mem_info.sections) + { + if (!section.flags.contains_flags(multiboot::elf_section_flags::OCCUPIES_MEMORY)) + { + continue; + } + exception_handling::assert(section.physical_address % allocator::PAGE_FRAME_SIZE == 0U, + "[Kernel Mapper] Section must be page aligned"); + auto const start_frame = allocator::physical_frame::containing_address(section.physical_address); + // End address is exclusive, so that it is not part of the section anymore (one past the last frame of this + // section). But end frame would now point to the actual last frame and not one past the last frame, therefore + // we increment by one to get one past the last frame of this section. + auto const end_frame = + ++(allocator::physical_frame::containing_address(section.physical_address + section.section_size - 1)); + + allocator::frame_container::iterator const begin{start_frame}; + allocator::frame_container::iterator const end{end_frame}; + allocator::frame_container const frames{begin, end}; + entry const entry{section.flags}; + + for (auto const & frame : frames) + { + active_table.identity_map(allocator, frame, entry.get_flags()); + } + } + + auto const vga_buffer_frame = + allocator::physical_frame::containing_address(video::vga::text::DEFAULT_VGA_TEXT_BUFFER_ADDRESS); + active_table.identity_map(allocator, vga_buffer_frame, entry::WRITABLE); + } + +} // namespace teachos::arch::memory::paging \ No newline at end of file diff --git a/arch/x86_64/src/memory/paging/page_table.cpp b/arch/x86_64/src/memory/paging/page_table.cpp index eb11810..48e6d4f 100644 --- a/arch/x86_64/src/memory/paging/page_table.cpp +++ b/arch/x86_64/src/memory/paging/page_table.cpp @@ -96,6 +96,27 @@ namespace teachos::arch::memory::paging "[Page Table] Attempted to pass nullptr as table to page table table method"); } + auto page_table_handle::next_table_or_create(allocator::frame_allocator & allocator, + std::size_t table_index) -> page_table_handle + { + auto next_handle = next_table(table_index); + // If the next table method failed then it means that the page level of the frame we want allocate has not yet + // been created itself. So we have to do that before we are able to allocate the wanted frame. This has to be done + // for every level, meaning we potenitally create a level 4, level 3 and level 2 page entry, each pointing to a + // page table one level below. + if (!next_handle.has_value()) + { + auto const allocated_frame = allocator.allocate_frame(); + exception_handling::assert(allocated_frame.has_value(), "[Page mapper] Unable to allocate frame"); + this->operator[](table_index).set_entry(allocated_frame.value(), entry::PRESENT | entry::WRITABLE); + // There should now be an entry at the previously not existent index, therefore we can simply access it again. + next_handle = next_table(table_index); + exception_handling::assert(next_handle.has_value(), "[Page mapper] Unable to create new entry into page table"); + next_handle.value().zero_entries(); + } + return next_handle.value(); + } + auto page_table_handle::zero_entries() -> void { table->zero_entries(); } auto page_table_handle::is_empty() const -> bool { return table->is_empty(); } diff --git a/arch/x86_64/src/memory/paging/temporary_page.cpp b/arch/x86_64/src/memory/paging/temporary_page.cpp index 152241d..8439864 100644 --- a/arch/x86_64/src/memory/paging/temporary_page.cpp +++ b/arch/x86_64/src/memory/paging/temporary_page.cpp @@ -4,6 +4,13 @@ namespace teachos::arch::memory::paging { + temporary_page::temporary_page(virtual_page page, allocator::frame_allocator & allocator) + : page{page} + , allocator{allocator} + { + // Nothing to do + } + auto temporary_page::map_table_frame(allocator::physical_frame frame, active_page_table & active_table) -> page_table_handle { @@ -24,6 +31,6 @@ namespace teachos::arch::memory::paging auto temporary_page::unmap_page(active_page_table & active_table) -> void { - active_table.unmap_page(allocator, page); + active_table.unmap_page(this->allocator, page); } } // namespace teachos::arch::memory::paging -- cgit v1.2.3