aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-12-11 17:46:02 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-12-11 17:46:02 +0100
commit998a001fc621ca0e7560ca09a8acd29469ae3373 (patch)
tree435a9042ea4a1185c360e8eb92a6d2f082ed3224 /arch
parenteafbf588760c289b7f54a4771b39af0ccfe8cf59 (diff)
downloadteachos-998a001fc621ca0e7560ca09a8acd29469ae3373.tar.xz
teachos-998a001fc621ca0e7560ca09a8acd29469ae3373.zip
docs: improve documentation
Diffstat (limited to 'arch')
-rw-r--r--arch/x86_64/include/x86_64/boot/ld.hpp95
-rw-r--r--arch/x86_64/include/x86_64/cpu/impl/control_registers.hpp339
-rw-r--r--arch/x86_64/include/x86_64/memory/buffered_allocator.hpp2
-rw-r--r--arch/x86_64/include/x86_64/memory/page_table.hpp145
-rw-r--r--arch/x86_64/include/x86_64/memory/paging_root.hpp2
-rw-r--r--arch/x86_64/include/x86_64/memory/region_allocator.hpp94
-rw-r--r--arch/x86_64/src/memory/page_table.cpp18
-rw-r--r--arch/x86_64/src/memory/region_allocator.cpp2
-rw-r--r--arch/x86_64/x86_64.dox14
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