From 675f38d6733fb19b4ffc7e9fbddb93acdd1d1e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matteo=20Gm=C3=BCr?= Date: Sat, 19 Oct 2024 14:40:25 +0000 Subject: Seperate allocation and paging code into multiple files as well --- arch/x86_64/CMakeLists.txt | 6 +- .../arch/memory/allocator/area_frame_allocator.hpp | 75 +++++++ .../arch/memory/allocator/physical_frame.hpp | 52 +++++ .../x86_64/include/arch/memory/frame_allocator.hpp | 126 ------------ arch/x86_64/include/arch/memory/paging.hpp | 220 --------------------- .../include/arch/memory/paging/page_entry.hpp | 90 +++++++++ .../include/arch/memory/paging/page_table.hpp | 100 ++++++++++ .../include/arch/memory/paging/virtual_page.hpp | 27 +++ arch/x86_64/src/kernel/main.cpp | 4 +- .../src/memory/allocator/area_frame_allocator.cpp | 97 +++++++++ .../x86_64/src/memory/allocator/physical_frame.cpp | 22 +++ arch/x86_64/src/memory/frame_allocator.cpp | 110 ----------- arch/x86_64/src/memory/paging.cpp | 42 ---- arch/x86_64/src/memory/paging/page_entry.cpp | 42 ++++ arch/x86_64/src/memory/paging/page_table.cpp | 65 ++++++ 15 files changed, 576 insertions(+), 502 deletions(-) create mode 100644 arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp create mode 100644 arch/x86_64/include/arch/memory/allocator/physical_frame.hpp delete mode 100644 arch/x86_64/include/arch/memory/frame_allocator.hpp delete mode 100644 arch/x86_64/include/arch/memory/paging.hpp create mode 100644 arch/x86_64/include/arch/memory/paging/page_entry.hpp create mode 100644 arch/x86_64/include/arch/memory/paging/page_table.hpp create mode 100644 arch/x86_64/include/arch/memory/paging/virtual_page.hpp create mode 100644 arch/x86_64/src/memory/allocator/area_frame_allocator.cpp create mode 100644 arch/x86_64/src/memory/allocator/physical_frame.cpp delete mode 100644 arch/x86_64/src/memory/frame_allocator.cpp delete mode 100644 arch/x86_64/src/memory/paging.cpp create mode 100644 arch/x86_64/src/memory/paging/page_entry.cpp create mode 100644 arch/x86_64/src/memory/paging/page_table.cpp (limited to 'arch') diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 7ddf303..016215b 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -44,8 +44,10 @@ target_sources("_memory" PRIVATE "src/memory/multiboot/elf_symbols_section.cpp" "src/memory/multiboot/memory_map.cpp" "src/memory/multiboot/reader.cpp" - "src/memory/frame_allocator.cpp" - "src/memory/paging.cpp" + "src/memory/allocator/area_frame_allocator.cpp" + "src/memory/allocator/physical_frame.cpp" + "src/memory/paging/page_entry.cpp" + "src/memory/paging/page_table.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 new file mode 100644 index 0000000..c9d4e7f --- /dev/null +++ b/arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp @@ -0,0 +1,75 @@ +#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/multiboot/reader.hpp" + +#include "physical_frame.hpp" +#include + +namespace teachos::arch::memory::allocator +{ + /** + * @brief Allocates memory using memory areas read from the multiboot2 information pointer. + */ + struct area_frame_allocator + { + /** + * @brief Constructor + * + * @param mem_info Structure containg all relevant information to map and allocate memory + */ + area_frame_allocator(multiboot::memory_information mem_info); + + /** + * @brief Allocate memory by finding and returning a free physical_frame. + * + * The physical_frame allocation executes multiple checks before returning + * the physical_frame that is available to allocate. It must at least + * do the following: + * - check if the next_free_frame is within the current_area + * - check if the next_free_frame is actually free + * - update the next_free_frame after finding a free physical_frame + */ + auto allocate_frame() -> std::optional; + + /** + * @brief Deallocates a previously allocated physical_frame. + * + * @param physical_frame Previously allocated physical_frame that should be allocated. + */ + auto deallocate_frame(physical_frame physical_frame) -> void; + + /** + * @brief Returns the iterator pointing to the first element of the memory area. + * Allows using this class in the for each loop, because it follows the InputIterator template scheme. + * + * @return Iterator pointing to first element of the memory area. + */ + auto begin() -> multiboot::memory_area_iterator; + + /** + * @brief Returns the iterator pointing to one past the last element of the memory area. + * Allows using this class in the for each loop, because it follows the InputIterator template scheme. + * + * @return Iterator pointing to one past the last element of the memory area. + */ + auto end() -> multiboot::memory_area_iterator; + + private: + /** + * @brief Find the next memory area and write it into current_area. + */ + auto choose_next_area() -> void; + + physical_frame next_free_frame; ///< The physical_frame after the last allocated one. + std::optional current_area; ///< The current memory area. + multiboot::memory_area_iterator area_begin; ///< Pointer to the first element of all memory areas. + multiboot::memory_area_iterator area_end; ///< Pointer to one pas the last element of all memory areas. + physical_frame const kernel_start; ///< The start address of the kernel code in memory. + physical_frame const kernel_end; ///< The end address of the kernel code in memory. + physical_frame const multiboot_start; ///< The start address of the multiboot code in memory. + physical_frame const multiboot_end; ///< The end address of the multiboot code in memory. + }; +} // namespace teachos::arch::memory::allocator + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp b/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp new file mode 100644 index 0000000..5e99d10 --- /dev/null +++ b/arch/x86_64/include/arch/memory/allocator/physical_frame.hpp @@ -0,0 +1,52 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP + +#include +#include + +namespace teachos::arch::memory::allocator +{ + /** + * @brief Specific physical frame containing helper functions to determine if a specific address is in that + * physical frame or not. + */ + struct physical_frame + { + /** + * @brief Constructor. + * + * @param frame_number Index number that should be assigned to this physical_frame. + */ + explicit physical_frame(std::size_t frame_number); + + /** + * @brief Returns the physical_frame the given address is contained in. + * + * @param address Address we want to get the corresponding physical_frame for. + * @return Frame the given address is contained in. + */ + static auto containing_address(std::size_t address) -> physical_frame; + + /** + * @brief Evaluates the start address of the physical frame. + * + * @return start address of the physical frame. + */ + auto start_address() const -> uint64_t; + + /** + * @brief Defaulted equals operator. + */ + constexpr auto operator==(const physical_frame & other) const -> bool = default; + + /** + * @brief Defaulted three-way comparsion operator. + */ + constexpr auto operator<=>(const physical_frame & other) const -> std::partial_ordering = default; + + std::size_t + frame_number; ///< Index number of the current physical frame, used to distinguish it from other frames. + }; +} // namespace teachos::arch::memory::allocator + +#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_PHYSICAL_FRAME_HPP diff --git a/arch/x86_64/include/arch/memory/frame_allocator.hpp b/arch/x86_64/include/arch/memory/frame_allocator.hpp deleted file mode 100644 index f1096d1..0000000 --- a/arch/x86_64/include/arch/memory/frame_allocator.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_FRAME_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_FRAME_HPP - -#include "multiboot/reader.hpp" - -#include -#include -#include - -namespace teachos::arch::memory -{ - constexpr std::size_t PAGE_FRAME_SIZE = 4096U; ///< Default page size of x86_84 is always 4KiB. - - /** - * @brief Specific physical_frame containing helper functions to determine if a specific address is in that - * physical_frame or not. - */ - struct physical_frame - { - /** - * @brief Constructor. - * - * @param frame_number Index number that should be assigned to this physical_frame. - */ - explicit physical_frame(std::size_t frame_number); - - /** - * @brief Returns the physical_frame the given address is contained in. - * - * @param address Address we want to get the corresponding physical_frame for. - * @return Frame the given address is contained in. - */ - static auto containing_address(std::size_t address) -> physical_frame; - - /** - * @brief Evaluates the start address of the physical frame. - * - * @return start address of the physical frame. - */ - auto start_address() const -> uint64_t; - - /** - * @brief Defaulted equals operator. - */ - constexpr auto operator==(const physical_frame & other) const -> bool = default; - - /** - * @brief Defaulted three-way comparsion operator. - */ - constexpr auto operator<=>(const physical_frame & other) const -> std::partial_ordering = default; - - std::size_t - frame_number; ///< Index number of the current physical frame, used to distinguish it from other frames. - }; - - template - concept FrameAllocator = requires(T t) { - { t.allocate_frame() } -> std::same_as>; - { t.deallocate_frame() } -> std::same_as; - }; - - /** - * @brief Allocates memory using memory areas read from the multiboot2 information pointer. - */ - struct area_frame_allocator - { - /** - * @brief Constructor - * - * @param mem_info Structure containg all relevant information to map and allocate memory - */ - area_frame_allocator(multiboot::memory_information mem_info); - - /** - * @brief Allocate memory by finding and returning a free physical_frame. - * - * The physical_frame allocation executes multiple checks before returning - * the physical_frame that is available to allocate. It must at least - * do the following: - * - check if the next_free_frame is within the current_area - * - check if the next_free_frame is actually free - * - update the next_free_frame after finding a free physical_frame - */ - auto allocate_frame() -> std::optional; - - /** - * @brief Deallocates a previously allocated physical_frame. - * - * @param physical_frame Previously allocated physical_frame that should be allocated. - */ - auto deallocate_frame(physical_frame physical_frame) -> void; - - /** - * @brief Returns the iterator pointing to the first element of the memory area. - * Allows using this class in the for each loop, because it follows the InputIterator template scheme. - * - * @return Iterator pointing to first element of the memory area. - */ - auto begin() -> multiboot::memory_area_iterator; - - /** - * @brief Returns the iterator pointing to one past the last element of the memory area. - * Allows using this class in the for each loop, because it follows the InputIterator template scheme. - * - * @return Iterator pointing to one past the last element of the memory area. - */ - auto end() -> multiboot::memory_area_iterator; - - private: - /** - * @brief Find the next memory area and write it into current_area. - */ - auto choose_next_area() -> void; - - physical_frame next_free_frame; ///< The physical_frame after the last allocated one. - std::optional current_area; ///< The current memory area. - multiboot::memory_area_iterator area_begin; ///< Pointer to the first element of all memory areas. - multiboot::memory_area_iterator area_end; ///< Pointer to one pas the last element of all memory areas. - physical_frame const kernel_start; ///< The start address of the kernel code in memory. - physical_frame const kernel_end; ///< The end address of the kernel code in memory. - physical_frame const multiboot_start; ///< The start address of the multiboot code in memory. - physical_frame const multiboot_end; ///< The end address of the multiboot code in memory. - }; -} // namespace teachos::arch::memory - -#endif // TEACHOS_ARCH_X86_64_MEMORY_FRAME_HPP diff --git a/arch/x86_64/include/arch/memory/paging.hpp b/arch/x86_64/include/arch/memory/paging.hpp deleted file mode 100644 index 04c2065..0000000 --- a/arch/x86_64/include/arch/memory/paging.hpp +++ /dev/null @@ -1,220 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_HPP -#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_HPP - -#include "arch/exception_handling/assert.hpp" - -#include "frame_allocator.hpp" -#include - -namespace teachos::arch::memory -{ - constexpr std::size_t PAGE_TABLE_ENTRY_COUNT = 512U; ///< Default entry count of a page table in x86_84 is 512. - - /** - * @brief Virtual page entry contained in P1 page tables - */ - struct virtual_page - { - std::size_t number; ///< Index number of the current virtual page, used to distinguish it from other pages. - - /** - * @brief Defaulted equals operator. - */ - constexpr auto operator==(const virtual_page & other) const -> bool = default; - - /** - * @brief Defaulted three-way comparsion operator. - */ - constexpr auto operator<=>(const virtual_page & other) const -> std::partial_ordering = default; - }; - - /** - * @brief Marks a specific entry in an actual page table. - */ - struct entry - { - /** - * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. - */ - enum bitset : uint64_t - { - PRESENT = 1UL << 0UL, ///< Page is in memory and therefore present. - ///< is assumed to be READONLY and only that flag is shown in the objdump. - WRITABLE = 1UL << 1UL, ///< It is possible to write to the page. - USER_ACCESIBLE = 1UL << 2UL, ///< Page can be accessed in user mode instead of only in kernel mode code. - WRITE_THROUGH_CACHING = 1UL << 3UL, ///< Write to the page go directly to memory instead of the cache. - DISABLED_CACHING = 1UL << 4UL, ///< Page uses caching. - ACCESSED = 1UL << 5UL, ///< Page is currently in use. - DIRTY = 1UL << 6UL, ///< Page has been writen too. - HUGE_PAGE = 1UL << 7UL, ///< Page is huge (2 MiB page size in P2 page table and 1 GiB in P3 page table, - ///< instead of 4 KiB). Has to be false for P1 and P4 page tables. - GLOBAL = 1UL << 8UL, ///< Page is not flushed from caches on address space switches (PGE bit of CR4 register - ///< has to be set) - EXECUTING_CODE_FORBIDDEN = - 1UL << 63UL, ///< Page is forbidden from executing code (NXE bit in the EFER register has to be set) - }; - - /** - * @brief Whether the current page is unused, meaning the underlying std::bitset is 0. - * - * @return Current page is in memory. - */ - auto is_unused() const -> bool; - - /** - * @brief Marks the page as unused, meaning the underlying std::bitset is set to 0. - */ - auto set_unused() -> void; - - /** - * @brief Calculates the physical frame this entry is pointing too, can be null if the page is not present in - * memory. - * - * @return Calculated physical frame entry is pointing too. - */ - auto calculate_pointed_to_frame() const -> std::optional; - - /** - * @brief Copies the address from the given physical frame into the underlying std::bitset so future calls to - * calculate_physical_address() will return the new address instead of the old one. - * - * @param frame Physical frame that contains the address we want to copy into our underlying std::bitset. - */ - auto set_address(physical_frame frame) -> void; - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset, meaning that all - * bits that are set in the given std::bitset also have to be set in the underlyng std::bitset. Any additional bits - * that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying std::bitset - */ - auto contains_flags(std::bitset<64U> other) const -> bool; - - private: - /** - * @brief Extracts the physical address from the underlying bitset read from bit index 12 - 51. Is a 52 bit page - * aligned physical address of the frame of the next page table or the pyhscial address of the frame for P1 page - * tables. - * - * @return Extracted physical address of the next page or of the frame for P1 page tables. - */ - auto calculate_physical_address() const -> std::size_t; - - std::bitset<64U> flags; ///< Underlying bitset used to read the flags from. Bits 9 - 11 and 52 - 62 can be freely - ///< used for additional flags by the operating system. - }; - - enum level : uint8_t; - - /** - * @brief A Page table containing 512 entries. - */ - template - struct page_table - { - /** - * @brief Level of the page table, level 1 should not be able to call next_table anymore, because it would result in - * attempting to access memory that it should not. - */ - enum level : uint8_t - { - LEVEL1, - LEVEL2, - LEVEL3, - LEVEL4 - }; - - /** - * @brief Constructor. - */ - page_table() - : entries() - , p4(reinterpret_cast(0xfffffffffffff000)) - { - // Nothing to do - } - - /** - * @brief Set every entry of the page to unused - */ - auto zero_entries() -> void - { - constexpr size_t entry_amount = sizeof(entries) / sizeof(entries[0]); - for (size_t i = 0; i < entry_amount; ++i) - { - auto entry = entry.set_unused(); - } - } - - /** - * @brief Gets the complete next page table level from the given page table index. Meaning we use an index into a - * Level 4 page table to get the according Level 3 page table. - * - * @param table_index Index of this page table in the page table one level higher. - * @return Entire page table of the next level. - */ - auto next_table(std::size_t table_index) const -> std::optional - requires(page_table_level > 0) - { - auto address = next_table_address(table_index); - - if (address.has_value()) - { - return reinterpret_cast(*address); - } - - return std::nullopt; - } - - /** - * @brief Index operator overload to access specific mutable entry directy - * - * @param index Index of the entry we want to access and change - * @return Entry at the given table index - */ - auto operator[](std::size_t index) -> entry & - { - // C array is not bounds checked, therefore we have to check ourselves, to ensure no out of bounds reads, which - // could be incredibly hard to debug later. - arch::exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] index out of bounds"); - return entries[index]; - } - - /** - * @brief Index operator overload to access specific immutable entry directy - * - * @param index Index of the entry we want to access and only read - * @return Entry at the given table index - */ - auto operator[](std::size_t index) const -> entry const & { return this->operator[](index); } - - private: - /** - * @brief Calculates the address of the next page table level for the given table index. The next page table address - * is only valid if the corresponding entry is present and not a huge page. Meaning we use an index into a - * Level 4 page table to get the according Level 3 page table address. - * - * @param table_index Index of this page table in the page table one level higher. - * @return An optional of the address of the next page table or null - */ - auto next_table_address(std::size_t table_index) const -> std::optional - { - auto entry = this->operator[](table_index); - - if (entry.contains_flags(entry::PRESENT) && !entry.contains_flags(entry::HUGE_PAGE)) - { - std::size_t const table_address = reinterpret_cast(this); - return (table_address << 9) | (table_index << 12); - } - // TODO: Implement behaviour for huge pages currently not done - return std::nullopt; - } - - entry entries[PAGE_TABLE_ENTRY_COUNT]; - page_table const * p4; - }; -} // namespace teachos::arch::memory - -#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_HPP diff --git a/arch/x86_64/include/arch/memory/paging/page_entry.hpp b/arch/x86_64/include/arch/memory/paging/page_entry.hpp new file mode 100644 index 0000000..a40e764 --- /dev/null +++ b/arch/x86_64/include/arch/memory/paging/page_entry.hpp @@ -0,0 +1,90 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP + +#include "arch/memory/allocator/physical_frame.hpp" + +#include +#include + +namespace teachos::arch::memory::paging +{ + /** + * @brief Marks a specific entry in an actual page table. + */ + struct entry + { + /** + * @brief Possible set bits in our underlying std::bitset and the meaning when they are set. + */ + enum bitset : uint64_t + { + PRESENT = 1UL << 0UL, ///< Page is in memory and therefore present. + ///< is assumed to be READONLY and only that flag is shown in the objdump. + WRITABLE = 1UL << 1UL, ///< It is possible to write to the page. + USER_ACCESIBLE = 1UL << 2UL, ///< Page can be accessed in user mode instead of only in kernel mode code. + WRITE_THROUGH_CACHING = 1UL << 3UL, ///< Write to the page go directly to memory instead of the cache. + DISABLED_CACHING = 1UL << 4UL, ///< Page uses caching. + ACCESSED = 1UL << 5UL, ///< Page is currently in use. + DIRTY = 1UL << 6UL, ///< Page has been writen too. + HUGE_PAGE = 1UL << 7UL, ///< Page is huge (2 MiB page size in P2 page table and 1 GiB in P3 page table, + ///< instead of 4 KiB). Has to be false for P1 and P4 page tables. + GLOBAL = 1UL << 8UL, ///< Page is not flushed from caches on address space switches (PGE bit of CR4 register + ///< has to be set) + EXECUTING_CODE_FORBIDDEN = + 1UL << 63UL, ///< Page is forbidden from executing code (NXE bit in the EFER register has to be set) + }; + + /** + * @brief Whether the current page is unused, meaning the underlying std::bitset is 0. + * + * @return Current page is in memory. + */ + auto is_unused() const -> bool; + + /** + * @brief Marks the page as unused, meaning the underlying std::bitset is set to 0. + */ + auto set_unused() -> void; + + /** + * @brief Calculates the physical frame this entry is pointing too, can be null if the page is not present in + * memory. + * + * @return Calculated physical frame entry is pointing too. + */ + auto calculate_pointed_to_frame() const -> std::optional; + + /** + * @brief Copies the address from the given physical frame into the underlying std::bitset so future calls to + * calculate_physical_address() will return the new address instead of the old one. + * + * @param frame Physical frame that contains the address we want to copy into our underlying std::bitset. + */ + auto set_address(allocator::physical_frame frame) -> void; + + /** + * @brief Checks if the given std::bitset is a subset or equivalent to the underlying std::bitset, meaning that all + * bits that are set in the given std::bitset also have to be set in the underlyng std::bitset. Any additional bits + * that are set are not relevant. + * + * @param other Flags that we want to compare against and check if the underlying std::bitset has the same bits set. + * @return Whether the given flags are a subset or equivalent with the underlying std::bitset + */ + auto contains_flags(std::bitset<64U> other) const -> bool; + + private: + /** + * @brief Extracts the physical address from the underlying bitset read from bit index 12 - 51. Is a 52 bit page + * aligned physical address of the frame of the next page table or the pyhscial address of the frame for P1 page + * tables. + * + * @return Extracted physical address of the next page or of the frame for P1 page tables. + */ + auto calculate_physical_address() const -> std::size_t; + + std::bitset<64U> flags; ///< Underlying bitset used to read the flags from. Bits 9 - 11 and 52 - 62 can be freely + ///< used for additional flags by the operating system. + }; +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_ENTRY_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/paging/page_table.hpp b/arch/x86_64/include/arch/memory/paging/page_table.hpp new file mode 100644 index 0000000..32e49e5 --- /dev/null +++ b/arch/x86_64/include/arch/memory/paging/page_table.hpp @@ -0,0 +1,100 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP + +#include "page_entry.hpp" + +namespace teachos::arch::memory::paging +{ + namespace + { + constexpr std::size_t PAGE_TABLE_ENTRY_COUNT = 512U; ///< Default entry count of a page table in x86_84 is 512. + } + + /** + * @brief Actual data that is contained in every page table, this is the structure we cast a specific address too, + * because it consists of x amount os entries, which is a simple address. + */ + struct table_content + { + entry entries[PAGE_TABLE_ENTRY_COUNT]; ///< Entries containing addresses to page tables of a level below or actual + ///< virtual addresses for the level 1 page table. + }; + + /** + * @brief A Page table containing 512 entries. + */ + struct page_table + { + /** + * @brief Level of the page table, level 1 should not be able to call next_table anymore, because it would result in + * attempting to access memory that it should not. + */ + enum level : uint8_t + { + LEVEL1, + LEVEL2, + LEVEL3, + LEVEL4 + }; + + /** + * @brief Constructor. Automatically starts on the fixed address of the Level 4 page table. + */ + page_table(); + + /** + * @brief Set every entry of the page to unused. + */ + auto zero_entries() -> void; + + /** + * @brief Gets the complete next page table level from the given page table index. Meaning we use an index into a + * Level 4 page table to get the according Level 3 page table. When using this on an a level 1 page table it will + * cause an assertion. + * + * @param table_index Index of this page table in the page table one level higher. + */ + auto next_table(std::size_t table_index) -> void; + + /** + * @brief Index operator overload to access specific mutable entry directy. + * + * @param index Index of the entry we want to access and change. + * @return Entry at the given table index. + */ + auto operator[](std::size_t index) -> entry &; + + /** + * @brief Index operator overload to access specific immutable entry directy. + * + * @param index Index of the entry we want to access and only read. + * @return Entry at the given table index. + */ + auto operator[](std::size_t index) const -> entry const &; + + private: + /** + * @brief Constructor. Used internally to create new page tables. + * + * @param new_level New level of the page table. + * @param new_table New table data contained in the page table. + */ + page_table(level new_level, table_content * new_table); + + /** + * @brief Calculates the address of the next page table level for the given table index. The next page table address + * is only valid if the corresponding entry is present and not a huge page. Meaning we use an index into a + * Level 4 page table to get the according Level 3 page table address. + * + * @param table_index Index of this page table in the page table one level higher. + * @return An optional of the address of the next page table or null. + */ + auto next_table_address(std::size_t table_index) const -> std::optional; + + level current_level; ///< Current level of the page table, used to ensure next_table() is never called with a level + ///< 1 page table + table_content * current_table; ///< Current table we are accessing and indexing. + }; +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_PAGE_TABLE_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/paging/virtual_page.hpp b/arch/x86_64/include/arch/memory/paging/virtual_page.hpp new file mode 100644 index 0000000..12af510 --- /dev/null +++ b/arch/x86_64/include/arch/memory/paging/virtual_page.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP +#define TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP + +#include + +namespace teachos::arch::memory::paging +{ + /** + * @brief Virtual page entry contained in P1 page tables + */ + struct virtual_page + { + std::size_t number; ///< Index number of the current virtual page, used to distinguish it from other pages. + + /** + * @brief Defaulted equals operator. + */ + constexpr auto operator==(const virtual_page & other) const -> bool = default; + + /** + * @brief Defaulted three-way comparsion operator. + */ + constexpr auto operator<=>(const virtual_page & other) const -> std::partial_ordering = default; + }; +} // namespace teachos::arch::memory::paging + +#endif // TEACHOS_ARCH_X86_64_MEMORY_PAGING_VIRTUAL_PAGE_HPP \ No newline at end of file diff --git a/arch/x86_64/src/kernel/main.cpp b/arch/x86_64/src/kernel/main.cpp index c0d4aed..09fc2a2 100644 --- a/arch/x86_64/src/kernel/main.cpp +++ b/arch/x86_64/src/kernel/main.cpp @@ -1,7 +1,7 @@ #include "arch/kernel/main.hpp" #include "arch/exception_handling/assert.hpp" -#include "arch/memory/frame_allocator.hpp" +#include "arch/memory/allocator/area_frame_allocator.hpp" #include "arch/memory/multiboot/reader.hpp" #include "arch/video/vga/text.hpp" @@ -18,7 +18,7 @@ namespace teachos::arch::kernel text::write("TeachOS is starting up...", text::common_attributes::green_on_black); auto memory_information = memory::multiboot::read_multiboot2(); - auto allocator = memory::area_frame_allocator(memory_information); + memory::allocator::area_frame_allocator allocator(memory_information); auto last_allocated = allocator.allocate_frame(); auto allocated = last_allocated; diff --git a/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp new file mode 100644 index 0000000..9c344d8 --- /dev/null +++ b/arch/x86_64/src/memory/allocator/area_frame_allocator.cpp @@ -0,0 +1,97 @@ +#include "arch/memory/allocator/area_frame_allocator.hpp" + +#include "arch/exception_handling/assert.hpp" + +namespace teachos::arch::memory::allocator +{ + area_frame_allocator::area_frame_allocator(multiboot::memory_information mem_info) + : next_free_frame(0) + , current_area(std::nullopt) + , area_begin(mem_info.memory_areas) + , area_end(mem_info.memory_areas + mem_info.area_count) + , kernel_start(physical_frame::containing_address(mem_info.kernel_start)) + , kernel_end(physical_frame::containing_address(mem_info.kernel_end)) + , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start)) + , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end)) + { + choose_next_area(); + } + + auto area_frame_allocator::choose_next_area() -> void + { + current_area = std::nullopt; + + for (multiboot::memory_area_iterator it = begin(); it != end(); ++it) + { + multiboot::memory_area & area = *it; + + std::size_t address = area.base_address + area.area_length - 1; + if (physical_frame::containing_address(address) >= next_free_frame) + { + // The `next_free_frame` address is smaller than the last address of the current area + if (!current_area || area.base_address < current_area->base_address) + { + current_area = area; + } + } + } + + if (current_area) + { + // Update the `next_free_frame` according to the new memory area + physical_frame start_frame = physical_frame::containing_address(current_area->base_address); + if (next_free_frame < start_frame) + { + next_free_frame = start_frame; + } + } + } + + auto area_frame_allocator::allocate_frame() -> std::optional + { + /* + * Only try to allocate memory if current_area is not null, because + * the current_area is null if there is no more available memory. + */ + if (current_area) + { + physical_frame physical_frame{next_free_frame.frame_number}; + + struct physical_frame current_area_last_frame = { + physical_frame::containing_address(current_area->base_address + current_area->area_length - 1)}; + + if (next_free_frame > current_area_last_frame) + { + // All frames of current area are used, switch to next area + choose_next_area(); + } + else if (physical_frame >= multiboot_start && physical_frame <= kernel_end) + { + // `physical_frame` is used by the kernel or multiboot information structure + next_free_frame = allocator::physical_frame{kernel_end.frame_number + 1}; + } + else + { + // Frame is unused, increment `next_free_frame` and return it + next_free_frame.frame_number += 1; + return physical_frame; + } + + // `physical_frame` was not valid, try it again with the updated `next_free_frame` + return allocate_frame(); + } + + // no free frames left + return std::nullopt; + } + + auto area_frame_allocator::deallocate_frame(physical_frame physical_frame) -> void + { + arch::exception_handling::assert(false && physical_frame.frame_number == 0, + "[deallocate_frame] Not implemented Exception"); + } + + auto area_frame_allocator::begin() -> multiboot::memory_area_iterator { return area_begin; } + + auto area_frame_allocator::end() -> multiboot::memory_area_iterator { return area_end; } +} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/allocator/physical_frame.cpp b/arch/x86_64/src/memory/allocator/physical_frame.cpp new file mode 100644 index 0000000..03ec193 --- /dev/null +++ b/arch/x86_64/src/memory/allocator/physical_frame.cpp @@ -0,0 +1,22 @@ +#include "arch/memory/allocator/physical_frame.hpp" + +namespace teachos::arch::memory::allocator +{ + namespace + { + constexpr std::size_t PAGE_FRAME_SIZE = 4096U; ///< Default page size of x86_84 is always 4KiB. + } + + physical_frame::physical_frame(std::size_t frame_number) + : frame_number(frame_number) + { + // Nothing to do + } + + auto physical_frame::containing_address(std::size_t address) -> physical_frame + { + return physical_frame{address / PAGE_FRAME_SIZE}; + } + + auto physical_frame::start_address() const -> uint64_t { return frame_number * PAGE_FRAME_SIZE; } +} // namespace teachos::arch::memory::allocator diff --git a/arch/x86_64/src/memory/frame_allocator.cpp b/arch/x86_64/src/memory/frame_allocator.cpp deleted file mode 100644 index 7776082..0000000 --- a/arch/x86_64/src/memory/frame_allocator.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "arch/memory/frame_allocator.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory -{ - physical_frame::physical_frame(std::size_t frame_number) - : frame_number(frame_number) - { - // Nothing to do - } - - auto physical_frame::containing_address(std::size_t address) -> physical_frame - { - return physical_frame{address / PAGE_FRAME_SIZE}; - } - - auto physical_frame::start_address() const -> uint64_t { return frame_number * PAGE_FRAME_SIZE; } - - area_frame_allocator::area_frame_allocator(multiboot::memory_information mem_info) - : next_free_frame(0) - , current_area(std::nullopt) - , area_begin(mem_info.memory_areas) - , area_end(mem_info.memory_areas + mem_info.area_count) - , kernel_start(physical_frame::containing_address(mem_info.kernel_start)) - , kernel_end(physical_frame::containing_address(mem_info.kernel_end)) - , multiboot_start(physical_frame::containing_address(mem_info.multiboot_start)) - , multiboot_end(physical_frame::containing_address(mem_info.multiboot_end)) - { - choose_next_area(); - } - - auto area_frame_allocator::choose_next_area() -> void - { - current_area = std::nullopt; - - for (multiboot::memory_area_iterator it = begin(); it != end(); ++it) - { - multiboot::memory_area & area = *it; - - std::size_t address = area.base_address + area.area_length - 1; - if (physical_frame::containing_address(address) >= next_free_frame) - { - // The `next_free_frame` address is smaller than the last address of the current area - if (!current_area || area.base_address < current_area->base_address) - { - current_area = area; - } - } - } - - if (current_area) - { - // Update the `next_free_frame` according to the new memory area - physical_frame start_frame = physical_frame::containing_address(current_area->base_address); - if (next_free_frame < start_frame) - { - next_free_frame = start_frame; - } - } - } - - auto area_frame_allocator::allocate_frame() -> std::optional - { - /* - * Only try to allocate memory if current_area is not null, because - * the current_area is null if there is no more available memory. - */ - if (current_area) - { - physical_frame physical_frame{next_free_frame.frame_number}; - - struct physical_frame current_area_last_frame = { - physical_frame::containing_address(current_area->base_address + current_area->area_length - 1)}; - - if (next_free_frame > current_area_last_frame) - { - // All frames of current area are used, switch to next area - choose_next_area(); - } - else if (physical_frame >= multiboot_start && physical_frame <= kernel_end) - { - // `physical_frame` is used by the kernel or multiboot information structure - next_free_frame = arch::memory::physical_frame{kernel_end.frame_number + 1}; - } - else - { - // Frame is unused, increment `next_free_frame` and return it - next_free_frame.frame_number += 1; - return physical_frame; - } - - // `physical_frame` was not valid, try it again with the updated `next_free_frame` - return allocate_frame(); - } - - // no free frames left - return std::nullopt; - } - - auto area_frame_allocator::deallocate_frame(physical_frame physical_frame) -> void - { - arch::exception_handling::assert(false && physical_frame.frame_number == 0, - "[deallocate_frame] Not implemented Exception"); - } - - auto area_frame_allocator::begin() -> multiboot::memory_area_iterator { return area_begin; } - - auto area_frame_allocator::end() -> multiboot::memory_area_iterator { return area_end; } -} // namespace teachos::arch::memory diff --git a/arch/x86_64/src/memory/paging.cpp b/arch/x86_64/src/memory/paging.cpp deleted file mode 100644 index 9774132..0000000 --- a/arch/x86_64/src/memory/paging.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "arch/memory/paging.hpp" - -#include "arch/exception_handling/assert.hpp" - -namespace teachos::arch::memory -{ - auto entry::is_unused() const -> bool { return flags == 0U; } - - auto entry::set_unused() -> void { flags = 0U; } - - auto entry::calculate_pointed_to_frame() const -> std::optional - { - if (contains_flags(PRESENT)) - { - auto physical_address = calculate_physical_address(); - return physical_frame::containing_address(physical_address); - } - return std::nullopt; - } - - auto entry::calculate_physical_address() const -> std::size_t - { - constexpr std::size_t start_bit = 12U; - constexpr std::size_t end_bit = 52U; - size_t value = 0U; - - for (auto i = start_bit; i < end_bit; i++) - { - value |= (flags[i] ? (1 << (i - start_bit)) : 0); - } - return value; - } - - auto entry::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } - - auto entry::set_address(physical_frame frame) -> void - { - arch::exception_handling::assert((frame.start_address() & ~0x000fffff'fffff000) == 0, - "Start address is not aligned with Page"); - flags = std::bitset<64U>(frame.start_address()) | flags; - } -} // namespace teachos::arch::memory diff --git a/arch/x86_64/src/memory/paging/page_entry.cpp b/arch/x86_64/src/memory/paging/page_entry.cpp new file mode 100644 index 0000000..43c0b71 --- /dev/null +++ b/arch/x86_64/src/memory/paging/page_entry.cpp @@ -0,0 +1,42 @@ +#include "arch/memory/paging/page_entry.hpp" + +#include "arch/exception_handling/assert.hpp" + +namespace teachos::arch::memory::paging +{ + auto entry::is_unused() const -> bool { return flags == 0U; } + + auto entry::set_unused() -> void { flags = 0U; } + + auto entry::calculate_pointed_to_frame() const -> std::optional + { + if (contains_flags(PRESENT)) + { + auto physical_address = calculate_physical_address(); + return allocator::physical_frame::containing_address(physical_address); + } + return std::nullopt; + } + + auto entry::calculate_physical_address() const -> std::size_t + { + constexpr std::size_t start_bit = 12U; + constexpr std::size_t end_bit = 52U; + size_t value = 0U; + + for (auto i = start_bit; i < end_bit; i++) + { + value |= (flags[i] ? (1 << (i - start_bit)) : 0); + } + return value; + } + + auto entry::contains_flags(std::bitset<64U> other) const -> bool { return (flags & other) == other; } + + auto entry::set_address(allocator::physical_frame frame) -> void + { + arch::exception_handling::assert((frame.start_address() & ~0x000fffff'fffff000) == 0, + "Start address is not aligned with Page"); + flags = std::bitset<64U>(frame.start_address()) | flags; + } +} // namespace teachos::arch::memory::paging diff --git a/arch/x86_64/src/memory/paging/page_table.cpp b/arch/x86_64/src/memory/paging/page_table.cpp new file mode 100644 index 0000000..a1cbc72 --- /dev/null +++ b/arch/x86_64/src/memory/paging/page_table.cpp @@ -0,0 +1,65 @@ +#include "arch/memory/paging/page_table.hpp" + +#include "arch/exception_handling/assert.hpp" + +namespace teachos::arch::memory::paging +{ + page_table::page_table() + : current_level(LEVEL4) + , current_table(reinterpret_cast(0xfffffffffffff000)) + { + // Nothing to do + } + + auto page_table::zero_entries() -> void + { + constexpr size_t entry_amount = sizeof(current_table->entries) / sizeof(current_table->entries[0]); + for (size_t i = 0; i < entry_amount; ++i) + { + auto entry = this->operator[](i); + entry.set_unused(); + } + } + + auto page_table::next_table(std::size_t table_index) -> void + { + arch::exception_handling::assert(current_level != LEVEL1, + "[Page Table] Attempted to call next_table on level 1 page table"); + auto address = next_table_address(table_index); + + if (address.has_value()) + { + current_table = reinterpret_cast(*address); + current_level = static_cast(current_level - 1U); + } + } + + auto page_table::operator[](std::size_t index) -> entry & + { + // C array is not bounds checked, therefore we have to check ourselves, to ensure no out of bounds reads, which + // could be incredibly hard to debug later. + arch::exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] index out of bounds"); + return current_table->entries[index]; + } + + auto page_table::operator[](std::size_t index) const -> entry const & + { + // C array is not bounds checked, therefore we have to check ourselves, to ensure no out of bounds reads, which + // could be incredibly hard to debug later. + arch::exception_handling::assert(index < PAGE_TABLE_ENTRY_COUNT, "[Page Table] index out of bounds"); + return current_table->entries[index]; + } + + auto page_table::next_table_address(std::size_t table_index) const -> std::optional + { + auto entry = this->operator[](table_index); + + if (entry.contains_flags(entry::PRESENT) && !entry.contains_flags(entry::HUGE_PAGE)) + { + std::size_t const table_address = reinterpret_cast(this); + return (table_address << 9) | (table_index << 12); + } + // TODO: Implement behaviour for huge pages currently not done + return std::nullopt; + } +} // namespace teachos::arch::memory::paging -- cgit v1.2.3