diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2025-12-11 17:46:02 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2025-12-11 17:46:02 +0100 |
| commit | 998a001fc621ca0e7560ca09a8acd29469ae3373 (patch) | |
| tree | 435a9042ea4a1185c360e8eb92a6d2f082ed3224 /arch | |
| parent | eafbf588760c289b7f54a4771b39af0ccfe8cf59 (diff) | |
| download | teachos-998a001fc621ca0e7560ca09a8acd29469ae3373.tar.xz teachos-998a001fc621ca0e7560ca09a8acd29469ae3373.zip | |
docs: improve documentation
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/x86_64/include/x86_64/boot/ld.hpp | 95 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp | 339 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/buffered_allocator.hpp | 2 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/page_table.hpp | 145 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/paging_root.hpp | 2 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/memory/region_allocator.hpp | 94 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/page_table.cpp | 18 | ||||
| -rw-r--r-- | arch/x86_64/src/memory/region_allocator.cpp | 2 | ||||
| -rw-r--r-- | arch/x86_64/x86_64.dox | 14 |
9 files changed, 440 insertions, 271 deletions
diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp index 9af3dc8..b073863 100644 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ b/arch/x86_64/include/x86_64/boot/ld.hpp @@ -1,19 +1,19 @@ -/** - * @file - * @brief Provides an interface to linker script defined symbols. - * - * @details - * This header provides declarations for symbols that are defined in the linker script itself. The symbols declared here - * provide important information, for example the start and end of the kernel image in physical memory. - * - * Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a - * pointer to the memory location the represent. - * - * @see arch/x86_64/scripts/kernel.ld - */ - -#ifndef TEACHOS_X86_64_BOOT_LD_H -#define TEACHOS_X86_64_BOOT_LD_H +//! @file +//! The interface to linker script defined symbols. +//! +//! This header provides declarations for symbols that are defined in the linker script itself. The symbols declared +//! here provide important information, for example the start and end of the kernel image in virtual and physical +//! memory. +//! +//! Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a +//! pointer to the memory location the represent. +//! +//! @note The symbols declared in this header are declared using C-language linkage in order to suppress name mangling. +//! +//! @see arch/x86_64/scripts/kernel.ld + +#ifndef TEACHOS_X86_64_BOOT_LD_HPP +#define TEACHOS_X86_64_BOOT_LD_HPP #include <cstddef> @@ -22,55 +22,40 @@ namespace teachos::boot::x86_64 extern "C" { - /** - * @brief The first byte of the loaded kernel image. - * - * @details - * This symbol is defined in the kernel linker script and marks the start of the kernel image in physical memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - * - * @see _end_physical - */ + //! The beginning of the kernel image in physical memory + //! + //! This symbol marks the start of the kernel image in physical memory. + //! + //! @see _end_physical extern std::byte _start_physical; - /** - * @brief The first byte after the loaded kernel image. - * - * @details - * This symbol is defined in the kernel linker script and marks the end of the kernel image in physical memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - * - * @see _start_physical - */ + //! The first byte after the loaded kernel image. + //! + //! This symbol marks the end of the kernel image in physical memory. + //! + //! @see _start_physical extern std::byte _end_physical; - /** - * @brief The first byte of the loaded kernel image in the virtual address space. - * - * @details - * This symbol is defined in the kernel linker script and marks the start of the kernel image in virtual memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - */ + //! The first byte of the loaded kernel image in the virtual address space. + //! + //! This symbol and marks the start of the kernel image in virtual memory. + //! + //! @see _end_virtual extern std::byte _start_virtual; - /** - * @brief The first byte after the loaded kernel image in the virtual address space. - * - * @details - * This symbol is defined in the kernel linker script and marks the end of the kernel image in virtual memory. - * To use this symbol for its intended purpose, the address of it shall be taken. - */ + //! The first byte after the loaded kernel image in the virtual address space. + //! + //! This symbol marks the end of the kernel image in virtual memory. + //! + //! @see _start_virtual extern std::byte _end_virtual; - /** - * @brief The first byte of the kernel's virtual address space. - * - * @details - * This symbol is defined in the kernel linker script and marks beginning of the kernel virtual address space. To - * use this symbol for its intended purpose, the address of it shall be taken. - */ + //! The first byte of the kernel's virtual address space. + //! + //! This symbol marks beginning of the kernel virtual address space. extern std::byte TEACHOS_VMA; } + } // namespace teachos::boot::x86_64 #endif diff --git a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp index 0c2254a..c2ad3e7 100644 --- a/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp +++ b/arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp @@ -13,141 +13,248 @@ namespace teachos::cpu::x86_64 { - namespace impl { + //! The assembler templates used to access (r/w) CR0; constexpr auto static cr0_asm = std::pair<std::string_view, std::string_view>{"mov %%cr0, %0", "mov %0, %%cr0"}; - constexpr auto static cr2_asm = std::pair<std::string_view, std::string_view>{"mov %%cr2, %0", "mov %0, %%cr2"}; - constexpr auto static cr3_asm = std::pair<std::string_view, std::string_view>{"mov %%cr3, %0", "mov %0, %%cr3"}; - - template<typename Derived, typename ValueType, typename = void> - struct control_register_with_flags - { - }; - template<typename Derived, typename ValueType> - struct control_register_with_flags<Derived, ValueType, std::enable_if_t<std::is_enum_v<ValueType>>> - { - using flags = ValueType; - - auto static set(ValueType value) -> void - { - auto current = Derived::read(); - current |= value; - Derived::write(current); - } - - auto static clear(ValueType value) -> void - { - auto current = Derived::read(); - current &= ~value; - Derived::write(current); - } - }; - - template<typename ValueType, auto AssemblerTemplates> - struct control_register : control_register_with_flags<control_register<ValueType, AssemblerTemplates>, ValueType> - { - [[nodiscard]] auto static read() -> ValueType - { - auto value = ValueType{}; - asm volatile((AssemblerTemplates->first) : "=r"(value)); - return value; - } - - auto static write(ValueType value) -> void - { - asm volatile((AssemblerTemplates->second) : : "r"(value)); - } - }; - - enum struct cr0_flags : uint64_t - { - //! Enable protected mode. - protection_enable = 1uz << 0, - //! Enable wait-monitoring of the coprocessor after task switching. - monitor_coprocessor = 1uz << 1, - //! Emulate floating point coprocessor. - emulation = 1uz << 2, - //! Marks that a task switch has occurred. - task_switched = 1uz << 3, - //! Marks Intel 387 DX math coprocessor as available - extension_type = 1uz << 4, - //! Numeric error handling mode. - numeric_error = 1uz << 5, - //! Disable writing to read-only marked memory. - write_protect = 1uz << 16, - //! Enable Ring-3 alignment checks - alignment_check = 1uz << 18, - //! Disable write through - not_write_through = 1uz << 29, - //! Disable caching of memory accesses - cache_disable = 1uz << 30, - //! Enable paging - paging = 1uz << 31 - }; - - enum struct cr3_flags : std::uint64_t - { - page_level_write_through = 1uz << 0, - page_level_cache_disable = 1uz << 1, - }; - - struct cr3_value - { - constexpr cr3_value() = default; - - constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast<cr3_flags>(0)) - : m_flags{static_cast<std::uint64_t>(flags)} - , m_address{static_cast<std::uint64_t>(address.raw())} - {} - - [[nodiscard]] constexpr auto address() const -> memory::physical_address - { - return memory::physical_address{m_address}; - } - - constexpr auto address(memory::physical_address address) -> void - { - m_address = static_cast<std::uint64_t>(address.raw()); - } - - [[nodiscard]] constexpr auto flags() const -> cr3_flags - { - return static_cast<cr3_flags>(m_flags); - } - - constexpr auto flags(cr3_flags flags) -> void - { - m_flags = static_cast<std::uint64_t>(flags); - } - - private: - std::uint64_t : 3; - std::uint64_t m_flags : 2 {}; - std::uint64_t : 7; - std::uint64_t m_address : 52 {}; - }; + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair<std::string_view, std::string_view>{"mov %%cr2, %0", "mov %0, %%cr2"}; + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair<std::string_view, std::string_view>{"mov %%cr3, %0", "mov %0, %%cr3"}; } // namespace impl - using cr0 = impl::control_register<impl::cr0_flags, &impl::cr0_asm>; - using cr2 = impl::control_register<memory::linear_address, &impl::cr2_asm>; - using cr3 = impl::control_register<impl::cr3_value, &impl::cr3_asm>; + //! The flags that can be set on CR0 configuration register. + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; } // namespace teachos::cpu::x86_64 namespace kstd::ext { template<> - struct is_bitfield_enum<teachos::cpu::x86_64::impl::cr0_flags> : std::true_type + struct is_bitfield_enum<teachos::cpu::x86_64::cr0_flags> : std::true_type { }; template<> - struct is_bitfield_enum<teachos::cpu::x86_64::impl::cr3_flags> : std::true_type + struct is_bitfield_enum<teachos::cpu::x86_64::cr3_flags> : std::true_type { }; } // namespace kstd::ext +namespace teachos::cpu::x86_64 +{ + //! A mixin for flag-oriented control registers. + //! + //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A + //! control register is flag-oriented, when it's comprises is a bitfield and zero or more additional non-bitfield + //! parts. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template<typename Derived, typename ValueType, typename = void> + struct control_register_with_flags + { + }; + + //! @copydoc control_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the control register + //! is an enum. + template<typename Derived, typename ValueType> + struct control_register_with_flags<Derived, ValueType, std::enable_if_t<kstd::ext::bitfield_enum<ValueType>>> + { + //! The type of the flags used by this control register + using flags = ValueType; + + //! Set one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be set in the control register. + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + //! Clear one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be cleared in the control register. + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + //! A CPU control register. + //! + //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU + //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU + //! in an invalid/undefined state. + template<typename ValueType, auto AssemblerTemplates> + struct control_register : control_register_with_flags<control_register<ValueType, AssemblerTemplates>, ValueType> + { + //! Read the current value of the control register. + //! + //! @return The currently set value of the control register. + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + //! Write a new value to the control register. + //! + //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value The new value to write to the control register. + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + //! The value type of the CR3 control register. + //! + //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the + //! page aligned physical address of the root page map, as well as the root paging configuration flags. + struct cr3_value + { + //! Contstruct a 0-value CR3 value. + constexpr cr3_value() = default; + + //! Construct a CR3 value using the given root page map address and flags. + //! + //! @param address The physical address of the root page map + //! @param flags The root configuration flags of the paging system. + constexpr cr3_value(memory::physical_address address, cr3_flags flags = static_cast<cr3_flags>(0)) + : m_flags{static_cast<std::uint64_t>(flags)} + , m_address{static_cast<std::uint64_t>(address.raw())} + {} + + //! Extract the physical address of the root page map from this value. + //! + //! @return The physical address of the root page map. + [[nodiscard]] constexpr auto address() const -> memory::physical_address + { + return memory::physical_address{m_address}; + } + + //! Encode the page aligned physical address of the root page map into this value. + //! + //! @param address The page aligned physical address of the root page map. + constexpr auto address(memory::physical_address address) -> void + { + m_address = static_cast<std::uint64_t>(address.raw()); + } + + //! Extract the root paging configuration flags from this value. + //! + //! @return The root paging configuration flags. + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast<cr3_flags>(m_flags); + } + + //! Encode the root paging configuration flags into this value. + //! + //! @param flags The root paging configuration flags. + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast<std::uint64_t>(flags); + } + + //! Add the given flags to the current set of encoded root configuration flags of this value. + //! + //! @param flags The root configuration flags to add. + //! @return A reference to this value. + constexpr auto operator|=(cr3_flags flags) -> cr3_value & + { + m_flags |= static_cast<std::uint64_t>(flags); + return *this; + } + + //! Mask the root configuration flags of this value. + //! + //! @param mask The mask to apply to the root configuration flags. + //! @return A reference to this value. + constexpr auto operator&=(cr3_flags mask) -> cr3_value & + { + m_flags &= static_cast<std::uint64_t>(mask); + return *this; + } + + private: + //! Reserved bits. + std::uint64_t : 3; + //! The root paging configuration flags. + std::uint64_t m_flags : 2 {}; + //! Reserved bits. + std::uint64_t : 7; + //! The page aligned physical address of the root page map. + std::uint64_t m_address : 52 {}; + }; + + static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); + + //! Configuration Register 0. + //! + //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. + using cr0 = control_register<cr0_flags, &impl::cr0_asm>; + + //! Configuration Register 2. + //! + //! This configuration register holds the memory address the access to which has triggered the most recent page fault. + using cr2 = control_register<memory::linear_address, &impl::cr2_asm>; + + //! Configuration Register 3. + //! + //! This register holds the configuration of the virtual memory protection configuration. + using cr3 = control_register<cr3_value, &impl::cr3_asm>; + +} // namespace teachos::cpu::x86_64 + #endif
\ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp index 2d05010..d7e9491 100644 --- a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp @@ -35,7 +35,7 @@ namespace teachos::memory::x86_64 auto operator=(buffered_allocator const &) = delete; auto operator=(buffered_allocator &&) = delete; - auto allocate() -> std::optional<frame> override + auto allocate() noexcept -> std::optional<frame> override { auto found = std::ranges::find_if(m_pool, [](auto const & candidate) { return candidate.has_value(); }); if (found == std::end(m_pool)) 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 6a9c045..6c102b7 100644 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ b/arch/x86_64/include/x86_64/memory/page_table.hpp @@ -16,60 +16,103 @@ namespace teachos::memory::x86_64 { - //! A table containing paging entries. + //! A table containing page mapping entries. + //! + //! Page tables exist in a multi-level hierarchy and are used to map pages (virtual memory) onto frames (physical + //! memory). Conceptually, pages represent the data found in a virtual address space, while frames represent their + //! storage. Only a level 1 page table maps an actual page onto a frame. All other page tables on higher levels do not + //! map payload pages, but rather their subordinate page tables. struct page_table { - //! A single page table entry + //! An entry in a page table. + //! + //! A page table entry is a combination of a frame number and a set of flags that determine the properties and + //! access rights to a mapped page. Entries at a higher level in the page hierarchy do not map a page directly, + //! unless that page is marked as huge on the relevant level, but rather map the next lower page table. struct entry { //! Flags marking the state and configuration of an entry. + //! + //! An entry in a page table may have any combination of these flags active at the same time. The final flags of a + //! page are determined as the strictest combination (logical AND) of all flags along the hierarchy. + //! + //! @note This is a bitfield enum as defined by kstd::ext::bitfield_enum. enum struct flags : std::uint64_t { empty = 0, - present = 1uz << 0, - writable = 1uz << 1, - user_accessible = 1uz << 2, - write_through = 1uz << 3, - disable_cache = 1uz << 4, - accessed = 1uz << 5, - dirty = 1uz << 6, - huge_page = 1uz << 7, - global = 1uz << 8, - no_execute = 1uz << 63, + present = 1uz << 0, //!< The page is mapped. + writable = 1uz << 1, //!< The page is writable. + user_accessible = 1uz << 2, //!< The page is accessible in user mode. + write_through = 1uz << 3, //!< Any writes to the page must immediately hit memory. + disable_cache = 1uz << 4, //!< Any writes to the page must never be cached. + accessed = 1uz << 5, //!< The page was accessed. + dirty = 1uz << 6, //!< The page was written to. + huge_page = 1uz << 7, //!< The page is huge. + global = 1uz << 8, //!< The TLB entry for this page must not be flushed on context switches. + no_execute = 1uz << 63, //!< The data in this page must not be executed. }; + //! Construct an empty entry. entry() = default; - //! Clear this entry, ensuring all information is set to zero, marking the page represented by this entry as not - //! present. - auto clear() -> void; - - //! Check if the page represented by this entry is present at the containing page table's level. - [[nodiscard]] auto present() const -> bool; - - //! Check if the page represented by this entry is huge (2MiB, or 1GiB, depending on the containing page table's - //! level). - [[nodiscard]] auto huge() const -> bool; + //! Clear this entry, ensuring all information is set to zero. + //! + //! This effectively marks the page represented by this entry as not present. In addition, it also removes any + //! information about the frame referenced by this entry. + auto clear() noexcept -> void; + + //! Check if the page represented by this entry is present. + //! + //! @note This function does not attempt to walk the page table hierarchy, but only performs a local check. This + //! means, that if the page is not mapped at a lower level, this will not be detected. + //! + //! @return @p true iff. the page is present at this level, @p false otherwise. + [[nodiscard]] auto present() const noexcept -> bool; + + //! Check if the page represented by this entry is a huge page. + //! + //! @note The effective size of the page depends on the level of the page table containing this entry. + //! + //! @return @p true iff. the page is marked as being huge, @p false otherwise. + [[nodiscard]] auto huge() const noexcept -> bool; //! Get all flags present in this entry. - [[nodiscard]] auto all_flags() const -> flags; + //! + //! @return The flags that are currently set on this entry. + [[nodiscard]] auto all_flags() const noexcept -> flags; - //! Set the flags of this entry to the given set. - auto all_flags(flags flags) -> void; + //! Set all flags of this entry. + //! + //! @param flags The flags to apply to this entry. + auto all_flags(flags flags) noexcept -> void; - //! Additively set the given flags in this entry. - auto operator|=(flags rhs) -> entry &; + //! Add the given flags to the flags of this entry. + //! + //! @param rhs The flags to add to this entry's flags. + //! @return A reference to this entry. + auto operator|=(flags rhs) noexcept -> entry &; //! Get the frame number associated with this entry, if the referenced page is present. - [[nodiscard]] auto frame() const -> std::optional<frame>; + //! + //! @return an engaged std::optional iff. this entry maps a page, std::nullopt otherwise. + [[nodiscard]] auto frame() const noexcept -> std::optional<frame>; - //! Set the entry to reference the given frame with the given flags. - auto frame(struct frame frame, flags flags) -> void; + //! Map this entry. + //! + //! @param frame The frame to map in this entry. + //! @param flags The flags to apply to this entry. + auto frame(struct frame frame, flags flags) noexcept -> void; private: //! A mask to retrieve, or exclude, the frame number from the raw entry. + //! + //! Page table entries in x86_64 are a compacted combination of the relevant flags and the frame number. This mask + //! represents the bits that make up the frame number in an entry. constexpr auto static frame_number_mask{0x000f'ffff'ffff'f000uz}; + //! The raw entry bytes. + //! + //! @see entry::frame_number_mask std::uint64_t m_raw{}; }; @@ -77,13 +120,24 @@ namespace teachos::memory::x86_64 constexpr auto static entry_count{page::size / sizeof(entry)}; //! Get the entry at the given index. + //! + //! @warning This function will panic if the entry index is out of bounds. + //! + //! @param index The index of the desired entry. + //! @return A reference to the entry at the given index. [[nodiscard]] auto operator[](std::size_t index) -> entry &; + + //! @copydoc page_table::operator[] [[nodiscard]] auto operator[](std::size_t index) const -> entry const &; - //! Clear the entire page table, effectively evicting all entries. - auto clear() -> void; + //! Clear the entire page table. + //! + //! This function effectively marks the page table as not mapping any pages. + auto clear() noexcept -> void; //! Check if the page table is empty. + //! + //! @return @p true iff. this page table has no entries marked present, @p false otherwise. [[nodiscard]] auto empty() const noexcept -> bool; private: @@ -91,6 +145,10 @@ namespace teachos::memory::x86_64 }; //! A recursively mapped page table. + //! + //! A page table in which at least one entry maps the same table. Recursive page tables allow for easy access to other + //! tables within the page mapping hierarchy, without having to map them prior to access, through careful construction + //! of linear addresses that pass through the same index multiple times. template<std::size_t Level> requires(Level > 0uz && Level < 5uz) struct recursive_page_table : page_table @@ -99,7 +157,12 @@ namespace teachos::memory::x86_64 constexpr auto static recursive_index = 0776uz; //! Get the next lower lever table. - [[nodiscard]] auto next(this auto && self, std::size_t index) + //! + //! @param self The object type of this object. + //! @param index The index corresponding to the desired page map. + //! @return An engaged std::optional holding a pointer to the next lower page table iff. the next lower page table + //! at the desired index is present, std::nullopt otherwise. + [[nodiscard]] auto next(this auto && self, std::size_t index) noexcept requires(next_level > 1) { return self.next_address(index).transform([](auto address) -> auto { @@ -108,8 +171,10 @@ namespace teachos::memory::x86_64 }); } - //! Get the next lower lever table. - [[nodiscard]] auto next(this auto && self, std::size_t index) + //! @copydoc recursive_page_table::next + //! + //! @note This overload returns a non-hierarchical, or leaf, page table + [[nodiscard]] auto next(this auto && self, std::size_t index) noexcept requires(next_level == 1) { return self.next_address(index).transform([](auto address) -> auto { @@ -119,11 +184,19 @@ namespace teachos::memory::x86_64 } private: + //! The number of address bits used to represent the page index per level. constexpr auto static level_bits = 9; + //! The highest address bit. constexpr auto static high_bit = 48; + //! The number of bits representing the offset into a page. constexpr auto static offset_bits = 12; - [[nodiscard]] auto next_address(std::size_t index) const -> std::optional<std::uintptr_t> + //! Calculate the recursive address of the next lower page table. + //! + //! @param index The index of the desired page table. + //! @return An engaged std::optional holding the address of the new lower page table iff. the next lower page table + //! at the desired index is present, std::nullopt otherwise. + [[nodiscard]] auto next_address(std::size_t index) const noexcept -> std::optional<std::uintptr_t> { if (auto entry = (*this)[index]; entry.present() && !entry.huge()) { 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 ed95106..75ba120 100644 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ b/arch/x86_64/include/x86_64/memory/paging_root.hpp @@ -36,7 +36,7 @@ namespace teachos::memory::x86_64 //! //! @warning If the page has not previously been mapped, this function will panic. //! @param page The page to unmap - auto unmap(page) -> void; + auto unmap(page page) -> void; private: paging_root() = default; diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp index 3ddc8b6..84b7a94 100644 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ b/arch/x86_64/include/x86_64/memory/region_allocator.hpp @@ -12,76 +12,66 @@ namespace teachos::memory::x86_64 { - /** - * @brief Allocates memory linearly using memory areas read from the multiboot2 information pointer and leaks any - * deallocated frames. - */ + //! A simple, memory-region based frame allocator. + //! + //! This frame allocator linearly allocates frames that are in available memory regions. It automatically skips any + //! frames occupied by the kernel image or any bootloader provided data. + //! + //! @note This allocator will never release frames. struct region_allocator final : frame_allocator { struct memory_information { - //! The memory range occupied by the loaded kernel image. This includes all sections that are marked as occupying - //! space in the kernel executable. The internal structure of this area is more described in a more fine-grained - //! manner by the ELF symbol information provided in the Multiboot2 information by the loader. + //! The memory range occupied by the loaded kernel image. + //! + //! This includes all sections that are marked as occupying space in the kernel executable. The internal structure + //! of this area is more described in a more fine-grained manner by the ELF symbol information provided in the + //! Multiboot2 information by the loader. std::pair<physical_address, physical_address> image_range; - //! The memory range occupied by the loader supplied Multiboot2 information structure. In general, this - //! information is allocated somewhere in the range of the loaded image, but the loader protocol does not - //! guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to allocate the - //! information structure outside of the image range. + //! The memory range occupied by the loader supplied Multiboot2 information structure. + //! + //! In general, this information is allocated somewhere in the range of the loaded image, but the loader protocol + //! does not guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to + //! allocate the information structure outside of the image range. std::pair<physical_address, physical_address> mbi_range; - //! The loader supplied map of memory regions. These include available, unavailable, and reclaimable regions. In - //! general, only frames that are located in non-reserved (as in available) regions should be allocated for page - //! storage. + //! The loader supplied map of memory regions. + //! + //! These include available, unavailable, and reclaimable regions. In general, only frames that are located in + //! non-reserved (as in available) regions should be allocated for page storage. multiboot2::memory_map memory_map; }; - /** - * @brief Constructor. - * - * @param mem_info Structure containing all relevant information to map and allocate memory. - */ - explicit region_allocator(memory_information const & mem_info); + using region = multiboot2::memory_map::region; - /** - * @brief Allocate memory by finding and returning a free physical frame. - * - * @note 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 - * - * @return next free physical frame or nullopt if none was found. - */ - auto allocate() -> std::optional<frame> override; + //! Construct a new allocator using the provided memory information + //! + //! @param information The description of the detected memory regions as well as regions that are already occupied. + explicit region_allocator(memory_information const & information); - /** - * @brief Deallocates a previously allocated physical frame. - * - * @note Simply does nothing, because the simp |
