From 7d6f0ed063790042a808f4bf07c50d308b3f2de4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 16 Jan 2026 13:36:38 +0100 Subject: chore: restructure namespaces --- arch/x86_64/CMakeLists.txt | 10 +- arch/x86_64/include/arch/boot/boot.hpp | 70 +++++ arch/x86_64/include/arch/boot/ld.hpp | 61 ++++ arch/x86_64/include/arch/cpu/control_register.hpp | 248 +++++++++++++++ arch/x86_64/include/arch/cpu/interrupts.hpp | 61 ++++ .../include/arch/cpu/model_specific_register.hpp | 151 +++++++++ arch/x86_64/include/arch/cpu/registers.hpp | 32 ++ arch/x86_64/include/arch/cpu/segment_selector.hpp | 21 ++ arch/x86_64/include/arch/debug/qemu_output.hpp | 43 +++ arch/x86_64/include/arch/device_io/port_io.hpp | 107 +++++++ .../include/arch/memory/buffered_allocator.hpp | 138 +++++++++ arch/x86_64/include/arch/memory/kernel_mapper.hpp | 34 ++ arch/x86_64/include/arch/memory/mmu.hpp | 27 ++ arch/x86_64/include/arch/memory/page_table.hpp | 342 +++++++++++++++++++++ arch/x86_64/include/arch/memory/page_utilities.hpp | 22 ++ arch/x86_64/include/arch/memory/paging_root.hpp | 27 ++ .../include/arch/memory/recursive_page_mapper.hpp | 25 ++ .../include/arch/memory/region_allocator.hpp | 84 +++++ arch/x86_64/include/arch/memory/scoped_mapping.hpp | 68 ++++ arch/x86_64/include/arch/vga/crtc.hpp | 35 +++ arch/x86_64/include/arch/vga/text.hpp | 10 + arch/x86_64/include/arch/vga/text/attribute.hpp | 30 ++ arch/x86_64/include/arch/vga/text/buffer.hpp | 101 ++++++ arch/x86_64/include/arch/vga/text/color.hpp | 35 +++ .../include/arch/vga/text/common_attributes.hpp | 37 +++ arch/x86_64/include/arch/vga/text/device.hpp | 45 +++ arch/x86_64/include/arch/vga/text/flags.hpp | 38 +++ arch/x86_64/include/x86_64/boot/boot.hpp | 70 ----- arch/x86_64/include/x86_64/boot/ld.hpp | 61 ---- .../x86_64/include/x86_64/cpu/control_register.hpp | 248 --------------- arch/x86_64/include/x86_64/cpu/interrupts.hpp | 61 ---- .../include/x86_64/cpu/model_specific_register.hpp | 151 --------- arch/x86_64/include/x86_64/cpu/registers.hpp | 32 -- .../x86_64/include/x86_64/cpu/segment_selector.hpp | 20 -- arch/x86_64/include/x86_64/debug/qemu_output.hpp | 43 --- arch/x86_64/include/x86_64/device_io/port_io.hpp | 107 ------- .../include/x86_64/memory/buffered_allocator.hpp | 137 --------- .../x86_64/include/x86_64/memory/kernel_mapper.hpp | 33 -- arch/x86_64/include/x86_64/memory/mmu.hpp | 27 -- arch/x86_64/include/x86_64/memory/page_table.hpp | 341 -------------------- .../include/x86_64/memory/page_utilities.hpp | 22 -- arch/x86_64/include/x86_64/memory/paging_root.hpp | 27 -- .../x86_64/memory/recursive_page_mapper.hpp | 25 -- .../include/x86_64/memory/region_allocator.hpp | 83 ----- .../include/x86_64/memory/scoped_mapping.hpp | 66 ---- arch/x86_64/include/x86_64/vga/crtc.hpp | 37 --- arch/x86_64/include/x86_64/vga/text.hpp | 10 - arch/x86_64/include/x86_64/vga/text/attribute.hpp | 30 -- arch/x86_64/include/x86_64/vga/text/buffer.hpp | 101 ------ arch/x86_64/include/x86_64/vga/text/color.hpp | 35 --- .../include/x86_64/vga/text/common_attributes.hpp | 37 --- arch/x86_64/include/x86_64/vga/text/device.hpp | 45 --- arch/x86_64/include/x86_64/vga/text/flags.hpp | 38 --- arch/x86_64/kapi/cio.cpp | 20 ++ arch/x86_64/kapi/cpu.cpp | 12 + arch/x86_64/kapi/memory.cpp | 194 ++++++++++++ arch/x86_64/src/boot/boot32.S | 2 +- arch/x86_64/src/boot/initialize_runtime.cpp | 23 +- arch/x86_64/src/debug/qemu_output.cpp | 8 +- arch/x86_64/src/kapi/cio.cpp | 20 -- arch/x86_64/src/kapi/cpu.cpp | 12 - arch/x86_64/src/kapi/memory.cpp | 192 ------------ arch/x86_64/src/memory/kernel_mapper.cpp | 36 +-- arch/x86_64/src/memory/mmu.cpp | 12 +- arch/x86_64/src/memory/page_table.cpp | 12 +- arch/x86_64/src/memory/paging_root.cpp | 6 +- arch/x86_64/src/memory/recursive_page_mapper.cpp | 30 +- arch/x86_64/src/memory/region_allocator.cpp | 30 +- arch/x86_64/src/memory/scoped_mapping.cpp | 22 +- arch/x86_64/src/vga/text/buffer.cpp | 8 +- arch/x86_64/src/vga/text/device.cpp | 24 +- kapi/include/kapi/boot.hpp | 4 +- kapi/include/kapi/cio.hpp | 4 +- kapi/include/kapi/cio/output_device.hpp | 4 +- kapi/include/kapi/cpu.hpp | 4 +- kapi/include/kapi/memory.hpp | 4 +- kapi/include/kapi/memory/address.hpp | 12 +- kapi/include/kapi/memory/chunk.hpp | 4 +- kapi/include/kapi/memory/frame.hpp | 4 +- kapi/include/kapi/memory/frame_allocator.hpp | 4 +- kapi/include/kapi/memory/page.hpp | 4 +- kapi/include/kapi/memory/page_mapper.hpp | 6 +- kapi/include/kapi/system.hpp | 4 +- kernel/CMakeLists.txt | 15 +- kernel/kapi/cio.cpp | 36 +++ kernel/kapi/memory.cpp | 94 ++++++ kernel/kapi/system.cpp | 21 ++ kernel/kstd/os.cpp | 16 + kernel/kstd/print.cpp | 145 +++++++++ kernel/src/kapi/cio.cpp | 36 --- kernel/src/kapi/memory.cpp | 94 ------ kernel/src/kapi/system.cpp | 21 -- kernel/src/kstd/os.cpp | 16 - kernel/src/kstd/print.cpp | 145 --------- kernel/src/main.cpp | 6 +- 95 files changed, 2585 insertions(+), 2570 deletions(-) create mode 100644 arch/x86_64/include/arch/boot/boot.hpp create mode 100644 arch/x86_64/include/arch/boot/ld.hpp create mode 100644 arch/x86_64/include/arch/cpu/control_register.hpp create mode 100644 arch/x86_64/include/arch/cpu/interrupts.hpp create mode 100644 arch/x86_64/include/arch/cpu/model_specific_register.hpp create mode 100644 arch/x86_64/include/arch/cpu/registers.hpp create mode 100644 arch/x86_64/include/arch/cpu/segment_selector.hpp create mode 100644 arch/x86_64/include/arch/debug/qemu_output.hpp create mode 100644 arch/x86_64/include/arch/device_io/port_io.hpp create mode 100644 arch/x86_64/include/arch/memory/buffered_allocator.hpp create mode 100644 arch/x86_64/include/arch/memory/kernel_mapper.hpp create mode 100644 arch/x86_64/include/arch/memory/mmu.hpp create mode 100644 arch/x86_64/include/arch/memory/page_table.hpp create mode 100644 arch/x86_64/include/arch/memory/page_utilities.hpp create mode 100644 arch/x86_64/include/arch/memory/paging_root.hpp create mode 100644 arch/x86_64/include/arch/memory/recursive_page_mapper.hpp create mode 100644 arch/x86_64/include/arch/memory/region_allocator.hpp create mode 100644 arch/x86_64/include/arch/memory/scoped_mapping.hpp create mode 100644 arch/x86_64/include/arch/vga/crtc.hpp create mode 100644 arch/x86_64/include/arch/vga/text.hpp create mode 100644 arch/x86_64/include/arch/vga/text/attribute.hpp create mode 100644 arch/x86_64/include/arch/vga/text/buffer.hpp create mode 100644 arch/x86_64/include/arch/vga/text/color.hpp create mode 100644 arch/x86_64/include/arch/vga/text/common_attributes.hpp create mode 100644 arch/x86_64/include/arch/vga/text/device.hpp create mode 100644 arch/x86_64/include/arch/vga/text/flags.hpp delete mode 100644 arch/x86_64/include/x86_64/boot/boot.hpp delete mode 100644 arch/x86_64/include/x86_64/boot/ld.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/control_register.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/interrupts.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/model_specific_register.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/registers.hpp delete mode 100644 arch/x86_64/include/x86_64/cpu/segment_selector.hpp delete mode 100644 arch/x86_64/include/x86_64/debug/qemu_output.hpp delete mode 100644 arch/x86_64/include/x86_64/device_io/port_io.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/buffered_allocator.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/kernel_mapper.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/mmu.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/page_table.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/page_utilities.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/paging_root.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/region_allocator.hpp delete mode 100644 arch/x86_64/include/x86_64/memory/scoped_mapping.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/crtc.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text/attribute.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text/buffer.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text/color.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text/common_attributes.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text/device.hpp delete mode 100644 arch/x86_64/include/x86_64/vga/text/flags.hpp create mode 100644 arch/x86_64/kapi/cio.cpp create mode 100644 arch/x86_64/kapi/cpu.cpp create mode 100644 arch/x86_64/kapi/memory.cpp delete mode 100644 arch/x86_64/src/kapi/cio.cpp delete mode 100644 arch/x86_64/src/kapi/cpu.cpp delete mode 100644 arch/x86_64/src/kapi/memory.cpp create mode 100644 kernel/kapi/cio.cpp create mode 100644 kernel/kapi/memory.cpp create mode 100644 kernel/kapi/system.cpp create mode 100644 kernel/kstd/os.cpp create mode 100644 kernel/kstd/print.cpp delete mode 100644 kernel/src/kapi/cio.cpp delete mode 100644 kernel/src/kapi/memory.cpp delete mode 100644 kernel/src/kapi/system.cpp delete mode 100644 kernel/src/kstd/os.cpp delete mode 100644 kernel/src/kstd/print.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 519fb93..413b5aa 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -11,6 +11,11 @@ target_link_libraries("x86_64" PUBLIC ) target_sources("x86_64" PRIVATE + # Platform-dependent KAPI implementation + "kapi/cio.cpp" + "kapi/cpu.cpp" + "kapi/memory.cpp" + # Low-level bootstrap "src/boot/boot32.S" "src/boot/entry64.s" @@ -20,11 +25,6 @@ target_sources("x86_64" PRIVATE # Debug interfaces "src/debug/qemu_output.cpp" - # api::kapi implementation - "src/kapi/cio.cpp" - "src/kapi/cpu.cpp" - "src/kapi/memory.cpp" - # Memory management "src/memory/kernel_mapper.cpp" "src/memory/mmu.cpp" diff --git a/arch/x86_64/include/arch/boot/boot.hpp b/arch/x86_64/include/arch/boot/boot.hpp new file mode 100644 index 0000000..71e8a70 --- /dev/null +++ b/arch/x86_64/include/arch/boot/boot.hpp @@ -0,0 +1,70 @@ +#ifndef TEACHOS_X86_64_BOOT_BOOT_H +#define TEACHOS_X86_64_BOOT_BOOT_H + +#ifdef __ASSEMBLER__ +/* clang-format off */ +/** + * @brief The number of huge pages to map during bootstrap. + */ +#define HUGE_PAGES_TO_MAP (16) + +/** + * @brief The magic value to be set in eax by the multiboot 2 loader. + */ +#define MULTIBOOT2_MAGIC (0x36d76289) + +/** + * @brief The "A" bit in a GDT entry. + */ +#define GDT_ACCESSED (1 << 40) + +/** + * @brief The "R/W" bit in a GDT entry + */ +#define GDT_READ_WRITE (1 << 41) + +/** + * @brief The "E" bit in a GDT entry. + */ +#define GDT_EXECUTABLE (1 << 43) + +/** + * @brief The "S" bit in a GDT entry. + */ +#define GDT_DESCRIPTOR_TYPE (1 << 44) + +/** + * @brief The "P" bit in a GDT entry. + */ +#define GDT_PRESENT (1 << 47) + +/** + * @brief The "L" bit in a GDT entry. + */ +#define GDT_LONG_MODE (1 << 53) +/* clang-format on */ +#else + +#include "kapi/boot.hpp" // IWYU pragma: export + +#include + +#include + +namespace kapi::boot +{ + + struct information + { + //! A pointer to the loader provided Multiboot2 Information structure. + multiboot2::information_view const * mbi; + + //! The index of the next character to be written in the VGA text buffer after handoff. + std::size_t vga_buffer_index; + }; + +} // namespace kapi::boot + +#endif + +#endif diff --git a/arch/x86_64/include/arch/boot/ld.hpp b/arch/x86_64/include/arch/boot/ld.hpp new file mode 100644 index 0000000..988723d --- /dev/null +++ b/arch/x86_64/include/arch/boot/ld.hpp @@ -0,0 +1,61 @@ +//! @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 + +namespace arch::boot +{ + + extern "C" + { + //! 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; + + //! 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; + + //! 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; + + //! 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; + + //! 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 arch::boot + +#endif diff --git a/arch/x86_64/include/arch/cpu/control_register.hpp b/arch/x86_64/include/arch/cpu/control_register.hpp new file mode 100644 index 0000000..681dc5f --- /dev/null +++ b/arch/x86_64/include/arch/cpu/control_register.hpp @@ -0,0 +1,248 @@ +#ifndef TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP + +// IWYU pragma: private, include "arch/cpu/registers.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include + +namespace arch::cpu +{ + namespace impl + { + //! The assembler templates used to access (r/w) CR0; + constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; + + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; + } // namespace impl + + //! 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 arch::cpu + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; + + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace arch::cpu +{ + //! 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, if it comprises 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 + struct control_register_with_flags + { + private: + constexpr control_register_with_flags() noexcept = default; + friend Derived; + }; + + //! @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 + struct control_register_with_flags>> + { + //! 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 + struct control_register : control_register_with_flags, 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(kapi::memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(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 -> kapi::memory::physical_address + { + return kapi::memory::physical_address{m_address}; + } + + //! Encode the frame aligned physical address of the root page map into this value. + //! + //! @param frame The frame containing a PML4. + constexpr auto frame(kapi::memory::frame frame) -> void + { + m_address = static_cast(frame.number()); + } + + //! 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(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(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(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(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)); + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp new file mode 100644 index 0000000..92c5824 --- /dev/null +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -0,0 +1,61 @@ +#ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP +#define TEACHOS_X86_64_CPU_INTERRUPTS_HPP + +#include "arch/cpu/segment_selector.hpp" + +#include +#include +#include + +namespace arch::cpu +{ + + //! The types of supported gates. + enum struct gate_type : std::uint8_t + { + //! A gate entered through the @p INT instruction. + interrupt_gate = 0b1110, + //! A gate entered via an exception. + trap_gate = 0b1111, + }; + + struct alignas(std::uint64_t) gate_descriptor + { + //! The lowest 16 bits of the address of this gate's handler function. + std::uint16_t offset_low : 16; + //! The code segment used when handling the respective interrupt. + segment_selector m_code_segment; + //! The index into the Interrupt Stack Table naming the stack to use. + std::uint8_t interrupt_stack_table_selector : 3; + //! Reserved + std::uint8_t : 5; + //! The type of this gate. + gate_type gate_type : 4; + //! Reserved + std::uint8_t : 1; + //! The privilege level required to enter through this gate. + std::uint8_t descriptor_privilege_level : 2; + //! Whether this gate is present or not. + std::uint8_t present : 1; + //! The middle 16 bits of the address of this gate's handler function. + std::uint16_t offset_middle : 16; + //! The highest 32 bits of the address of this gate's handler function. + std::uint32_t offset_high : 32; + //! Reserved + std::uint32_t : 32; + }; + + static_assert(sizeof(gate_descriptor) == 2 * sizeof(std::uint64_t)); + static_assert(std::is_aggregate_v); + + struct interrupt_descriptor_table + { + interrupt_descriptor_table(); + + private: + std::array m_descriptors{}; + }; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/model_specific_register.hpp b/arch/x86_64/include/arch/cpu/model_specific_register.hpp new file mode 100644 index 0000000..8539a24 --- /dev/null +++ b/arch/x86_64/include/arch/cpu/model_specific_register.hpp @@ -0,0 +1,151 @@ +#ifndef TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP +#define TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP + +// IWYU pragma: private, include "x86_64/cpu/registers.hpp" + +#include + +#include +#include +#include + +namespace arch::cpu +{ + + //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. + enum struct ia32_efer_flags : std::uint64_t + { + //! Enable the syscall and sysret instructions. + syscall_enable = 1uz << 0, + //! Enable IA-32e mode operation. + ia32e_mode_enable = 1uz << 8, + //! Indicates IA-32e mode is active (read-only) + ia32e_mode_active = 1uz << 10, + //! Enable the use of the NX page table bit. + execute_disable_bit_enable = 1uz << 11, + }; + +} // namespace arch::cpu + +namespace kstd::ext +{ + + template<> + struct is_bitfield_enum : std::true_type + { + }; + +} // namespace kstd::ext + +namespace arch::cpu +{ + //! The MSR number for the IA32_EFER MSR + constexpr auto ia32_efer_number = 0xC000'0080u; + + //! A mixin for flag-oriented model specific registers. + //! + //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific + //! register is flag-oriented, if it comprises a single field of bitfield. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct model_specific_register_with_flags + { + private: + constexpr model_specific_register_with_flags() noexcept = default; + friend Derived; + }; + + //! @copydoc model_specific_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the model specific + //! register is a bitfield enum. + template + struct model_specific_register_with_flags>> + { + //! The of the flags used by this model specific register. + using flags = ValueType; + + //! Set one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific 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 flag One or a combination of flags to be set in the model specific register. + auto static set(flags flag) -> void + { + auto current = Derived::read(); + current |= flag; + Derived::write(current); + } + + //! Clear one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific 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 flag One or a combination of flags to be cleared in the model specific register. + auto static clear(flags flag) -> void + { + auto current = Derived::read(); + current &= ~flag; + Derived::write(current); + } + + //! Test one or more flags in this model specific register + //! + //! @param flag One or a combination of flags to test for. + auto test(flags flag) -> flags + { + return Derived::read() & flag; + } + }; + + //! A model specific register (MSR) + //! + //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. + //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in + //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in + //! an invalid/undefined state. + //! + //! @tparam Number The register number of this MSR + //! @tparam ValueType The value type of this MSR + template + struct model_specific_register + : model_specific_register_with_flags, ValueType> + { + //! A raw MSR value, comprising two halfs. + //! + //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. + struct raw_value + { + std::uint32_t low_half; //!< The lower half of the register value + std::uint32_t high_half; //!< The upper half of the register value + }; + + //! Read the current value of this MSR. + //! + //! @return The current value of this MSR. + auto static read() -> ValueType + { + auto raw = raw_value{}; + asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); + return static_cast(std::bit_cast(raw)); + } + + //! Write a new value to this MSR. + //! + //! @param value The new value for this MSR. + auto static write(ValueType value) -> void + { + auto raw = std::bit_cast(static_cast(value)); + asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); + } + }; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/registers.hpp b/arch/x86_64/include/arch/cpu/registers.hpp new file mode 100644 index 0000000..d7def10 --- /dev/null +++ b/arch/x86_64/include/arch/cpu/registers.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_REGISTERS_HPP + +#include "kapi/memory.hpp" + +#include "arch/cpu/control_register.hpp" // IWYU pragma: export +#include "arch/cpu/model_specific_register.hpp" // IWYU pragma: export + +namespace arch::cpu +{ + + //! Configuration Register 0. + //! + //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. + using cr0 = control_register; + + //! 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; + + //! Configuration Register 3. + //! + //! This register holds the configuration of the virtual memory protection configuration. + using cr3 = control_register; + + //! The I32_EFER (Extended Feature Enable Register) MSR + using i32_efer = model_specific_register; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/segment_selector.hpp b/arch/x86_64/include/arch/cpu/segment_selector.hpp new file mode 100644 index 0000000..1a78c47 --- /dev/null +++ b/arch/x86_64/include/arch/cpu/segment_selector.hpp @@ -0,0 +1,21 @@ +#ifndef TEACHOS_X86_64_SEGMENT_SELECTOR_HPP +#define TEACHOS_X86_64_SEGMENT_SELECTOR_HPP + +#include + +namespace arch::cpu +{ + + struct segment_selector + { + std::uint16_t request_privilege_level : 2; + bool use_local_descriptor_table : 1; + std::uint16_t table_index : 13; + }; + + static_assert(sizeof(segment_selector) == sizeof(std::uint16_t)); + static_assert(alignof(segment_selector) == alignof(std::uint16_t)); + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/debug/qemu_output.hpp b/arch/x86_64/include/arch/debug/qemu_output.hpp new file mode 100644 index 0000000..e72eb39 --- /dev/null +++ b/arch/x86_64/include/arch/debug/qemu_output.hpp @@ -0,0 +1,43 @@ +#ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP +#define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP + +#include "kapi/cio.hpp" + +#include "arch/device_io/port_io.hpp" + +#include + +namespace arch::debug +{ + + //! A QEMU debug console output device. + //! + //! This device implements output to the port 0xE9 debug console present in QEMU in Bochs. It is designed to wrap a + //! different output device (e.g. a VGA text output device) and forwards the written data accordingly. + //! + //! @note Support for the device has to be enabled when the emulator is started. The device will try to detect if the + //! port is available. If the port is detected, any output to the device will be written to port before being + //! forwarded to the lower device. + struct qemu_output : kapi::cio::output_device + { + //! The port to write to. + using port = io::port<0xE9, unsigned char, io::port_write, io::port_read>; + + //! Construct a new debug device wrapper for the given output device. + //! + //! @param lower The device to forward the output to. + explicit qemu_output(output_device & lower); + + //! @copydoc kapi::cio::output_device + auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; + + private: + //! The device to forward the output to. + output_device & m_lower; + //! Whether the device has been detected. + bool m_present; + }; + +} // namespace arch::debug + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/device_io/port_io.hpp b/arch/x86_64/include/arch/device_io/port_io.hpp new file mode 100644 index 0000000..65e58e3 --- /dev/null +++ b/arch/x86_64/include/arch/device_io/port_io.hpp @@ -0,0 +1,107 @@ +#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP +#define TEACHOS_X86_64_IO_PORT_IO_HPP + +#include +#include +#include +#include +#include +#include + +namespace arch::io +{ + + //! The requirements imposed on a type usable for port I/O. + template + concept port_io_type = requires { + requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; + requires std::default_initializable; + std::bit_cast( + std::conditional_t>{}); + }; + + template + struct port_read + { + //! Read from the I/O port. + //! + //! @return The data read from the I/O port. + auto static read() noexcept + { + auto data = typename Derived::value_type{}; + asm volatile((code[Derived::size / 2]) + : [data] "=m"(data) + : [port] "i"(Derived::address) + : "dx", (Derived::data_register)); + return data; + } + + private: + constexpr port_read() noexcept = default; + friend Derived; + + //! The assembly templates used for reading from an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, + }; + }; + + template + struct port_write + { + //! Write data to the I/O port. + //! + //! @param data The data to write to the I/O port. + auto static write(std::same_as auto data) noexcept -> void + { + asm volatile((code[Derived::size / 2]) + : + : [port] "i"(Derived::address), [data] "im"(data) + : "dx", (Derived::data_register)); + } + + private: + constexpr port_write() noexcept = default; + friend Derived; + + //! The assembly templates used for writing to an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"}, + }; + }; + + //! An I/O port of a given size at a given address. + //! + //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte + //! to double-word sized transfers. + //! + //! @tparam Address The address (port number) of the I/O port. + //! @tparam Size The size (in bytes) of the I/O port. + //! @tparam Features The features (readable, writeable) + template typename... Features> + requires(sizeof...(Features) > 0) + struct port : Features>... + { + //! The type of the data of this port. + using value_type = ValueType; + + //! The address of this I/O port. + constexpr auto static address = Address; + + //! The size of this I/O port. + constexpr auto static size = sizeof(value_type); + + //! The register clobbered by the I/O operation. + constexpr auto static data_register = size == 1 ? std::string_view{"al"} + : size == 2 ? std::string_view{"ax"} + : std::string_view{"eax"}; + }; + +} // namespace arch::io + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/buffered_allocator.hpp b/arch/x86_64/include/arch/memory/buffered_allocator.hpp new file mode 100644 index 0000000..87ebbf6 --- /dev/null +++ b/arch/x86_64/include/arch/memory/buffered_allocator.hpp @@ -0,0 +1,138 @@ +#ifndef TEACHOS_X86_64_BUFFERED_ALLOCATOR_HPP +#define TEACHOS_X86_64_BUFFERED_ALLOCATOR_HPP + +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include +#include +#include +#include +#include + +namespace arch::memory +{ + + template + struct buffered_allocator : kapi::memory::frame_allocator + { + explicit buffered_allocator(frame_allocator * underlying) + : m_underlying{underlying} + , m_free{BufferSize} + { + auto from_underlying = m_underlying->allocate_many(BufferSize); + if (!from_underlying) + { + kapi::system::panic("[x86_64:MEM] Not enough frames available from underlying allocator."); + } + auto [first_frame, count] = *from_underlying; + std::ranges::generate_n(m_pool.begin(), count, [first_frame]() mutable { return first_frame++; }); + } + + buffered_allocator(buffered_allocator const &) = delete; + buffered_allocator(buffered_allocator && other) noexcept = delete; + + ~buffered_allocator() override + { + std::ranges::for_each(m_pool, [this](auto const & maybe_frame) { + if (maybe_frame) + { + m_underlying->release_many({*maybe_frame, 1}); + } + }); + } + + auto operator=(buffered_allocator const &) = delete; + auto operator=(buffered_allocator &&) = delete; + + auto allocate_many(std::size_t count = 1) noexcept + -> std::optional> override + { + if (count > m_free) + { + return m_underlying->allocate_many(count); + } + + auto start_of_set = std::ranges::find_if(m_pool, [](auto const & candidate) { return candidate.has_value(); }); + if (start_of_set == m_pool.end()) + { + return m_underlying->allocate_many(count); + } + + if (std::distance(start_of_set, m_pool.end()) < static_cast(count)) + { + return m_underlying->allocate_many(count); + } + + auto number_of_consecutive_frames = 1uz; + for (auto current = std::next(start_of_set); current != m_pool.end() && number_of_consecutive_frames < count; + ++current) + { + if (*current && *start_of_set && **current == (**start_of_set) + 1) + { + ++number_of_consecutive_frames; + continue; + } + number_of_consecutive_frames = 0; + start_of_set = current; + } + + if (std::distance(start_of_set, m_pool.end()) >= static_cast(count)) + { + auto result = std::make_pair(**start_of_set, count); + m_free -= count; + std::ranges::for_each(start_of_set, start_of_set + count, [](auto & frame) { frame.reset(); }); + sort_pool(); + return result; + } + return m_underlying->allocate_many(count); + } + + auto release_many(std::pair frame_set) -> void override + { + if (m_free == BufferSize) + { + return m_underlying->release_many(frame_set); + } + + auto [first_frame, count] = frame_set; + auto start_of_set = std::ranges::find_if(m_pool, [](auto const & candidate) { return !candidate.has_value(); }); + + for (auto current = start_of_set; current != m_pool.end() && count; ++current) + { + *current = first_frame++; + ++m_free; + --count; + } + sort_pool(); + + if (count) + { + m_underlying->release_many({first_frame, count}); + } + } + + private: + auto sort_pool() -> void + { + std::ranges::sort(m_pool, [](auto lhs, auto rhs) -> bool { + if (lhs && rhs) + { + return *lhs < *rhs; + } + if (!lhs) + { + return false; + } + return true; + }); + } + + frame_allocator * m_underlying; + std::size_t m_free; + std::array, BufferSize> m_pool{}; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/kernel_mapper.hpp new file mode 100644 index 0000000..4329f1b --- /dev/null +++ b/arch/x86_64/include/arch/memory/kernel_mapper.hpp @@ -0,0 +1,34 @@ +#ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP +#define TEACHOS_X86_64_KERNEL_MAPPER_HPP + +#include "kapi/memory.hpp" + +#include +#include +#include + +#include +#include + +namespace arch::memory +{ + + struct kernel_mapper + { + using section_header_type = elf::section_header; + + explicit kernel_mapper(multiboot2::information_view const * mbi); + + auto remap_kernel(kapi::memory::page_mapper & mapper) -> void; + + private: + auto map_section(section_header_type const & section, std::string_view name, kapi::memory::page_mapper & mapper) + -> void; + + multiboot2::information_view const * m_mbi; + std::uintptr_t m_kernel_load_base; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/mmu.hpp b/arch/x86_64/include/arch/memory/mmu.hpp new file mode 100644 index 0000000..2d64184 --- /dev/null +++ b/arch/x86_64/include/arch/memory/mmu.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_X86_64_MEMORY_MMU_HPP +#define TEACHOS_X86_64_MEMORY_MMU_HPP + +#include "kapi/memory/address.hpp" + +namespace arch::memory +{ + /** + * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained + * in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction. + * + * @param address Memory address, which will be used to determine the contained page and flush the TLB entry for + * that page. + */ + auto tlb_flush(kapi::memory::linear_address address) -> void; + + /** + * @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables. + * + * @note Simply reassigns the CR3 register the value of the CR3 register, causing a flush of the TLB buffer, because + * the system has to assume that the location of the level 4 page table moved. + */ + auto tlb_flush_all() -> void; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/page_table.hpp b/arch/x86_64/include/arch/memory/page_table.hpp new file mode 100644 index 0000000..a82d9e0 --- /dev/null +++ b/arch/x86_64/include/arch/memory/page_table.hpp @@ -0,0 +1,342 @@ +#ifndef TEACHOS_X86_64_PAGE_TABLE_HPP +#define TEACHOS_X86_64_PAGE_TABLE_HPP + +#include "kapi/memory.hpp" + +#include "arch/memory/page_utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace arch::memory +{ + + //! 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 + { + //! 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, //!< 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. + //! + //! 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. + //! + //! @return The flags that are currently set on this entry. + [[nodiscard]] auto all_flags() const noexcept -> flags; + + //! Set all flags of this entry. + //! + //! @param flags The flags to apply to this entry. + auto all_flags(flags flags) noexcept -> void; + + //! 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. + //! + //! @return an engaged std::optional iff. this entry maps a page, std::nullopt otherwise. + [[nodiscard]] auto frame() const noexcept -> std::optional; + + //! Map this entry. + //! + //! @param frame The frame to map in this entry. + //! @param flags The flags to apply to this entry. + auto frame(kapi::memory::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{}; + }; + + //! The maximum number of entries in this table. + constexpr auto static entry_count{kapi::memory::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. + //! + //! 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: + std::array m_entries{}; + }; + + //! 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 + requires(Level > 0uz && Level < 5uz) + struct recursive_page_table : page_table + { + constexpr auto static next_level = Level - 1uz; + constexpr auto static recursive_index = 0776uz; + + //! Get the next lower lever table. + //! + //! @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 { + auto table_pointer = std::bit_cast *>(address); + return &std::forward_like(*table_pointer); + }); + } + + //! @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 { + auto table_pointer = std::bit_cast(address); + return &std::forward_like(*table_pointer); + }); + } + + [[nodiscard]] auto translate(kapi::memory::linear_address address) const + -> std::optional + requires(Level == 4) + { + auto offset = address.raw() % kapi::memory::page::size; + return translate(kapi::memory::page::containing(address)).transform([offset](auto frame) -> auto { + return kapi::memory::physical_address{frame.start_address().raw() + offset}; + }); + } + + [[nodiscard]] auto translate(kapi::memory::page page) const -> std::optional + requires(Level == 4) + { + auto pml3 = next(pml_index<4>(page)); + + if (!pml3) + { + return std::nullopt; + } + + auto handle_huge_page = [&] -> std::optional { + auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; }); + if (!pml3_entry) + { + return std::nullopt; + } + else if (pml3_entry->huge()) + { + auto pml3_entry_frame = *pml3_entry->frame(); + return kapi::memory::frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)}; + } + + auto pml2 = (*pml3)->next(pml_index<3>(page)); + auto pml2_entry = pml2.transform([&](auto pml2) -> auto { return (*pml2)[pml_index<2>(page)]; }); + if (!pml2_entry) + { + return std::nullopt; + } + else if (pml2_entry->huge()) + { + auto pml2_entry_frame = *pml2_entry->frame(); + return kapi::memory::frame{pml2_entry_frame.number() + pml_index<1>(page)}; + } + + return std::nullopt; + }; + + return pml3.and_then([&](auto pml3) -> auto { return pml3->next(pml_index<3>(page)); }) + .and_then([&](auto pml2) -> auto { return pml2->next(pml_index<2>(page)); }) + .and_then([&](auto pml1) -> auto { return (*pml1)[pml_index<1>(page)].frame(); }) + .or_else(handle_huge_page); + } + + 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; + + //! 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 + { + if (auto entry = (*this)[index]; entry.present() && !entry.huge()) + { + auto this_address = std::bit_cast(this); + auto next_address = (this_address << level_bits) | 1uz << high_bit | (index << offset_bits); + return next_address; + } + + return std::nullopt; + } + }; + +} // namespace arch::memory + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace arch::memory +{ + + constexpr auto to_mapper_flags(page_table::entry::flags flags) -> kapi::memory::page_mapper::flags + { + using table_flags = page_table::entry::flags; + using mapper_flags = kapi::memory::page_mapper::flags; + + auto result = mapper_flags{}; + + if ((flags & table_flags::no_execute) == table_flags::empty) + { + result |= mapper_flags::executable; + } + + if ((flags & table_flags::writable) != table_flags::empty) + { + result |= mapper_flags::writable; + } + + if ((flags & table_flags::disable_cache) != table_flags::empty) + { + result |= mapper_flags::uncached; + } + + if ((flags & table_flags::user_accessible) == table_flags::empty) + { + result |= mapper_flags::supervisor_only; + } + + return result; + } + + constexpr auto to_table_flags(kapi::memory::page_mapper::flags flags) -> page_table::entry::flags + { + using table_flags = page_table::entry::flags; + using mapper_flags = kapi::memory::page_mapper::flags; + + auto result = table_flags{}; + + if ((flags & mapper_flags::executable) == mapper_flags::empty) + { + result |= table_flags::no_execute; + } + + if ((flags & mapper_flags::writable) != mapper_flags::empty) + { + result |= table_flags::writable; + } + + if ((flags & mapper_flags::uncached) != mapper_flags::empty) + { + result |= table_flags::disable_cache; + } + + if ((flags & mapper_flags::supervisor_only) != mapper_flags::empty) + { + result |= table_flags::user_accessible; + } + + return result; + } + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/page_utilities.hpp b/arch/x86_64/include/arch/memory/page_utilities.hpp new file mode 100644 index 0000000..8c25af3 --- /dev/null +++ b/arch/x86_64/include/arch/memory/page_utilities.hpp @@ -0,0 +1,22 @@ +#ifndef TEACHOS_X86_64_PAGE_UTILITIES_HPP +#define TEACHOS_X86_64_PAGE_UTILITIES_HPP + +#include "kapi/memory.hpp" + +#include + +namespace arch::memory +{ + + template + requires(Level > 0uz && Level < 5uz) + constexpr auto pml_index(kapi::memory::page page) noexcept -> std::size_t + { + constexpr auto shift_width = (Level - 1) * 9; + constexpr auto index_mask = 0x1ffuz; + return page.number() >> shift_width & index_mask; + } + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/paging_root.hpp b/arch/x86_64/include/arch/memory/paging_root.hpp new file mode 100644 index 0000000..febbb11 --- /dev/null +++ b/arch/x86_64/include/arch/memory/paging_root.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_X86_64_PAGING_ROOT_HPP +#define TEACHOS_X86_64_PAGING_ROOT_HPP + +#include "arch/memory/page_table.hpp" + +namespace arch::memory +{ + + //! The active, recursively mapped, root map (e.g. PML4) + struct paging_root : recursive_page_table<4> + { + auto static get() -> paging_root *; + + paging_root(paging_root const &) = delete; + paging_root(paging_root &&) = delete; + auto operator=(paging_root const &) -> paging_root & = delete; + auto operator=(paging_root &&) -> paging_root & = delete; + + ~paging_root() = delete; + + private: + paging_root() = default; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/recursive_page_mapper.hpp b/arch/x86_64/include/arch/memory/recursive_page_mapper.hpp new file mode 100644 index 0000000..e278a6d --- /dev/null +++ b/arch/x86_64/include/arch/memory/recursive_page_mapper.hpp @@ -0,0 +1,25 @@ +#ifndef TEACHOS_X86_64_RECURSIVE_PAGE_MAPPER_HPP +#define TEACHOS_X86_64_RECURSIVE_PAGE_MAPPER_HPP + +#include "kapi/memory.hpp" + +#include + +namespace arch::memory +{ + + struct recursive_page_mapper : kapi::memory::page_mapper + { + explicit recursive_page_mapper(kapi::memory::frame_allocator & allocator); + + auto map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte * override; + auto unmap(kapi::memory::page page) -> void override; + auto try_unmap(kapi::memory::page page) noexcept -> bool override; + + private: + kapi::memory::frame_allocator * m_allocator; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/region_allocator.hpp b/arch/x86_64/include/arch/memory/region_allocator.hpp new file mode 100644 index 0000000..f391293 --- /dev/null +++ b/arch/x86_64/include/arch/memory/region_allocator.hpp @@ -0,0 +1,84 @@ +#ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP +#define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP + +#include "kapi/memory/address.hpp" +#include "kapi/memory/frame.hpp" +#include "kapi/memory/frame_allocator.hpp" + +#include + +#include +#include +#include + +namespace arch::memory +{ + //! 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 : kapi::memory::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. + std::pair 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 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. + multiboot2::memory_map memory_map; + }; + + using region = multiboot2::memory_map::region; + + //! 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); + + //! @copydoc kapi::memory::frame_allocator::allocate_many + //! + //! @note As long as free frames are available, successive calls to this implementation are guaranteed to yield + //! frames in ascending order. + auto allocate_many(std::size_t count = 1) noexcept + -> std::optional> override; + + //! @copydoc kapi::memory::frame_allocator::release_many + //! + //! @note This implementation will never actually release any frames. + auto release_many(std::pair frame_set) -> void override; + + auto next_free_frame() noexcept -> std::optional; + + private: + //! Find the next memory area and write it into current_area. + auto choose_next_region() -> void; + auto find_next_frame() -> std::optional; + + kapi::memory::frame m_next_frame; //!< The next available frame. + std::optional m_current_region; //!< The memory region currently used for allocation + multiboot2::memory_map m_memory_map; //!< The boot loader supplied memory map. + kapi::memory::frame m_kernel_start; //!< The start of the kernel image in physical memory. + kapi::memory::frame m_kernel_end; //!< The end of the kernel image in physical memory. + kapi::memory::frame m_multiboot_start; //!< The start of the Multiboot2 information in physical memory. + kapi::memory::frame m_multiboot_end; //!< The end of the Multiboot2 information in physical memory. + }; + +} // namespace arch::memory + +#endif diff --git a/arch/x86_64/include/arch/memory/scoped_mapping.hpp b/arch/x86_64/include/arch/memory/scoped_mapping.hpp new file mode 100644 index 0000000..c713e03 --- /dev/null +++ b/arch/x86_64/include/arch/memory/scoped_mapping.hpp @@ -0,0 +1,68 @@ +#ifndef TEACHOS_X86_64_SCOPED_MAPPING_HPP +#define TEACHOS_X86_64_SCOPED_MAPPING_HPP + +#include "kapi/memory.hpp" + +#include "arch/memory/page_table.hpp" + +#include + +namespace arch::memory +{ + + //! A page mapping that, if established, maps a given frame to a given unused page, unmapping it on destruction. It + //! allows for an easy way to quickly map a page that is not required to be mapped forever. When mapping a frame, new + //! page tables may be allocated. On destruction, these pages tables, or rather their respective frames, will be + //! released again. + struct scoped_mapping + { + //! Copying a scoped mapping would be meaningless. + scoped_mapping(scoped_mapping const &) noexcept = delete; + + //! Adopt an existing scoped mapping, transferring mapping ownership to this new object. + scoped_mapping(scoped_mapping &&) noexcept; + + //! Construct a new scoped mapping, which can be used to map a frame to the given unused page. + //! @param page An unused page. If the page is already mapped, this constructor will panic. + //! @param mapper The page mapper to use for mapping and unmapping of the page. + explicit scoped_mapping(kapi::memory::page page, kapi::memory::page_mapper & mapper); + + //! Unmap the mapped frame if one was mapped. + //! @note Any page tables that were allocated to support the mapping will be released. + ~scoped_mapping() noexcept; + + //! Copying a scoped mapping would be meaningless. + auto operator=(scoped_mapping const &) -> scoped_mapping = delete; + + //! Adopt an existing scoped mapping, swapping mapping ownerships between the objects. + auto operator=(scoped_mapping &&) noexcept -> scoped_mapping &; + + //! Map the given frame with the given flags. + //! @note If a mapping has already been established, this function will panic + //! @param frame A frame to map. + //! @param flags The flags, besides the present flag, to apply to the mapping. + //! @return A pointer to the first byte of the mapped frame. + auto map(kapi::memory::frame frame, page_table::entry::flags flags) -> std::byte *; + + //! Map the given frame, returning a typed pointer. + template + auto map_as(kapi::memory::frame frame, page_table::entry::flags flags) -> DataType * + { + return std::bit_cast(map(frame, flags)); + } + + //! Unmap the mapped frame. + //! @note If no frame was ever mapped, this function will panic. + auto unmap() -> void; + + friend auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void; + + private: + kapi::memory::page m_page; + kapi::memory::page_mapper * m_mapper; + bool m_mapped; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/crtc.hpp b/arch/x86_64/include/arch/vga/crtc.hpp new file mode 100644 index 0000000..dbdc365 --- /dev/null +++ b/arch/x86_64/include/arch/vga/crtc.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_VGA_IO_HPP +#define TEACHOS_X86_64_VGA_IO_HPP + +#include "arch/device_io/port_io.hpp" + +#include + +namespace arch::vga::crtc +{ + /** + * @brief The address port of the CRT Controller. + */ + using address = io::port<0x3d4, std::byte, io::port_write>; + + /** + * @brief The data port of the CRT Controller. + */ + using data = io::port<0x3d5, std::byte, io::port_read, io::port_write>; + + namespace registers + { + /** + * @brief The address of the Cursor Start register of the CRTC. + */ + [[maybe_unused]] constexpr auto cursor_start = std::byte{0x0a}; + + /** + * @brief The address of the Cursor End register of the CRTC. + */ + [[maybe_unused]] constexpr auto cursor_end = std::byte{0x0b}; + } // namespace registers + +} // namespace arch::vga::crtc + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text.hpp b/arch/x86_64/include/arch/vga/text.hpp new file mode 100644 index 0000000..f81ab60 --- /dev/null +++ b/arch/x86_64/include/arch/vga/text.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_HPP +#define TEACHOS_X86_64_VGA_TEXT_HPP + +#include "text/attribute.hpp" // IWYU pragma: export +#include "text/color.hpp" // IWYU pragma: export +#include "text/common_attributes.hpp" // IWYU pragma: export +#include "text/device.hpp" // IWYU pragma: export +#include "text/flags.hpp" // IWYU pragma: export + +#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/attribute.hpp b/arch/x86_64/include/arch/vga/text/attribute.hpp new file mode 100644 index 0000000..6a0f995 --- /dev/null +++ b/arch/x86_64/include/arch/vga/text/attribute.hpp @@ -0,0 +1,30 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP +#define TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP + +// IWYU pragma: private, include "arch/vga/text.hpp" + +#include "arch/vga/text/color.hpp" +#include "arch/vga/text/flags.hpp" + +namespace arch::vga::text +{ + //! The VGA text mode attribute. + //! + //! @note In the text mode of VGA, every code point being presented is followed by an attribute description. This + //! allows for the modification of how the relevant "cell" is presented. + //! + //! @see text::foreground_flag + //! @see text::background_flag + struct attribute + { + color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. + enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. + color background_color : 3; ///< The background color of the cell. + enum background_flag background_flag : 1; ///< The background color modification flag of the cell. + }; + + static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/buffer.hpp b/arch/x86_64/include/arch/vga/text/buffer.hpp new file mode 100644 index 0000000..648d37a --- /dev/null +++ b/arch/x86_64/include/arch/vga/text/buffer.hpp @@ -0,0 +1,101 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP +#define TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP + +// IWYU pragma: private, include "arch/vga/text.hpp" + +#include "arch/vga/text/attribute.hpp" + +#include +#include +#include +#include + +namespace arch::vga::text +{ + //! A VGA text buffer. + //! + //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a + //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute + //! determines the visual style of that cell. + //! + //! @see text::attribute + struct buffer + { + using cell = std::pair; + + //! Create a new buffer. + //! + //! @param width The width of the buffer + //! @param height The height of the buffer + //! @param start A pointer to the first byte of the buffer. + //! @param position The starting position for the first write to the buffer + buffer(std::size_t width, std::size_t height, cell * start, std::size_t position = 0); + + //! Clear the buffer. + //! + //! Clearing the buffer ensures it is filled with zeroes, effectively erasing all data and resetting the output + //! position to the start of the buffer. + auto clear() -> void; + + //! Write a string of formatted code points to the buffer. + //! + //! @param code_points A string of (8-bit) code points to write to the buffer. + //! @param attribute The formatting to apply to the written sequence of code points. + auto write(std::string_view code_points, attribute attribute) -> void; + + //! Write a single, formatted code point to the buffer. + //! + //! @param code_point A single (8-bit) code point + //! @param attribute The formatting to apply to the code point. + auto write(char code_point, attribute attribute) -> void; + + //! Move the output position to a new line and scroll the buffer if necessary. + auto newline() -> void; + + //! Scroll the buffer contents. + //! + //! @param nof_lines The number of lines to scroll up. + auto scroll(std::size_t nof_lines = 1) -> void; + + private: + //! Get column number of the current cell. + [[nodiscard]] auto column() const noexcept -> std::ptrdiff_t; + + //! Get the line number of the current cell. + [[nodiscard]] auto line() const noexcept -> std::ptrdiff_t; + + //! Process the semantics of special code points, for example newlines and carriage returns. + //! + //! @param code_point The code point to process. + //! @param attribute The attribute to use when writing to the text buffer. + //! @return @p true iff. the code point was handled, @p false otherwise. + auto handle_special_code_point(char code_point, attribute attribute) -> bool; + + //! Perform the actual output to the buffer. + //! + //! @param code_points The code points to output.. + //! @param attribute The attribute to use when writing to the text buffer. + auto do_write(std::string_view code_points, attribute attribute) -> void; + + //! Perform the actual output to the buffer. + //! + //! @param code_point The code point to output. + //! @param attribute The attribute to use when writing to the text buffer. + auto do_write(char code_point, attribute attribute) -> void; + + //! The width, in cells, of the buffer. + std::size_t m_width{}; + + //! The height, in cells, of the buffer. + std::size_t m_height{}; + + //! The text mode data buffer. + std::span m_buffer; + + //! The position of the next cell to be written to. + std::size_t m_position{}; + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/color.hpp b/arch/x86_64/include/arch/vga/text/color.hpp new file mode 100644 index 0000000..a541830 --- /dev/null +++ b/arch/x86_64/include/arch/vga/text/color.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_COLOR_HPP +#define TEACHOS_X86_64_VGA_TEXT_COLOR_HPP + +// IWYU pragma: private, include "arch/vga/text.hpp" + +#include + +namespace arch::vga::text +{ + //! VGA Text Mode standard colors. + //! + //! Every color may be used as a foreground and/or background color, in any combination. + enum struct color : std::uint8_t + { + //! Equivalent to HTML color \#000000. + black, + //! Equivalent to HTML color \#0000AA. + blue, + //! Equivalent to HTML color \#00AA00. + green, + //! Equivalent to HTML color \#00AAAA. + cyan, + //! Equivalent to HTML color \#AA0000. + red, + //! Equivalent to HTML color \#AA00AA. + purple, + //! Equivalent to HTML color \#AA5500. + brown, + //! Equivalent to HTML color \#AAAAAA. + gray, + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/common_attributes.hpp b/arch/x86_64/include/arch/vga/text/common_attributes.hpp new file mode 100644 index 0000000..9bd61a5 --- /dev/null +++ b/arch/x86_64/include/arch/vga/text/common_attributes.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP +#define TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP + +// IWYU pragma: private, include "arch/vga/text.hpp" + +#include "arch/vga/text/attribute.hpp" +#include "arch/vga/text/color.hpp" +#include "arch/vga/text/flags.hpp" + +namespace arch::vga::text +{ + //! Make the affected cell display with a gray foreground and black background. + [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a green foreground and black background. + [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a green foreground and black background. + [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a white (gray + intense) foreground and red background. + [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::intense, + .background_color = color::red, + .background_flag = background_flag::none}; +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/device.hpp b/arch/x86_64/include/arch/vga/text/device.hpp new file mode 100644 index 0000000..11b3281 --- /dev/null +++ b/arch/x86_64/include/arch/vga/text/device.hpp @@ -0,0 +1,45 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP +#define TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP + +// IWYU pragma: private, include "arch/vga/text.hpp" + +#include "kapi/cio.hpp" + +#include "arch/vga/text/buffer.hpp" + +#include + +namespace arch::vga::text +{ + //! A VGA Text Mode device. + //! + //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a + //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute + //! determines the visual style of that cell. + //! + //! @see text::attribute + struct device final : kapi::cio::output_device + { + device(); + + //! Clear the screen. + //! + //! Clearing the screen ensures the text mode buffer is filled with zeroes, effectively erasing all displayed data + //! and resetting the output position to the start of the buffer. + auto clear() -> void; + + //! Enable or disable the VGA text mode cursor. + //! + //! @param enabled Whether to enable the cursor. + auto cursor(bool enabled) -> void; + + //! @copydoc kapi::cio::output_device + auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; + + private: + buffer m_buffer; + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/flags.hpp b/arch/x86_64/include/arch/vga/text/flags.hpp new file mode 100644 index 0000000..67c6c11 --- /dev/null +++ b/arch/x86_64/include/arch/vga/text/flags.hpp @@ -0,0 +1,38 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP +#define TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP + +// IWYU pragma: private, include "arch/vga/text.hpp" + +namespace arch::vga::text +{ + + //! VGA Text Mode standard background modification flags. + enum struct background_flag : bool + { + //! Do not modify the foreground rendering. + none, + //! Render the background as blinking or intense. + //! + //! Whether this flag is interpreted as 'blink' or 'bright' depends on the currently active configuration of the VGA + //! device. + //! + //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such + //! colors brighter. + blink_or_bright, + }; + + //! VGA Text Mode standard foreground modification flags. + enum struct foreground_flag : bool + { + //! Do not modify the foreground rendering. + none, + //! Render the foreground as intense. + //! + //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such + //! colors brighter. + intense, + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/boot/boot.hpp b/arch/x86_64/include/x86_64/boot/boot.hpp deleted file mode 100644 index 2c44659..0000000 --- a/arch/x86_64/include/x86_64/boot/boot.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef TEACHOS_X86_64_BOOT_BOOT_H -#define TEACHOS_X86_64_BOOT_BOOT_H - -#ifdef __ASSEMBLER__ -/* clang-format off */ -/** - * @brief The number of huge pages to map during bootstrap. - */ -#define HUGE_PAGES_TO_MAP (16) - -/** - * @brief The magic value to be set in eax by the multiboot 2 loader. - */ -#define MULTIBOOT2_MAGIC (0x36d76289) - -/** - * @brief The "A" bit in a GDT entry. - */ -#define GDT_ACCESSED (1 << 40) - -/** - * @brief The "R/W" bit in a GDT entry - */ -#define GDT_READ_WRITE (1 << 41) - -/** - * @brief The "E" bit in a GDT entry. - */ -#define GDT_EXECUTABLE (1 << 43) - -/** - * @brief The "S" bit in a GDT entry. - */ -#define GDT_DESCRIPTOR_TYPE (1 << 44) - -/** - * @brief The "P" bit in a GDT entry. - */ -#define GDT_PRESENT (1 << 47) - -/** - * @brief The "L" bit in a GDT entry. - */ -#define GDT_LONG_MODE (1 << 53) -/* clang-format on */ -#else - -#include "kapi/boot.hpp" // IWYU pragma: export - -#include - -#include - -namespace teachos::boot -{ - - struct information - { - //! A pointer to the loader provided Multiboot2 Information structure. - multiboot2::information_view const * mbi; - - //! The index of the next character to be written in the VGA text buffer after handoff. - std::size_t vga_buffer_index; - }; - -} // namespace teachos::boot - -#endif - -#endif diff --git a/arch/x86_64/include/x86_64/boot/ld.hpp b/arch/x86_64/include/x86_64/boot/ld.hpp deleted file mode 100644 index b073863..0000000 --- a/arch/x86_64/include/x86_64/boot/ld.hpp +++ /dev/null @@ -1,61 +0,0 @@ -//! @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 - -namespace teachos::boot::x86_64 -{ - - extern "C" - { - //! 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; - - //! 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; - - //! 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; - - //! 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; - - //! 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/control_register.hpp b/arch/x86_64/include/x86_64/cpu/control_register.hpp deleted file mode 100644 index 67ae87e..0000000 --- a/arch/x86_64/include/x86_64/cpu/control_register.hpp +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP -#define TEACHOS_X86_64_CPU_IMPL_CONTROL_REGISTERS_HPP - -// IWYU pragma: private, include "x86_64/cpu/registers.hpp" - -#include "kapi/memory.hpp" - -#include - -#include -#include -#include -#include - -namespace teachos::cpu::x86_64 -{ - namespace impl - { - //! The assembler templates used to access (r/w) CR0; - constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; - - //! The assembler templates used to access (r/w) CR2; - constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; - - //! The assembler templates used to access (r/w) CR3; - constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; - } // namespace impl - - //! 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 : std::true_type - { - }; - - template<> - struct is_bitfield_enum : 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, if it comprises 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 - struct control_register_with_flags - { - private: - constexpr control_register_with_flags() noexcept = default; - friend Derived; - }; - - //! @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 - struct control_register_with_flags>> - { - //! 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 - struct control_register : control_register_with_flags, 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(0)) - : m_flags{static_cast(flags)} - , m_address{static_cast(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 frame aligned physical address of the root page map into this value. - //! - //! @param frame The frame containing a PML4. - constexpr auto frame(memory::frame frame) -> void - { - m_address = static_cast(frame.number()); - } - - //! 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(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(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(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(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)); - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/interrupts.hpp b/arch/x86_64/include/x86_64/cpu/interrupts.hpp deleted file mode 100644 index 88d0b78..0000000 --- a/arch/x86_64/include/x86_64/cpu/interrupts.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP -#define TEACHOS_X86_64_CPU_INTERRUPTS_HPP - -#include "x86_64/cpu/segment_selector.hpp" - -#include -#include -#include - -namespace teachos::cpu::x86_64 -{ - - //! The types of supported gates. - enum struct gate_type : std::uint8_t - { - //! A gate entered through the @p INT instruction. - interrupt_gate = 0b1110, - //! A gate entered via an exception. - trap_gate = 0b1111, - }; - - struct alignas(std::uint64_t) gate_descriptor - { - //! The lowest 16 bits of the address of this gate's handler function. - std::uint16_t offset_low : 16; - //! The code segment used when handling the respective interrupt. - segment_selector m_code_segment; - //! The index into the Interrupt Stack Table naming the stack to use. - std::uint8_t interrupt_stack_table_selector : 3; - //! Reserved - std::uint8_t : 5; - //! The type of this gate. - gate_type gate_type : 4; - //! Reserved - std::uint8_t : 1; - //! The privilege level required to enter through this gate. - std::uint8_t descriptor_privilege_level : 2; - //! Whether this gate is present or not. - std::uint8_t present : 1; - //! The middle 16 bits of the address of this gate's handler function. - std::uint16_t offset_middle : 16; - //! The highest 32 bits of the address of this gate's handler function. - std::uint32_t offset_high : 32; - //! Reserved - std::uint32_t : 32; - }; - - static_assert(sizeof(gate_descriptor) == 2 * sizeof(std::uint64_t)); - static_assert(std::is_aggregate_v); - - struct interrupt_descriptor_table - { - interrupt_descriptor_table(); - - private: - std::array m_descriptors{}; - }; - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp b/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp deleted file mode 100644 index 39765fe..0000000 --- a/arch/x86_64/include/x86_64/cpu/model_specific_register.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP -#define TEACHOS_X86_64_CPU_IMPL_MODEL_SPECIFIC_REGISTER_HPP - -// IWYU pragma: private, include "x86_64/cpu/registers.hpp" - -#include - -#include -#include -#include - -namespace teachos::cpu::x86_64 -{ - - //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. - enum struct ia32_efer_flags : std::uint64_t - { - //! Enable the syscall and sysret instructions. - syscall_enable = 1uz << 0, - //! Enable IA-32e mode operation. - ia32e_mode_enable = 1uz << 8, - //! Indicates IA-32e mode is active (read-only) - ia32e_mode_active = 1uz << 10, - //! Enable the use of the NX page table bit. - execute_disable_bit_enable = 1uz << 11, - }; - -} // namespace teachos::cpu::x86_64 - -namespace kstd::ext -{ - - template<> - struct is_bitfield_enum : std::true_type - { - }; - -} // namespace kstd::ext - -namespace teachos::cpu::x86_64 -{ - //! The MSR number for the IA32_EFER MSR - constexpr auto ia32_efer_number = 0xC000'0080u; - - //! A mixin for flag-oriented model specific registers. - //! - //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific - //! register is flag-oriented, if it comprises a single field of bitfield. - //! - //! @tparam Derived The class deriving from this mixin. - //! @tparam ValueType The value type of the class deriving from this mixin. - template - struct model_specific_register_with_flags - { - private: - constexpr model_specific_register_with_flags() noexcept = default; - friend Derived; - }; - - //! @copydoc model_specific_register_with_flags - //! - //! @note This specialization provides the implementation for the case in which the value type of the model specific - //! register is a bitfield enum. - template - struct model_specific_register_with_flags>> - { - //! The of the flags used by this model specific register. - using flags = ValueType; - - //! Set one or more flags in this model specific register. - //! - //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific 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 flag One or a combination of flags to be set in the model specific register. - auto static set(flags flag) -> void - { - auto current = Derived::read(); - current |= flag; - Derived::write(current); - } - - //! Clear one or more flags in this model specific register. - //! - //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific 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 flag One or a combination of flags to be cleared in the model specific register. - auto static clear(flags flag) -> void - { - auto current = Derived::read(); - current &= ~flag; - Derived::write(current); - } - - //! Test one or more flags in this model specific register - //! - //! @param flag One or a combination of flags to test for. - auto test(flags flag) -> flags - { - return Derived::read() & flag; - } - }; - - //! A model specific register (MSR) - //! - //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. - //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in - //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in - //! an invalid/undefined state. - //! - //! @tparam Number The register number of this MSR - //! @tparam ValueType The value type of this MSR - template - struct model_specific_register - : model_specific_register_with_flags, ValueType> - { - //! A raw MSR value, comprising two halfs. - //! - //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. - struct raw_value - { - std::uint32_t low_half; //!< The lower half of the register value - std::uint32_t high_half; //!< The upper half of the register value - }; - - //! Read the current value of this MSR. - //! - //! @return The current value of this MSR. - auto static read() -> ValueType - { - auto raw = raw_value{}; - asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); - return static_cast(std::bit_cast(raw)); - } - - //! Write a new value to this MSR. - //! - //! @param value The new value for this MSR. - auto static write(ValueType value) -> void - { - auto raw = std::bit_cast(static_cast(value)); - asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); - } - }; - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/registers.hpp b/arch/x86_64/include/x86_64/cpu/registers.hpp deleted file mode 100644 index 3ddd539..0000000 --- a/arch/x86_64/include/x86_64/cpu/registers.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP -#define TEACHOS_X86_64_CPU_REGISTERS_HPP - -#include "kapi/memory.hpp" - -#include "x86_64/cpu/control_register.hpp" // IWYU pragma: export -#include "x86_64/cpu/model_specific_register.hpp" // IWYU pragma: export - -namespace teachos::cpu::x86_64 -{ - - //! Configuration Register 0. - //! - //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. - using cr0 = control_register; - - //! 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; - - //! Configuration Register 3. - //! - //! This register holds the configuration of the virtual memory protection configuration. - using cr3 = control_register; - - //! The I32_EFER (Extended Feature Enable Register) MSR - using i32_efer = model_specific_register; - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/cpu/segment_selector.hpp b/arch/x86_64/include/x86_64/cpu/segment_selector.hpp deleted file mode 100644 index bb07e28..0000000 --- a/arch/x86_64/include/x86_64/cpu/segment_selector.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TEACHOS_X86_64_SEGMENT_SELECTOR_HPP -#define TEACHOS_X86_64_SEGMENT_SELECTOR_HPP - -#include -namespace teachos::cpu::x86_64 -{ - - struct segment_selector - { - std::uint16_t request_privilege_level : 2; - bool use_local_descriptor_table : 1; - std::uint16_t table_index : 13; - }; - - static_assert(sizeof(segment_selector) == sizeof(std::uint16_t)); - static_assert(alignof(segment_selector) == alignof(std::uint16_t)); - -} // namespace teachos::cpu::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/debug/qemu_output.hpp b/arch/x86_64/include/x86_64/debug/qemu_output.hpp deleted file mode 100644 index 61b47d6..0000000 --- a/arch/x86_64/include/x86_64/debug/qemu_output.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP -#define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP - -#include "kapi/cio.hpp" - -#include "x86_64/device_io/port_io.hpp" - -#include - -namespace teachos::debug::x86_64 -{ - - //! A QEMU debug console output device. - //! - //! This device implements output to the port 0xE9 debug console present in QEMU in Bochs. It is designed to wrap a - //! different output device (e.g. a VGA text output device) and forwards the written data accordingly. - //! - //! @note Support for the device has to be enabled when the emulator is started. The device will try to detect if the - //! port is available. If the port is detected, any output to the device will be written to port before being - //! forwarded to the lower device. - struct qemu_output : cio::output_device - { - //! The port to write to. - using port = io::x86_64::port<0xE9, unsigned char, io::x86_64::port_write, io::x86_64::port_read>; - - //! Construct a new debug device wrapper for the given output device. - //! - //! @param lower The device to forward the output to. - explicit qemu_output(output_device & lower); - - //! @copydoc cio::output_device - auto write(cio::output_stream stream, std::string_view text) -> void override; - - private: - //! The device to forward the output to. - output_device & m_lower; - //! Whether the device has been detected. - bool m_present; - }; - -} // namespace teachos::debug::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp deleted file mode 100644 index fa2ec2d..0000000 --- a/arch/x86_64/include/x86_64/device_io/port_io.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP -#define TEACHOS_X86_64_IO_PORT_IO_HPP - -#include -#include -#include -#include -#include -#include - -namespace teachos::io::x86_64 -{ - - //! The requirements imposed on a type usable for port I/O. - template - concept port_io_type = requires { - requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; - requires std::default_initializable; - std::bit_cast( - std::conditional_t>{}); - }; - - template - struct port_read - { - //! Read from the I/O port. - //! - //! @return The data read from the I/O port. - auto static read() noexcept - { - auto data = typename Derived::value_type{}; - asm volatile((code[Derived::size / 2]) - : [data] "=m"(data) - : [port] "i"(Derived::address) - : "dx", (Derived::data_register)); - return data; - } - - private: - constexpr port_read() noexcept = default; - friend Derived; - - //! The assembly templates used for reading from an I/O port. - constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, - }; - }; - - template - struct port_write - { - //! Write data to the I/O port. - //! - //! @param data The data to write to the I/O port. - auto static write(std::same_as auto data) noexcept -> void - { - asm volatile((code[Derived::size / 2]) - : - : [port] "i"(Derived::address), [data] "im"(data) - : "dx", (Derived::data_register)); - } - - private: - constexpr port_write() noexcept = default; - friend Derived; - - //! The assembly templates used for writing to an I/O port. - constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"}, - }; - }; - - //! An I/O port of a given size at a given address. - //! - //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte - //! to double-word sized transfers. - //! - //! @tparam Address The address (port number) of the I/O port. - //! @tparam Size The size (in bytes) of the I/O port. - //! @tparam Features The features (readable, writeable) - template typename... Features> - requires(sizeof...(Features) > 0) - struct port : Features>... - { - //! The type of the data of this port. - using value_type = ValueType; - - //! The address of this I/O port. - constexpr auto static address = Address; - - //! The size of this I/O port. - constexpr auto static size = sizeof(value_type); - - //! The register clobbered by the I/O operation. - constexpr auto static data_register = size == 1 ? std::string_view{"al"} - : size == 2 ? std::string_view{"ax"} - : std::string_view{"eax"}; - }; - -} // namespace teachos::io::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 deleted file mode 100644 index fa3b5cb..0000000 --- a/arch/x86_64/include/x86_64/memory/buffered_allocator.hpp +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef TEACHOS_X86_64_BUFFERED_ALLOCATOR_HPP -#define TEACHOS_X86_64_BUFFERED_ALLOCATOR_HPP - -#include "kapi/memory.hpp" -#include "kapi/system.hpp" - -#include -#include -#include -#include -#include - -namespace teachos::memory::x86_64 -{ - - template - struct buffered_allocator : frame_allocator - { - explicit buffered_allocator(frame_allocator * underlying) - : m_underlying{underlying} - , m_free{BufferSize} - { - auto from_underlying = m_underlying->allocate_many(BufferSize); - if (!from_underlying) - { - system::panic("[x86_64:MEM] Not enough frames available from underlying allocator."); - } - auto [first_frame, count] = *from_underlying; - std::ranges::generate_n(m_pool.begin(), count, [first_frame]() mutable { return first_frame++; }); - } - - buffered_allocator(buffered_allocator const &) = delete; - buffered_allocator(buffered_allocator && other) noexcept = delete; - - ~buffered_allocator() override - { - std::ranges::for_each(m_pool, [this](auto const & maybe_frame) { - if (maybe_frame) - { - m_underlying->release_many({*maybe_frame, 1}); - } - }); - } - - auto operator=(buffered_allocator const &) = delete; - auto operator=(buffered_allocator &&) = delete; - - auto allocate_many(std::size_t count = 1) noexcept -> std::optional> override - { - if (count > m_free) - { - return m_underlying->allocate_many(count); - } - - auto start_of_set = std::ranges::find_if(m_pool, [](auto const & candidate) { return candidate.has_value(); }); - if (start_of_set == m_pool.end()) - { - return m_underlying->allocate_many(count); - } - - if (std::distance(start_of_set, m_pool.end()) < static_cast(count)) - { - return m_underlying->allocate_many(count); - } - - auto number_of_consecutive_frames = 1uz; - for (auto current = std::next(start_of_set); current != m_pool.end() && number_of_consecutive_frames < count; - ++current) - { - if (*current && *start_of_set && **current == (**start_of_set) + 1) - { - ++number_of_consecutive_frames; - continue; - } - number_of_consecutive_frames = 0; - start_of_set = current; - } - - if (std::distance(start_of_set, m_pool.end()) >= static_cast(count)) - { - auto result = std::make_pair(**start_of_set, count); - m_free -= count; - std::ranges::for_each(start_of_set, start_of_set + count, [](auto & frame) { frame.reset(); }); - sort_pool(); - return result; - } - return m_underlying->allocate_many(count); - } - - auto release_many(std::pair frame_set) -> void override - { - if (m_free == BufferSize) - { - return m_underlying->release_many(frame_set); - } - - auto [first_frame, count] = frame_set; - auto start_of_set = std::ranges::find_if(m_pool, [](auto const & candidate) { return !candidate.has_value(); }); - - for (auto current = start_of_set; current != m_pool.end() && count; ++current) - { - *current = first_frame++; - ++m_free; - --count; - } - sort_pool(); - - if (count) - { - m_underlying->release_many({first_frame, count}); - } - } - - private: - auto sort_pool() -> void - { - std::ranges::sort(m_pool, [](auto lhs, auto rhs) -> bool { - if (lhs && rhs) - { - return *lhs < *rhs; - } - if (!lhs) - { - return false; - } - return true; - }); - } - - frame_allocator * m_underlying; - std::size_t m_free; - std::array, BufferSize> m_pool{}; - }; - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp b/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp deleted file mode 100644 index c393d73..0000000 --- a/arch/x86_64/include/x86_64/memory/kernel_mapper.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP -#define TEACHOS_X86_64_KERNEL_MAPPER_HPP - -#include "kapi/memory.hpp" - -#include -#include -#include - -#include -#include - -namespace teachos::memory::x86_64 -{ - - struct kernel_mapper - { - using section_header_type = elf::section_header; - - explicit kernel_mapper(multiboot2::information_view const * mbi); - - auto remap_kernel(page_mapper & mapper) -> void; - - private: - auto map_section(section_header_type const & section, std::string_view name, page_mapper & mapper) -> void; - - multiboot2::information_view const * m_mbi; - std::uintptr_t m_kernel_load_base; - }; - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/mmu.hpp b/arch/x86_64/include/x86_64/memory/mmu.hpp deleted file mode 100644 index 323d18a..0000000 --- a/arch/x86_64/include/x86_64/memory/mmu.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_MMU_HPP -#define TEACHOS_X86_64_MEMORY_MMU_HPP - -#include "kapi/memory/address.hpp" - -namespace teachos::memory::x86_64 -{ - /** - * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained - * in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction. - * - * @param address Memory address, which will be used to determine the contained page and flush the TLB entry for - * that page. - */ - auto tlb_flush(linear_address address) -> void; - - /** - * @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables. - * - * @note Simply reassigns the CR3 register the value of the CR3 register, causing a flush of the TLB buffer, because - * the system has to assume that the location of the level 4 page table moved. - */ - auto tlb_flush_all() -> void; - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/page_table.hpp b/arch/x86_64/include/x86_64/memory/page_table.hpp deleted file mode 100644 index 71ba5b7..0000000 --- a/arch/x86_64/include/x86_64/memory/page_table.hpp +++ /dev/null @@ -1,341 +0,0 @@ -#ifndef TEACHOS_X86_64_PAGE_TABLE_HPP -#define TEACHOS_X86_64_PAGE_TABLE_HPP - -#include "kapi/memory.hpp" - -#include "x86_64/memory/page_utilities.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace teachos::memory::x86_64 -{ - - //! 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 - { - //! 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, //!< 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. - //! - //! 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. - //! - //! @return The flags that are currently set on this entry. - [[nodiscard]] auto all_flags() const noexcept -> flags; - - //! Set all flags of this entry. - //! - //! @param flags The flags to apply to this entry. - auto all_flags(flags flags) noexcept -> void; - - //! 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. - //! - //! @return an engaged std::optional iff. this entry maps a page, std::nullopt otherwise. - [[nodiscard]] auto frame() const noexcept -> std::optional; - - //! 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{}; - }; - - //! The maximum number of entries in this table. - 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. - //! - //! 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: - std::array m_entries{}; - }; - - //! 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 - requires(Level > 0uz && Level < 5uz) - struct recursive_page_table : page_table - { - constexpr auto static next_level = Level - 1uz; - constexpr auto static recursive_index = 0776uz; - - //! Get the next lower lever table. - //! - //! @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 { - auto table_pointer = std::bit_cast *>(address); - return &std::forward_like(*table_pointer); - }); - } - - //! @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 { - auto table_pointer = std::bit_cast(address); - return &std::forward_like(*table_pointer); - }); - } - - [[nodiscard]] auto translate(linear_address address) const -> std::optional - requires(Level == 4) - { - auto offset = address.raw() % page::size; - return translate(page::containing(address)).transform([offset](auto frame) -> auto { - return physical_address{frame.start_address().raw() + offset}; - }); - } - - [[nodiscard]] auto translate(page page) const -> std::optional - requires(Level == 4) - { - auto pml3 = next(pml_index<4>(page)); - - if (!pml3) - { - return std::nullopt; - } - - auto handle_huge_page = [&] -> std::optional { - auto pml3_entry = pml3.transform([&](auto pml3) -> auto { return (*pml3)[pml_index<3>(page)]; }); - if (!pml3_entry) - { - return std::nullopt; - } - else if (pml3_entry->huge()) - { - auto pml3_entry_frame = *pml3_entry->frame(); - return frame{pml3_entry_frame.number() + pml_index<2>(page) * entry_count + pml_index<1>(page)}; - } - - auto pml2 = (*pml3)->next(pml_index<3>(page)); - auto pml2_entry = pml2.transform([&](auto pml2) -> auto { return (*pml2)[pml_index<2>(page)]; }); - if (!pml2_entry) - { - return std::nullopt; - } - else if (pml2_entry->huge()) - { - auto pml2_entry_frame = *pml2_entry->frame(); - return frame{pml2_entry_frame.number() + pml_index<1>(page)}; - } - - return std::nullopt; - }; - - return pml3.and_then([&](auto pml3) -> auto { return pml3->next(pml_index<3>(page)); }) - .and_then([&](auto pml2) -> auto { return pml2->next(pml_index<2>(page)); }) - .and_then([&](auto pml1) -> auto { return (*pml1)[pml_index<1>(page)].frame(); }) - .or_else(handle_huge_page); - } - - 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; - - //! 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 - { - if (auto entry = (*this)[index]; entry.present() && !entry.huge()) - { - auto this_address = std::bit_cast(this); - auto next_address = (this_address << level_bits) | 1uz << high_bit | (index << offset_bits); - return next_address; - } - - return std::nullopt; - } - }; - -} // namespace teachos::memory::x86_64 - -namespace kstd::ext -{ - template<> - struct is_bitfield_enum : std::true_type - { - }; -} // namespace kstd::ext - -namespace teachos::memory::x86_64 -{ - - constexpr auto to_mapper_flags(page_table::entry::flags flags) -> page_mapper::flags - { - using table_flags = page_table::entry::flags; - using mapper_flags = page_mapper::flags; - - auto result = mapper_flags{}; - - if ((flags & table_flags::no_execute) == table_flags::empty) - { - result |= mapper_flags::executable; - } - - if ((flags & table_flags::writable) != table_flags::empty) - { - result |= mapper_flags::writable; - } - - if ((flags & table_flags::disable_cache) != table_flags::empty) - { - result |= mapper_flags::uncached; - } - - if ((flags & table_flags::user_accessible) == table_flags::empty) - { - result |= mapper_flags::supervisor_only; - } - - return result; - } - - constexpr auto to_table_flags(page_mapper::flags flags) -> page_table::entry::flags - { - using table_flags = page_table::entry::flags; - using mapper_flags = page_mapper::flags; - - auto result = table_flags{}; - - if ((flags & mapper_flags::executable) == mapper_flags::empty) - { - result |= table_flags::no_execute; - } - - if ((flags & mapper_flags::writable) != mapper_flags::empty) - { - result |= table_flags::writable; - } - - if ((flags & mapper_flags::uncached) != mapper_flags::empty) - { - result |= table_flags::disable_cache; - } - - if ((flags & mapper_flags::supervisor_only) != mapper_flags::empty) - { - result |= table_flags::user_accessible; - } - - return result; - } - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/page_utilities.hpp b/arch/x86_64/include/x86_64/memory/page_utilities.hpp deleted file mode 100644 index efd1b80..0000000 --- a/arch/x86_64/include/x86_64/memory/page_utilities.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TEACHOS_X86_64_PAGE_UTILITIES_HPP -#define TEACHOS_X86_64_PAGE_UTILITIES_HPP - -#include "kapi/memory.hpp" - -#include - -namespace teachos::memory::x86_64 -{ - - template - requires(Level > 0uz && Level < 5uz) - constexpr auto pml_index(page page) noexcept -> std::size_t - { - constexpr auto shift_width = (Level - 1) * 9; - constexpr auto index_mask = 0x1ffuz; - return page.number() >> shift_width & index_mask; - } - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/paging_root.hpp b/arch/x86_64/include/x86_64/memory/paging_root.hpp deleted file mode 100644 index 47ee2f9..0000000 --- a/arch/x86_64/include/x86_64/memory/paging_root.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_X86_64_PAGING_ROOT_HPP -#define TEACHOS_X86_64_PAGING_ROOT_HPP - -#include "x86_64/memory/page_table.hpp" - -namespace teachos::memory::x86_64 -{ - - //! The active, recursively mapped, root map (e.g. PML4) - struct paging_root : recursive_page_table<4> - { - auto static get() -> paging_root *; - - paging_root(paging_root const &) = delete; - paging_root(paging_root &&) = delete; - auto operator=(paging_root const &) -> paging_root & = delete; - auto operator=(paging_root &&) -> paging_root & = delete; - - ~paging_root() = delete; - - private: - paging_root() = default; - }; - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp b/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp deleted file mode 100644 index 819f404..0000000 --- a/arch/x86_64/include/x86_64/memory/recursive_page_mapper.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TEACHOS_X86_64_RECURSIVE_PAGE_MAPPER_HPP -#define TEACHOS_X86_64_RECURSIVE_PAGE_MAPPER_HPP - -#include "kapi/memory.hpp" - -#include - -namespace teachos::memory::x86_64 -{ - - struct recursive_page_mapper : page_mapper - { - explicit recursive_page_mapper(frame_allocator & allocator); - - auto map(page page, frame frame, flags flags) -> std::byte * override; - auto unmap(page page) -> void override; - auto try_unmap(page page) noexcept -> bool override; - - private: - frame_allocator * m_allocator; - }; - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/memory/region_allocator.hpp b/arch/x86_64/include/x86_64/memory/region_allocator.hpp deleted file mode 100644 index 4063bcc..0000000 --- a/arch/x86_64/include/x86_64/memory/region_allocator.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP -#define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP - -#include "kapi/memory/address.hpp" -#include "kapi/memory/frame.hpp" -#include "kapi/memory/frame_allocator.hpp" - -#include - -#include -#include -#include - -namespace teachos::memory::x86_64 -{ - //! 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. - std::pair 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 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. - multiboot2::memory_map memory_map; - }; - - using region = multiboot2::memory_map::region; - - //! 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); - - //! @copydoc frame_allocator::allocate_many - //! - //! @note As long as free frames are available, successive calls to this implementation are guaranteed to yield - //! frames in ascending order. - auto allocate_many(std::size_t count = 1) noexcept -> std::optional> override; - - //! @copydoc frame_allocator::release_many - //! - //! @note This implementation will never actually release any frames. - auto release_many(std::pair frame_set) -> void override; - - auto next_free_frame() noexcept -> std::optional; - - private: - //! Find the next memory area and write it into current_area. - auto choose_next_region() -> void; - auto find_next_frame() -> std::optional; - - frame m_next_frame; //!< The next available frame. - std::optional m_current_region; //!< The memory region currently used for allocation - multiboot2::memory_map m_memory_map; //!< The boot loader supplied memory map. - frame m_kernel_start; //!< The start of the kernel image in physical memory. - frame m_kernel_end; //!< The end of the kernel image in physical memory. - frame m_multiboot_start; //!< The start of the Multiboot2 information in physical memory. - frame m_multiboot_end; //!< The end of the Multiboot2 information in physical memory. - }; - -} // namespace teachos::memory::x86_64 - -#endif // TEACHOS_ARCH_X86_64_MEMORY_ALLOCATOR_AREA_FRAME_ALLOCATOR_HPP diff --git a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp b/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp deleted file mode 100644 index 835e2df..0000000 --- a/arch/x86_64/include/x86_64/memory/scoped_mapping.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef TEACHOS_X86_64_SCOPED_MAPPING_HPP -#define TEACHOS_X86_64_SCOPED_MAPPING_HPP - -#include "kapi/memory.hpp" - -#include "x86_64/memory/page_table.hpp" - -namespace teachos::memory::x86_64 -{ - - //! A page mapping that, if established, maps a given frame to a given unused page, unmapping it on destruction. It - //! allows for an easy way to quickly map a page that is not required to be mapped forever. When mapping a frame, new - //! page tables may be allocated. On destruction, these pages tables, or rather their respective frames, will be - //! released again. - struct scoped_mapping - { - //! Copying a scoped mapping would be meaningless. - scoped_mapping(scoped_mapping const &) noexcept = delete; - - //! Adopt an existing scoped mapping, transferring mapping ownership to this new object. - scoped_mapping(scoped_mapping &&) noexcept; - - //! Construct a new scoped mapping, which can be used to map a frame to the given unused page. - //! @param page An unused page. If the page is already mapped, this constructor will panic. - //! @param mapper The page mapper to use for mapping and unmapping of the page. - explicit scoped_mapping(page page, page_mapper & mapper); - - //! Unmap the mapped frame if one was mapped. - //! @note Any page tables that were allocated to support the mapping will be released. - ~scoped_mapping() noexcept; - - //! Copying a scoped mapping would be meaningless. - auto operator=(scoped_mapping const &) -> scoped_mapping = delete; - - //! Adopt an existing scoped mapping, swapping mapping ownerships between the objects. - auto operator=(scoped_mapping &&) noexcept -> scoped_mapping &; - - //! Map the given frame with the given flags. - //! @note If a mapping has already been established, this function will panic - //! @param frame A frame to map. - //! @param flags The flags, besides the present flag, to apply to the mapping. - //! @return A pointer to the first byte of the mapped frame. - auto map(frame frame, page_table::entry::flags flags) -> std::byte *; - - //! Map the given frame, returning a typed pointer. - template - auto map_as(frame frame, page_table::entry::flags flags) -> DataType * - { - return std::bit_cast(map(frame, flags)); - } - - //! Unmap the mapped frame. - //! @note If no frame was ever mapped, this function will panic. - auto unmap() -> void; - - friend auto swap(scoped_mapping & lhs, scoped_mapping & rhs) -> void; - - private: - page m_page; - page_mapper * m_mapper; - bool m_mapped; - }; - -} // namespace teachos::memory::x86_64 - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp deleted file mode 100644 index fde69c2..0000000 --- a/arch/x86_64/include/x86_64/vga/crtc.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_IO_HPP -#define TEACHOS_X86_64_VGA_IO_HPP - -#include "x86_64/device_io/port_io.hpp" - -#include - -namespace teachos::vga::x86_64::crtc -{ - namespace io = io::x86_64; - - /** - * @brief The address port of the CRT Controller. - */ - using address = io::port<0x3d4, std::byte, io::port_write>; - - /** - * @brief The data port of the CRT Controller. - */ - using data = io::port<0x3d5, std::byte, io::port_read, io::port_write>; - - namespace registers - { - /** - * @brief The address of the Cursor Start register of the CRTC. - */ - [[maybe_unused]] constexpr auto cursor_start = std::byte{0x0a}; - - /** - * @brief The address of the Cursor End register of the CRTC. - */ - [[maybe_unused]] constexpr auto cursor_end = std::byte{0x0b}; - } // namespace registers - -} // namespace teachos::vga::x86_64::crtc - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp deleted file mode 100644 index f81ab60..0000000 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_HPP -#define TEACHOS_X86_64_VGA_TEXT_HPP - -#include "text/attribute.hpp" // IWYU pragma: export -#include "text/color.hpp" // IWYU pragma: export -#include "text/common_attributes.hpp" // IWYU pragma: export -#include "text/device.hpp" // IWYU pragma: export -#include "text/flags.hpp" // IWYU pragma: export - -#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/attribute.hpp b/arch/x86_64/include/x86_64/vga/text/attribute.hpp deleted file mode 100644 index 2dce708..0000000 --- a/arch/x86_64/include/x86_64/vga/text/attribute.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP -#define TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP - -// IWYU pragma: private, include "x86_64/vga/text.hpp" - -#include "x86_64/vga/text/color.hpp" -#include "x86_64/vga/text/flags.hpp" - -namespace teachos::vga::x86_64::text -{ - //! The VGA text mode attribute. - //! - //! @note In the text mode of VGA, every code point being presented is followed by an attribute description. This - //! allows for the modification of how the relevant "cell" is presented. - //! - //! @see text::foreground_flag - //! @see text::background_flag - struct attribute - { - color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. - enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. - color background_color : 3; ///< The background color of the cell. - enum background_flag background_flag : 1; ///< The background color modification flag of the cell. - }; - - static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); - -} // namespace teachos::vga::x86_64::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/buffer.hpp b/arch/x86_64/include/x86_64/vga/text/buffer.hpp deleted file mode 100644 index 9e93af3..0000000 --- a/arch/x86_64/include/x86_64/vga/text/buffer.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP -#define TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP - -// IWYU pragma: private, include "x86_64/vga/text.hpp" - -#include "x86_64/vga/text/attribute.hpp" - -#include -#include -#include -#include - -namespace teachos::vga::x86_64::text -{ - //! A VGA text buffer. - //! - //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a - //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute - //! determines the visual style of that cell. - //! - //! @see text::attribute - struct buffer - { - using cell = std::pair; - - //! Create a new buffer. - //! - //! @param width The width of the buffer - //! @param height The height of the buffer - //! @param start A pointer to the first byte of the buffer. - //! @param position The starting position for the first write to the buffer - buffer(std::size_t width, std::size_t height, cell * start, std::size_t position = 0); - - //! Clear the buffer. - //! - //! Clearing the buffer ensures it is filled with zeroes, effectively erasing all data and resetting the output - //! position to the start of the buffer. - auto clear() -> void; - - //! Write a string of formatted code points to the buffer. - //! - //! @param code_points A string of (8-bit) code points to write to the buffer. - //! @param attribute The formatting to apply to the written sequence of code points. - auto write(std::string_view code_points, attribute attribute) -> void; - - //! Write a single, formatted code point to the buffer. - //! - //! @param code_point A single (8-bit) code point - //! @param attribute The formatting to apply to the code point. - auto write(char code_point, attribute attribute) -> void; - - //! Move the output position to a new line and scroll the buffer if necessary. - auto newline() -> void; - - //! Scroll the buffer contents. - //! - //! @param nof_lines The number of lines to scroll up. - auto scroll(std::size_t nof_lines = 1) -> void; - - private: - //! Get column number of the current cell. - [[nodiscard]] auto column() const noexcept -> std::ptrdiff_t; - - //! Get the line number of the current cell. - [[nodiscard]] auto line() const noexcept -> std::ptrdiff_t; - - //! Process the semantics of special code points, for example newlines and carriage returns. - //! - //! @param code_point The code point to process. - //! @param attribute The attribute to use when writing to the text buffer. - //! @return @p true iff. the code point was handled, @p false otherwise. - auto handle_special_code_point(char code_point, attribute attribute) -> bool; - - //! Perform the actual output to the buffer. - //! - //! @param code_points The code points to output.. - //! @param attribute The attribute to use when writing to the text buffer. - auto do_write(std::string_view code_points, attribute attribute) -> void; - - //! Perform the actual output to the buffer. - //! - //! @param code_point The code point to output. - //! @param attribute The attribute to use when writing to the text buffer. - auto do_write(char code_point, attribute attribute) -> void; - - //! The width, in cells, of the buffer. - std::size_t m_width{}; - - //! The height, in cells, of the buffer. - std::size_t m_height{}; - - //! The text mode data buffer. - std::span m_buffer; - - //! The position of the next cell to be written to. - std::size_t m_position{}; - }; - -} // namespace teachos::vga::x86_64::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/color.hpp b/arch/x86_64/include/x86_64/vga/text/color.hpp deleted file mode 100644 index 0bdc146..0000000 --- a/arch/x86_64/include/x86_64/vga/text/color.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_COLOR_HPP -#define TEACHOS_X86_64_VGA_TEXT_COLOR_HPP - -// IWYU pragma: private, include "x86_64/vga/text.hpp" - -#include - -namespace teachos::vga::x86_64::text -{ - //! VGA Text Mode standard colors. - //! - //! Every color may be used as a foreground and/or background color, in any combination. - enum struct color : std::uint8_t - { - //! Equivalent to HTML color \#000000. - black, - //! Equivalent to HTML color \#0000AA. - blue, - //! Equivalent to HTML color \#00AA00. - green, - //! Equivalent to HTML color \#00AAAA. - cyan, - //! Equivalent to HTML color \#AA0000. - red, - //! Equivalent to HTML color \#AA00AA. - purple, - //! Equivalent to HTML color \#AA5500. - brown, - //! Equivalent to HTML color \#AAAAAA. - gray, - }; - -} // namespace teachos::vga::x86_64::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/common_attributes.hpp b/arch/x86_64/include/x86_64/vga/text/common_attributes.hpp deleted file mode 100644 index f6204f3..0000000 --- a/arch/x86_64/include/x86_64/vga/text/common_attributes.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP -#define TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP - -// IWYU pragma: private, include "x86_64/vga/text.hpp" - -#include "x86_64/vga/text/attribute.hpp" -#include "x86_64/vga/text/color.hpp" -#include "x86_64/vga/text/flags.hpp" - -namespace teachos::vga::x86_64::text -{ - //! Make the affected cell display with a gray foreground and black background. - [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - //! Make the affected cell display with a green foreground and black background. - [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - //! Make the affected cell display with a green foreground and black background. - [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - //! Make the affected cell display with a white (gray + intense) foreground and red background. - [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, - .foreground_flag = foreground_flag::intense, - .background_color = color::red, - .background_flag = background_flag::none}; -} // namespace teachos::vga::x86_64::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/device.hpp b/arch/x86_64/include/x86_64/vga/text/device.hpp deleted file mode 100644 index f5c69a1..0000000 --- a/arch/x86_64/include/x86_64/vga/text/device.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP -#define TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP - -// IWYU pragma: private, include "x86_64/vga/text.hpp" - -#include "kapi/cio.hpp" - -#include "x86_64/vga/text/buffer.hpp" - -#include - -namespace teachos::vga::x86_64::text -{ - //! A VGA Text Mode device. - //! - //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a - //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute - //! determines the visual style of that cell. - //! - //! @see text::attribute - struct device final : teachos::cio::output_device - { - device(); - - //! Clear the screen. - //! - //! Clearing the screen ensures the text mode buffer is filled with zeroes, effectively erasing all displayed data - //! and resetting the output position to the start of the buffer. - auto clear() -> void; - - //! Enable or disable the VGA text mode cursor. - //! - //! @param enabled Whether to enable the cursor. - auto cursor(bool enabled) -> void; - - //! @copydoc teachos::cio::output_device - auto write(cio::output_stream stream, std::string_view text) -> void override; - - private: - buffer m_buffer; - }; - -} // namespace teachos::vga::x86_64::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/flags.hpp b/arch/x86_64/include/x86_64/vga/text/flags.hpp deleted file mode 100644 index 784b910..0000000 --- a/arch/x86_64/include/x86_64/vga/text/flags.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP -#define TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP - -// IWYU pragma: private, include "x86_64/vga/text.hpp" - -namespace teachos::vga::x86_64::text -{ - - //! VGA Text Mode standard background modification flags. - enum struct background_flag : bool - { - //! Do not modify the foreground rendering. - none, - //! Render the background as blinking or intense. - //! - //! Whether this flag is interpreted as 'blink' or 'bright' depends on the currently active configuration of the VGA - //! device. - //! - //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such - //! colors brighter. - blink_or_bright, - }; - - //! VGA Text Mode standard foreground modification flags. - enum struct foreground_flag : bool - { - //! Do not modify the foreground rendering. - none, - //! Render the foreground as intense. - //! - //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such - //! colors brighter. - intense, - }; - -} // namespace teachos::vga::x86_64::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/cio.cpp b/arch/x86_64/kapi/cio.cpp new file mode 100644 index 0000000..015cf5e --- /dev/null +++ b/arch/x86_64/kapi/cio.cpp @@ -0,0 +1,20 @@ +#include "kapi/cio.hpp" + +#include "arch/debug/qemu_output.hpp" +#include "arch/vga/text.hpp" + +#include + +namespace kapi::cio +{ + + auto static constinit vga_device = std::optional{}; + auto static constinit dbg_device = std::optional{}; + + auto init() -> void + { + vga_device.emplace().cursor(false); + set_output_device(dbg_device.emplace(*vga_device)); + } + +} // namespace kapi::cio diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp new file mode 100644 index 0000000..2a0f8f7 --- /dev/null +++ b/arch/x86_64/kapi/cpu.cpp @@ -0,0 +1,12 @@ +#include "kapi/cpu.hpp" + +namespace kapi::cpu +{ + + auto halt() -> void + { + asm volatile("1: hlt\njmp 1b"); + __builtin_unreachable(); + } + +} // namespace kapi::cpu diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp new file mode 100644 index 0000000..e366ee1 --- /dev/null +++ b/arch/x86_64/kapi/memory.cpp @@ -0,0 +1,194 @@ +#include "kapi/memory.hpp" + +#include "kapi/boot.hpp" +#include "kapi/system.hpp" + +#include "arch/boot/boot.hpp" +#include "arch/boot/ld.hpp" +#include "arch/cpu/registers.hpp" +#include "arch/memory/buffered_allocator.hpp" +#include "arch/memory/kernel_mapper.hpp" +#include "arch/memory/mmu.hpp" +#include "arch/memory/page_table.hpp" +#include "arch/memory/page_utilities.hpp" +#include "arch/memory/paging_root.hpp" +#include "arch/memory/recursive_page_mapper.hpp" +#include "arch/memory/region_allocator.hpp" +#include "arch/memory/scoped_mapping.hpp" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace kapi::memory +{ + + namespace + { + constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; + constexpr auto static recursive_page_map_index = arch::memory::page_table::entry_count - 2; + + //! Instantiate a basic, memory region based, early frame allocator for remapping. + auto collect_memory_information() + { + auto memory_map = boot::bootstrap_information.mbi->maybe_memory_map(); + if (!memory_map) + { + system::panic("[x86_64] Failed to create early allocator, no memory map available."); + } + + auto const & mbi = boot::bootstrap_information.mbi; + auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; + auto image_span = std::span{&arch::boot::_start_physical, &arch::boot::_end_physical}; + + return arch::memory::region_allocator::memory_information{ + .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), + .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), + .memory_map = *memory_map, + }; + } + + //! Enable additional CPU protection features, required during later stages of the kernel. + auto enable_cpu_protections() -> void + { + arch::cpu::cr0::set(arch::cpu::cr0::flags::write_protect); + arch::cpu::i32_efer::set(arch::cpu::i32_efer::flags::execute_disable_bit_enable); + } + + //! Inject, or graft, a faux recursive PML4 into the active page mapping structure. + auto inject_faux_pml4(frame_allocator & allocator, page_mapper & mapper) + { + using arch::memory::page_table; + using arch::memory::paging_root; + using arch::memory::pml_index; + using entry_flags = arch::memory::page_table::entry::flags; + + auto page = page::containing(unused_page_address); + + auto temporary_mapper = arch::memory::scoped_mapping{page, mapper}; + auto new_pml4_frame = allocator.allocate(); + + auto pml4 = std::construct_at(temporary_mapper.map_as(*new_pml4_frame, entry_flags::writable)); + (*pml4)[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); + + auto pml4_index = pml_index<4>(page); + auto old_pml4 = paging_root::get(); + auto pml4_entry = (*old_pml4)[pml4_index]; + + auto pml3_index = pml_index<3>(page); + auto old_pml3 = old_pml4->next(pml4_index); + auto pml3_entry = (**old_pml3)[pml3_index]; + + auto pml2_index = pml_index<2>(page); + auto old_pml2 = (**old_pml3).next(pml3_index); + auto pml2_entry = (**old_pml2)[pml2_index]; + + auto pml1_index = pml_index<1>(page); + auto old_pml1 = (**old_pml2).next(pml2_index); + auto pml1_entry = (**old_pml1)[pml1_index]; + + (*paging_root::get())[recursive_page_map_index].frame(new_pml4_frame.value(), + entry_flags::present | entry_flags::writable); + + arch::memory::tlb_flush_all(); + + auto new_pml4 = paging_root::get(); + (*new_pml4)[pml4_index] = pml4_entry; + + auto new_pml3 = new_pml4->next(pml4_index); + (**new_pml3)[pml3_index] = pml3_entry; + + auto new_pml2 = (**new_pml3).next(pml3_index); + (**new_pml2)[pml2_index] = pml2_entry; + + auto new_pml1 = (**new_pml2).next(pml2_index); + (**new_pml1)[pml1_index] = pml1_entry; + + return *new_pml4_frame; + } + + auto remap_kernel(page_mapper & mapper) -> void + { + auto kernel_mapper = arch::memory::kernel_mapper{boot::bootstrap_information.mbi}; + kernel_mapper.remap_kernel(mapper); + } + + auto remap_vga_text_mode_buffer(page_mapper & mapper) -> void + { + constexpr auto vga_base = std::uintptr_t{0xb8000}; + auto vga_physical_start = physical_address{vga_base}; + auto vga_virtual_start = linear_address{vga_base + std::bit_cast(&arch::boot::TEACHOS_VMA)}; + + auto page = page::containing(vga_virtual_start); + auto frame = frame::containing(vga_physical_start); + + mapper.map(page, frame, page_mapper::flags::writable); + } + + auto remap_multiboot_information(page_mapper & mapper) -> void + { + auto mbi_base = std::bit_cast(boot::bootstrap_information.mbi); + auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); + auto mbi_physical_start = physical_address{mbi_base & ~std::bit_cast(&arch::boot::TEACHOS_VMA)}; + auto mbi_virtual_start = linear_address{mbi_base}; + auto mbi_block_count = (mbi_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; + + for (auto i = 0uz; i < mbi_block_count; ++i) + { + auto page = page::containing(mbi_virtual_start) + i; + auto frame = frame::containing(mbi_physical_start) + i; + mapper.map(page, frame, page_mapper::flags::empty); + } + } + + auto constinit region_based_allocator = std::optional{}; + auto constinit buffered_allocator = std::optional>{}; + auto constinit recursive_page_mapper = std::optional{}; + + } // namespace + + auto init() -> void + { + auto static constinit is_initialized = std::atomic_flag{}; + + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] Memory management has already been initialized."); + } + + kstd::println("[x86_64:MEM] Enabling additional CPU protection features."); + + enable_cpu_protections(); + + region_based_allocator.emplace(collect_memory_information()); + buffered_allocator.emplace(&*region_based_allocator); + recursive_page_mapper.emplace(*buffered_allocator); + + kstd::println("[x86_64:MEM] Preparing new paging hierarchy."); + + auto new_pml4_frame = inject_faux_pml4(*buffered_allocator, *recursive_page_mapper); + + remap_kernel(*recursive_page_mapper); + remap_vga_text_mode_buffer(*recursive_page_mapper); + remap_multiboot_information(*recursive_page_mapper); + + kstd::println("[x86_64:MEM] Switching to new paging hierarchy."); + + auto cr3 = arch::cpu::cr3::read(); + cr3.frame(new_pml4_frame); + arch::cpu::cr3::write(cr3); + + set_frame_allocator(*buffered_allocator); + set_page_mapper(*recursive_page_mapper); + } + +} // namespace kapi::memory diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S index 0e164e2..056cd8e 100644 --- a/arch/x86_64/src/boot/boot32.S +++ b/arch/x86_64/src/boot/boot32.S @@ -1,4 +1,4 @@ -#include "x86_64/boot/boot.hpp" +#include "arch/boot/boot.hpp" /** * @brief Uninitialized data for the bootstrapping process. diff --git a/arch/x86_64/src/boot/initialize_runtime.cpp b/arch/x86_64/src/boot/initialize_runtime.cpp index f413448..b08c13c 100644 --- a/arch/x86_64/src/boot/initialize_runtime.cpp +++ b/arch/x86_64/src/boot/initialize_runtime.cpp @@ -2,16 +2,21 @@ #include #include -extern "C" +namespace arch::boot { - using global_initializer = auto (*)() -> void; - extern global_initializer __init_array_start; - extern global_initializer __init_array_end; - - auto invoke_global_constructors() -> void + extern "C" { - auto initializers = std::span{&__init_array_start, &__init_array_end}; - std::ranges::for_each(initializers, [](auto invokable) { std::invoke(invokable); }); + using global_initializer = auto (*)() -> void; + + extern global_initializer __init_array_start; + extern global_initializer __init_array_end; + + auto invoke_global_constructors() -> void + { + auto initializers = std::span{&__init_array_start, &__init_array_end}; + std::ranges::for_each(initializers, [](auto invokable) { std::invoke(invokable); }); + } } -} + +} // namespace arch::boot \ No newline at end of file diff --git a/arch/x86_64/src/debug/qemu_output.cpp b/arch/x86_64/src/debug/qemu_output.cpp index 0af5bdc..535017d 100644 --- a/arch/x86_64/src/debug/qemu_output.cpp +++ b/arch/x86_64/src/debug/qemu_output.cpp @@ -1,11 +1,11 @@ -#include "x86_64/debug/qemu_output.hpp" +#include "arch/debug/qemu_output.hpp" #include "kapi/cio.hpp" #include #include -namespace teachos::debug::x86_64 +namespace arch::debug { qemu_output::qemu_output(output_device & lower) @@ -13,7 +13,7 @@ namespace teachos::debug::x86_64 , m_present{port::read() == port::address} {} - auto qemu_output::write(cio::output_stream stream, std::string_view text) -> void + auto qemu_output::write(kapi::cio::output_stream stream, std::string_view text) -> void { if (m_present) { @@ -22,4 +22,4 @@ namespace teachos::debug::x86_64 m_lower.write(stream, text); } -} // namespace teachos::debug::x86_64 +} // namespace arch::debug diff --git a/arch/x86_64/src/kapi/cio.cpp b/arch/x86_64/src/kapi/cio.cpp deleted file mode 100644 index 5594fcc..0000000 --- a/arch/x86_64/src/kapi/cio.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "kapi/cio.hpp" - -#include "x86_64/debug/qemu_output.hpp" -#include "x86_64/vga/text.hpp" - -#include - -namespace teachos::cio -{ - - auto static constinit vga_device = std::optional{}; - auto static constinit dbg_device = std::optional{}; - - auto init() -> void - { - vga_device.emplace().cursor(false); - set_output_device(dbg_device.emplace(*vga_device)); - } - -} // namespace teachos::cio diff --git a/arch/x86_64/src/kapi/cpu.cpp b/arch/x86_64/src/kapi/cpu.cpp deleted file mode 100644 index 22543ee..0000000 --- a/arch/x86_64/src/kapi/cpu.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "kapi/cpu.hpp" - -namespace teachos::cpu -{ - - auto halt() -> void - { - asm volatile("1: hlt\njmp 1b"); - __builtin_unreachable(); - } - -} // namespace teachos::cpu diff --git a/arch/x86_64/src/kapi/memory.cpp b/arch/x86_64/src/kapi/memory.cpp deleted file mode 100644 index e2681a6..0000000 --- a/arch/x86_64/src/kapi/memory.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "kapi/memory.hpp" - -#include "kapi/boot.hpp" -#include "kapi/system.hpp" - -#include "x86_64/boot/boot.hpp" -#include "x86_64/boot/ld.hpp" -#include "x86_64/cpu/registers.hpp" -#include "x86_64/memory/buffered_allocator.hpp" -#include "x86_64/memory/kernel_mapper.hpp" -#include "x86_64/memory/mmu.hpp" -#include "x86_64/memory/page_table.hpp" -#include "x86_64/memory/page_utilities.hpp" -#include "x86_64/memory/paging_root.hpp" -#include "x86_64/memory/recursive_page_mapper.hpp" -#include "x86_64/memory/region_allocator.hpp" -#include "x86_64/memory/scoped_mapping.hpp" - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace teachos::memory -{ - - namespace - { - constexpr auto static unused_page_address = linear_address{0x0000'7fff'cafe'faceuz}; - constexpr auto static recursive_page_map_index = x86_64::page_table::entry_count - 2; - - //! Instantiate a basic, memory region based, early frame allocator for remapping. - auto collect_memory_information() - { - auto memory_map = boot::bootstrap_information.mbi->maybe_memory_map(); - if (!memory_map) - { - system::panic("[x86_64] Failed to create early allocator, no memory map available."); - } - - auto const & mbi = boot::bootstrap_information.mbi; - auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; - auto image_span = std::span{&boot::x86_64::_start_physical, &boot::x86_64::_end_physical}; - - return x86_64::region_allocator::memory_information{ - .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), - .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), - .memory_map = *memory_map, - }; - } - - //! Enable additional CPU protection features, required during later stages of the kernel. - auto enable_cpu_protections() -> void - { - cpu::x86_64::cr0::set(cpu::x86_64::cr0::flags::write_protect); - cpu::x86_64::i32_efer::set(cpu::x86_64::i32_efer::flags::execute_disable_bit_enable); - } - - //! Inject, or graft, a faux recursive PML4 into the active page mapping structure. - auto inject_faux_pml4(frame_allocator & allocator, page_mapper & mapper) - { - using namespace x86_64; - using entry_flags = page_table::entry::flags; - - auto page = page::containing(unused_page_address); - - auto temporary_mapper = scoped_mapping{page, mapper}; - auto new_pml4_frame = allocator.allocate(); - - auto pml4 = std::construct_at(temporary_mapper.map_as(*new_pml4_frame, entry_flags::writable)); - (*pml4)[recursive_page_map_index].frame(new_pml4_frame.value(), entry_flags::present | entry_flags::writable); - - auto pml4_index = pml_index<4>(page); - auto old_pml4 = paging_root::get(); - auto pml4_entry = (*old_pml4)[pml4_index]; - - auto pml3_index = pml_index<3>(page); - auto old_pml3 = old_pml4->next(pml4_index); - auto pml3_entry = (**old_pml3)[pml3_index]; - - auto pml2_index = pml_index<2>(page); - auto old_pml2 = (**old_pml3).next(pml3_index); - auto pml2_entry = (**old_pml2)[pml2_index]; - - auto pml1_index = pml_index<1>(page); - auto old_pml1 = (**old_pml2).next(pml2_index); - auto pml1_entry = (**old_pml1)[pml1_index]; - - (*paging_root::get())[recursive_page_map_index].frame(new_pml4_frame.value(), - entry_flags::present | entry_flags::writable); - - tlb_flush_all(); - - auto new_pml4 = paging_root::get(); - (*new_pml4)[pml4_index] = pml4_entry; - - auto new_pml3 = new_pml4->next(pml4_index); - (**new_pml3)[pml3_index] = pml3_entry; - - auto new_pml2 = (**new_pml3).next(pml3_index); - (**new_pml2)[pml2_index] = pml2_entry; - - auto new_pml1 = (**new_pml2).next(pml2_index); - (**new_pml1)[pml1_index] = pml1_entry; - - return *new_pml4_frame; - } - - auto remap_kernel(page_mapper & mapper) -> void - { - auto kernel_mapper = x86_64::kernel_mapper{boot::bootstrap_information.mbi}; - kernel_mapper.remap_kernel(mapper); - } - - auto remap_vga_text_mode_buffer(page_mapper & mapper) -> void - { - constexpr auto vga_base = std::uintptr_t{0xb8000}; - auto vga_physical_start = physical_address{vga_base}; - auto vga_virtual_start = linear_address{vga_base + std::bit_cast(&boot::x86_64::TEACHOS_VMA)}; - - auto page = page::containing(vga_virtual_start); - auto frame = frame::containing(vga_physical_start); - - mapper.map(page, frame, page_mapper::flags::writable); - } - - auto remap_multiboot_information(page_mapper & mapper) -> void - { - auto mbi_base = std::bit_cast(boot::bootstrap_information.mbi); - auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); - auto mbi_physical_start = physical_address{mbi_base & ~std::bit_cast(&boot::x86_64::TEACHOS_VMA)}; - auto mbi_virtual_start = linear_address{mbi_base}; - auto mbi_block_count = (mbi_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; - - for (auto i = 0uz; i < mbi_block_count; ++i) - { - auto page = page::containing(mbi_virtual_start) + i; - auto frame = frame::containing(mbi_physical_start) + i; - mapper.map(page, frame, page_mapper::flags::empty); - } - } - - auto constinit region_based_allocator = std::optional{}; - auto constinit buffered_allocator = std::optional>{}; - auto constinit recursive_page_mapper = std::optional{}; - - } // namespace - - auto init() -> void - { - auto static constinit is_initialized = std::atomic_flag{}; - - if (is_initialized.test_and_set()) - { - system::panic("[x86_64] Memory management has already been initialized."); - } - - kstd::println("[x86_64:MEM] Enabling additional CPU protection features."); - - enable_cpu_protections(); - - region_based_allocator.emplace(collect_memory_information()); - buffered_allocator.emplace(&*region_based_allocator); - recursive_page_mapper.emplace(*buffered_allocator); - - kstd::println("[x86_64:MEM] Preparing new paging hierarchy."); - - auto new_pml4_frame = inject_faux_pml4(*buffered_allocator, *recursive_page_mapper); - - remap_kernel(*recursive_page_mapper); - remap_vga_text_mode_buffer(*recursive_page_mapper); - remap_multiboot_information(*recursive_page_mapper); - - kstd::println("[x86_64:MEM] Switching to new paging hierarchy."); - - auto cr3 = cpu::x86_64::cr3::read(); - cr3.frame(new_pml4_frame); - cpu::x86_64::cr3::write(cr3); - - set_frame_allocator(*buffered_allocator); - set_page_mapper(*recursive_page_mapper); - } - -} // namespace teachos::memory diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index 89b2e83..08c32c5 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -1,9 +1,9 @@ -#include "x86_64/memory/kernel_mapper.hpp" +#include "arch/memory/kernel_mapper.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "x86_64/boot/ld.hpp" +#include "arch/boot/ld.hpp" #include @@ -19,7 +19,7 @@ #include #include -namespace teachos::memory::x86_64 +namespace arch::memory { namespace @@ -39,15 +39,15 @@ namespace teachos::memory::x86_64 kernel_mapper::kernel_mapper(multiboot2::information_view const * mbi) : m_mbi{std::move(mbi)} - , m_kernel_load_base{std::bit_cast(&boot::x86_64::TEACHOS_VMA)} + , m_kernel_load_base{std::bit_cast(&arch::boot::TEACHOS_VMA)} {} - auto kernel_mapper::remap_kernel(page_mapper & mapper) -> void + auto kernel_mapper::remap_kernel(kapi::memory::page_mapper & mapper) -> void { auto elf_information = m_mbi->maybe_elf_symbols(); if (!elf_information) { - system::panic("[x86_64:MEM] ELF section information is not available."); + kapi::system::panic("[x86_64:MEM] ELF section information is not available."); } auto sections = *elf_information; @@ -61,38 +61,38 @@ namespace teachos::memory::x86_64 if (allocated_sections.empty()) { - system::panic("[x86_64:MEM] No allocated ELF sections were found."); + kapi::system::panic("[x86_64:MEM] No allocated ELF sections were found."); } std::ranges::for_each(allocated_sections, [&](auto const & section) -> auto { map_section(section, sections.name(section), mapper); }); } - auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, page_mapper & mapper) - -> void + auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, + kapi::memory::page_mapper & mapper) -> void { auto number_of_pages = (section.size + (PLATFORM_PAGE_SIZE - 1)) / PLATFORM_PAGE_SIZE; - auto linear_start_address = linear_address{section.virtual_load_address}; - auto physical_start_address = physical_address{section.virtual_load_address & ~m_kernel_load_base}; + auto linear_start_address = kapi::memory::linear_address{section.virtual_load_address}; + auto physical_start_address = kapi::memory::physical_address{section.virtual_load_address & ~m_kernel_load_base}; kstd::println("[x86_64:MEM] mapping {}" "\n {} bytes -> page count: {}" "\n {} @ {}", name, section.size, number_of_pages, linear_start_address, physical_start_address); - auto first_page = page::containing(linear_start_address); - auto first_frame = frame::containing(physical_start_address); + auto first_page = kapi::memory::page::containing(linear_start_address); + auto first_frame = kapi::memory::frame::containing(physical_start_address); - auto page_flags = page_mapper::flags::empty; + auto page_flags = kapi::memory::page_mapper::flags::empty; if (section.writable()) { - page_flags |= page_mapper::flags::writable; + page_flags |= kapi::memory::page_mapper::flags::writable; } if (section.executable()) { - page_flags |= page_mapper::flags::executable; + page_flags |= kapi::memory::page_mapper::flags::executable; } auto is_prefix_of_name = [=](auto prefix) -> bool { @@ -101,7 +101,7 @@ namespace teachos::memory::x86_64 if (!std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) { - page_flags |= page_mapper::flags::supervisor_only; + page_flags |= kapi::memory::page_mapper::flags::supervisor_only; } for (auto i = 0uz; i < number_of_pages; ++i) @@ -110,4 +110,4 @@ namespace teachos::memory::x86_64 } } -} // namespace teachos::memory::x86_64 \ No newline at end of file +} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp index e15d94e..ea23278 100644 --- a/arch/x86_64/src/memory/mmu.cpp +++ b/arch/x86_64/src/memory/mmu.cpp @@ -1,14 +1,12 @@ -#include "x86_64/memory/mmu.hpp" +#include "arch/memory/mmu.hpp" #include "kapi/memory.hpp" -#include "x86_64/cpu/registers.hpp" +#include "arch/cpu/registers.hpp" -namespace teachos::memory::x86_64 +namespace arch::memory { - namespace cpu = cpu::x86_64; - - auto tlb_flush(linear_address address) -> void + auto tlb_flush(kapi::memory::linear_address address) -> void { asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); } @@ -18,4 +16,4 @@ namespace teachos::memory::x86_64 auto paging_root = cpu::cr3::read(); cpu::cr3::write(paging_root); } -} // namespace teachos::memory::x86_64 +} // namespace arch::memory diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index 2de099d..26cdd29 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -1,4 +1,4 @@ -#include "x86_64/memory/page_table.hpp" +#include "arch/memory/page_table.hpp" #include "kapi/memory.hpp" @@ -9,7 +9,7 @@ #include #include -namespace teachos::memory::x86_64 +namespace arch::memory { auto page_table::entry::clear() noexcept -> void @@ -44,16 +44,16 @@ namespace teachos::memory::x86_64 return *this; } - auto page_table::entry::frame() const noexcept -> std::optional + auto page_table::entry::frame() const noexcept -> std::optional { if (present()) { - return frame::containing(physical_address{m_raw & frame_number_mask}); + return kapi::memory::frame::containing(kapi::memory::physical_address{m_raw & frame_number_mask}); } return std::nullopt; } - auto page_table::entry::frame(struct frame frame, flags flags) noexcept -> void + auto page_table::entry::frame(kapi::memory::frame frame, flags flags) noexcept -> void { m_raw = (frame.start_address().raw() | static_cast(flags)); }; @@ -79,4 +79,4 @@ namespace teachos::memory::x86_64 [](auto const & entry) -> auto { return entry.all_flags() == entry::flags::empty; }); } -} // namespace teachos::memory::x86_64 +} // namespace arch::memory diff --git a/arch/x86_64/src/memory/paging_root.cpp b/arch/x86_64/src/memory/paging_root.cpp index d849a82..41f40ed 100644 --- a/arch/x86_64/src/memory/paging_root.cpp +++ b/arch/x86_64/src/memory/paging_root.cpp @@ -1,9 +1,9 @@ -#include "x86_64/memory/paging_root.hpp" +#include "arch/memory/paging_root.hpp" #include #include -namespace teachos::memory::x86_64 +namespace arch::memory { namespace @@ -16,4 +16,4 @@ namespace teachos::memory::x86_64 return std::bit_cast(recursive_base); } -} // namespace teachos::memory::x86_64 \ No newline at end of file +} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/src/memory/recursive_page_mapper.cpp b/arch/x86_64/src/memory/recursive_page_mapper.cpp index c5aabcb..d8273e1 100644 --- a/arch/x86_64/src/memory/recursive_page_mapper.cpp +++ b/arch/x86_64/src/memory/recursive_page_mapper.cpp @@ -1,16 +1,16 @@ -#include "x86_64/memory/recursive_page_mapper.hpp" +#include "arch/memory/recursive_page_mapper.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "x86_64/memory/page_table.hpp" -#include "x86_64/memory/page_utilities.hpp" -#include "x86_64/memory/paging_root.hpp" +#include "arch/memory/page_table.hpp" +#include "arch/memory/page_utilities.hpp" +#include "arch/memory/paging_root.hpp" #include #include -namespace teachos::memory::x86_64 +namespace arch::memory { namespace { @@ -22,7 +22,8 @@ namespace teachos::memory::x86_64 //! added, thus still enforcing non-writability and non-execution of the affected page. template requires(Level > 1uz && Level <= PLATFORM_PAGING_LEVELS) - auto do_map(recursive_page_table * pml, page page, frame_allocator & allocator, page_mapper::flags flags) + auto do_map(recursive_page_table * pml, kapi::memory::page page, kapi::memory::frame_allocator & allocator, + kapi::memory::page_mapper::flags flags) { auto index = pml_index(page); auto entry_flags = to_table_flags(flags); @@ -41,12 +42,13 @@ namespace teachos::memory::x86_64 } //! Perform the actual PML1 update. - auto do_map(page_table * pml, page page, frame frame, page_mapper::flags flags) -> std::optional + auto do_map(page_table * pml, kapi::memory::page page, kapi::memory::frame frame, + kapi::memory::page_mapper::flags flags) -> std::optional { auto index = pml_index<1>(page); if ((*pml)[index].present()) { - system::panic("[x86_64:MEM] Tried to map a page that is already mapped"); + kapi::system::panic("[x86_64:MEM] Tried to map a page that is already mapped"); } (*pml)[index].frame(frame, page_table::entry::flags::present | to_table_flags(flags)); return std::optional{static_cast(page.start_address())}; @@ -54,11 +56,11 @@ namespace teachos::memory::x86_64 } // namespace - recursive_page_mapper::recursive_page_mapper(frame_allocator & allocator) + recursive_page_mapper::recursive_page_mapper(kapi::memory::frame_allocator & allocator) : m_allocator{&allocator} {} - auto recursive_page_mapper::map(page page, frame frame, flags flags) -> std::byte * + auto recursive_page_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte * { auto pml4 = static_cast *>((paging_root::get())); @@ -70,15 +72,15 @@ namespace teachos::memory::x86_64 .value_or(nullptr); } - auto recursive_page_mapper::unmap(page page) -> void + auto recursive_page_mapper::unmap(kapi::memory::page page) -> void { if (!try_unmap(page)) { - system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped."); + kapi::system::panic("[x86_64:MEM] Tried to unmap a page that was not mapped."); } } - auto recursive_page_mapper::try_unmap(page page) noexcept -> bool + auto recursive_page_mapper::try_unmap(kapi::memory::page page) noexcept -> bool { if (!paging_root::get()->translate(page)) { @@ -116,4 +118,4 @@ namespace teachos::memory::x86_64 return true; } -} // namespace teachos::memory::x86_64 \ No newline at end of file +} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 7a8fb8b..a2dfd48 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -1,4 +1,4 @@ -#include "x86_64/memory/region_allocator.hpp" +#include "arch/memory/region_allocator.hpp" #include "kapi/memory/address.hpp" #include "kapi/memory/frame.hpp" @@ -11,16 +11,17 @@ #include #include -namespace teachos::memory::x86_64 +namespace arch::memory { namespace { constexpr auto last_frame(multiboot2::memory_map::region const & region) { - return frame::containing(physical_address{region.base + region.size_in_B - 1}); + return kapi::memory::frame::containing(kapi::memory::physical_address{region.base + region.size_in_B - 1}); } - constexpr auto falls_within(frame const & candidate, frame const & start, frame const & end) + constexpr auto falls_within(kapi::memory::frame const & candidate, kapi::memory::frame const & start, + kapi::memory::frame const & end) { return candidate >= start && candidate <= end; } @@ -30,10 +31,10 @@ namespace teachos::memory::x86_64 : m_next_frame{} , m_current_region{} , m_memory_map{mem_info.memory_map} - , m_kernel_start(frame::containing(mem_info.image_range.first)) - , m_kernel_end(frame::containing(mem_info.image_range.second)) - , m_multiboot_start(frame::containing(mem_info.mbi_range.first)) - , m_multiboot_end(frame::containing(mem_info.mbi_range.second)) + , m_kernel_start{kapi::memory::frame::containing(mem_info.image_range.first)} + , m_kernel_end{kapi::memory::frame::containing(mem_info.image_range.second)} + , m_multiboot_start{kapi::memory::frame::containing(mem_info.mbi_range.first)} + , m_multiboot_end{kapi::memory::frame::containing(mem_info.mbi_range.second)} { choose_next_region(); } @@ -56,14 +57,14 @@ namespace teachos::memory::x86_64 } m_current_region = *lowest_region; - if (auto start_of_region = frame::containing(physical_address{m_current_region->base}); + if (auto start_of_region = kapi::memory::frame::containing(kapi::memory::physical_address{m_current_region->base}); start_of_region > m_next_frame) { m_next_frame = start_of_region; } } - auto region_allocator::find_next_frame() -> std::optional + auto region_allocator::find_next_frame() -> std::optional { if (!m_current_region || m_next_frame > last_frame(*m_current_region)) { @@ -97,7 +98,8 @@ namespace teachos::memory::x86_64 return m_current_region.transform([this](auto) -> auto { return m_next_frame; }); } - auto region_allocator::allocate_many(std::size_t count) noexcept -> std::optional> + auto region_allocator::allocate_many(std::size_t count) noexcept + -> std::optional> { while (m_current_region) { @@ -122,11 +124,11 @@ namespace teachos::memory::x86_64 return std::nullopt; } - auto region_allocator::release_many(std::pair) -> void {} + auto region_allocator::release_many(std::pair) -> void {} - auto region_allocator::next_free_frame() noexcept -> std::optional + auto region_allocator::next_free_frame() noexcept -> std::optional { return find_next_frame(); } -} // namespace teachos::memory::x86_64 +} // namespace arch::memory diff --git a/arch/x86_64/src/memory/scoped_mapping.cpp b/arch/x86_64/src/memory/scoped_mapping.cpp index 945183d..dde1dda 100644 --- a/arch/x86_64/src/memory/scoped_mapping.cpp +++ b/arch/x86_64/src/memory/scoped_mapping.cpp @@ -1,32 +1,32 @@ -#include "x86_64/memory/scoped_mapping.hpp" +#include "arch/memory/scoped_mapping.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "x86_64/memory/mmu.hpp" -#include "x86_64/memory/page_table.hpp" -#include "x86_64/memory/paging_root.hpp" +#include "arch/memory/mmu.hpp" +#include "arch/memory/page_table.hpp" +#include "arch/memory/paging_root.hpp" #include #include -namespace teachos::memory::x86_64 +namespace arch::memory { scoped_mapping::scoped_mapping(scoped_mapping && other) noexcept - : m_page{std::exchange(other.m_page, page{})} + : m_page{std::exchange(other.m_page, kapi::memory::page{})} , m_mapper{std::exchange(other.m_mapper, nullptr)} , m_mapped{std::exchange(other.m_mapped, false)} {} - scoped_mapping::scoped_mapping(page page, page_mapper & mapper) + scoped_mapping::scoped_mapping(kapi::memory::page page, kapi::memory::page_mapper & mapper) : m_page{page} , m_mapper{&mapper} , m_mapped{false} { if (paging_root::get()->translate(page)) { - system::panic("[MEM] Tried to map a page that is already mapped!"); + kapi::system::panic("[MEM] Tried to map a page that is already mapped!"); } } @@ -35,7 +35,7 @@ namespace teachos::memory::x86_64 if (m_mapped) { unmap(); - x86_64::tlb_flush(m_page.start_address()); + tlb_flush(m_page.start_address()); } } @@ -45,7 +45,7 @@ namespace teachos::memory::x86_64 return *this; } - auto scoped_mapping::map(frame frame, page_table::entry::flags flags) -> std::byte * + auto scoped_mapping::map(kapi::memory::frame frame, page_table::entry::flags flags) -> std::byte * { auto result = m_mapper->map(m_page, frame, to_mapper_flags(flags)); m_mapped = true; @@ -66,4 +66,4 @@ namespace teachos::memory::x86_64 swap(lhs.m_mapped, rhs.m_mapped); } -} // namespace teachos::memory::x86_64 \ No newline at end of file +} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/src/vga/text/buffer.cpp b/arch/x86_64/src/vga/text/buffer.cpp index 2dcf084..7112573 100644 --- a/arch/x86_64/src/vga/text/buffer.cpp +++ b/arch/x86_64/src/vga/text/buffer.cpp @@ -1,6 +1,6 @@ -#include "x86_64/vga/text/buffer.hpp" +#include "arch/vga/text/buffer.hpp" -#include "x86_64/vga/text/attribute.hpp" +#include "arch/vga/text/attribute.hpp" #include #include @@ -9,7 +9,7 @@ #include #include -namespace teachos::vga::x86_64::text +namespace arch::vga::text { buffer::buffer(std::size_t width, std::size_t height, cell * start, std::size_t position) : m_width{width} @@ -98,4 +98,4 @@ namespace teachos::vga::x86_64::text m_buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; } -} // namespace teachos::vga::x86_64::text +} // namespace arch::vga::text diff --git a/arch/x86_64/src/vga/text/device.cpp b/arch/x86_64/src/vga/text/device.cpp index 2da9e06..dcacd8c 100644 --- a/arch/x86_64/src/vga/text/device.cpp +++ b/arch/x86_64/src/vga/text/device.cpp @@ -1,16 +1,16 @@ #include "kapi/cio.hpp" -#include "x86_64/boot/boot.hpp" -#include "x86_64/boot/ld.hpp" -#include "x86_64/vga/crtc.hpp" -#include "x86_64/vga/text.hpp" +#include "arch/boot/boot.hpp" +#include "arch/boot/ld.hpp" +#include "arch/vga/crtc.hpp" +#include "arch/vga/text.hpp" #include #include #include #include -namespace teachos::vga::x86_64::text +namespace arch::vga::text { namespace { @@ -22,10 +22,10 @@ namespace teachos::vga::x86_64::text } // namespace device::device() - : m_buffer{default_buffer_width, default_buffer_height, - std::bit_cast(default_buffer_address + - std::bit_cast(&teachos::boot::x86_64::TEACHOS_VMA)), - boot::bootstrap_information.vga_buffer_index} + : m_buffer{ + default_buffer_width, default_buffer_height, + std::bit_cast(default_buffer_address + std::bit_cast(&boot::TEACHOS_VMA)), + kapi::boot::bootstrap_information.vga_buffer_index} { clear(); } @@ -43,12 +43,12 @@ namespace teachos::vga::x86_64::text crtc::data::write(crtc::data::read() | cursor_disable_byte); } - auto device::write(cio::output_stream stream, std::string_view text) -> void + auto device::write(kapi::cio::output_stream stream, std::string_view text) -> void { auto attributes = [&] -> attribute { switch (stream) { - case cio::output_stream::stderr: + case kapi::cio::output_stream::stderr: return red_on_black; default: return green_on_black; @@ -57,4 +57,4 @@ namespace teachos::vga::x86_64::text m_buffer.write(text, attributes); } -} // namespace teachos::vga::x86_64::text +} // namespace arch::vga::text diff --git a/kapi/include/kapi/boot.hpp b/kapi/include/kapi/boot.hpp index 9c344cf..55ca941 100644 --- a/kapi/include/kapi/boot.hpp +++ b/kapi/include/kapi/boot.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_BOOT_HPP #define TEACHOS_KAPI_BOOT_HPP -namespace teachos::boot +namespace kapi::boot { //! @qualifier platform-defined //! Information passed from the early pre-main stage to the kernel executable. @@ -12,6 +12,6 @@ namespace teachos::boot //! @qualifier platform-defined //! An object passed from the early pre-main stage to the kernel executable. extern "C" information const bootstrap_information; -} // namespace teachos::boot +} // namespace kapi::boot #endif diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp index 4d1fc3c..48f3000 100644 --- a/kapi/include/kapi/cio.hpp +++ b/kapi/include/kapi/cio.hpp @@ -8,7 +8,7 @@ #include #include -namespace teachos::cio +namespace kapi::cio { //! @qualifier platform-defined @@ -27,6 +27,6 @@ namespace teachos::cio auto write(output_stream stream, std::string_view text) -> void; -} // namespace teachos::cio +} // namespace kapi::cio #endif diff --git a/kapi/include/kapi/cio/output_device.hpp b/kapi/include/kapi/cio/output_device.hpp index bbdf6ed..f08d7ba 100644 --- a/kapi/include/kapi/cio/output_device.hpp +++ b/kapi/include/kapi/cio/output_device.hpp @@ -5,7 +5,7 @@ #include -namespace teachos::cio +namespace kapi::cio { enum struct output_stream @@ -34,6 +34,6 @@ namespace teachos::cio output_device() = default; }; -} // namespace teachos::cio +} // namespace kapi::cio #endif diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 0a26bcf..05b84b7 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,13 +1,13 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -namespace teachos::cpu +namespace kapi::cpu { //! @qualifier platform-defined //! Halt the CPU. //! //! This function terminates execution of the kernel. [[noreturn]] auto halt() -> void; -} // namespace teachos::cpu +} // namespace kapi::cpu #endif diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp index c2e9df8..87268ff 100644 --- a/kapi/include/kapi/memory.hpp +++ b/kapi/include/kapi/memory.hpp @@ -12,7 +12,7 @@ #include #include -namespace teachos::memory +namespace kapi::memory { //! @qualifier platform-defined @@ -73,6 +73,6 @@ namespace teachos::memory //! @param page The page to unmap auto unmap(page page) -> void; -} // namespace teachos::memory +} // namespace kapi::memory #endif diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp index 4fdaf5a..39eb1ee 100644 --- a/kapi/include/kapi/memory/address.hpp +++ b/kapi/include/kapi/memory/address.hpp @@ -11,7 +11,7 @@ #include #include -namespace teachos::memory +namespace kapi::memory { //! @qualifier kernel-defined @@ -99,15 +99,15 @@ namespace teachos::memory //! A physical address. using physical_address = address; -} // namespace teachos::memory +} // namespace kapi::memory namespace kstd { - template - struct formatter> : kstd::formatter + template + struct formatter> : kstd::formatter { - constexpr auto static suffix = Type == teachos::memory::address_type::linear ? "%lin" : "%phy"; + constexpr auto static suffix = Type == kapi::memory::address_type::linear ? "%lin" : "%phy"; constexpr auto parse(std::string_view context) -> std::string_view { @@ -120,7 +120,7 @@ namespace kstd return result; } - auto format(teachos::memory::address const & address, format_context & context) const -> void + auto format(kapi::memory::address const & address, format_context & context) const -> void { formatter::format(address.raw(), context); context.push(suffix); diff --git a/kapi/include/kapi/memory/chunk.hpp b/kapi/include/kapi/memory/chunk.hpp index fde2e36..dd97a58 100644 --- a/kapi/include/kapi/memory/chunk.hpp +++ b/kapi/include/kapi/memory/chunk.hpp @@ -6,7 +6,7 @@ #include #include -namespace teachos::memory +namespace kapi::memory { //! @qualifier kernel-defined @@ -99,6 +99,6 @@ namespace teachos::memory std::size_t m_number{}; }; -} // namespace teachos::memory +} // namespace kapi::memory #endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/frame.hpp b/kapi/include/kapi/memory/frame.hpp index 49f3bb0..e7b0832 100644 --- a/kapi/include/kapi/memory/frame.hpp +++ b/kapi/include/kapi/memory/frame.hpp @@ -6,7 +6,7 @@ #include "kapi/memory/address.hpp" #include "kapi/memory/chunk.hpp" -namespace teachos::memory +namespace kapi::memory { //! @qualifier kernel-defined @@ -34,6 +34,6 @@ namespace teachos::memory {} }; -} // namespace teachos::memory +} // namespace kapi::memory #endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/frame_allocator.hpp b/kapi/include/kapi/memory/frame_allocator.hpp index 8532a45..6ed114c 100644 --- a/kapi/include/kapi/memory/frame_allocator.hpp +++ b/kapi/include/kapi/memory/frame_allocator.hpp @@ -9,7 +9,7 @@ #include #include -namespace teachos::memory +namespace kapi::memory { //! The interface of all frame allocators. @@ -58,6 +58,6 @@ namespace teachos::memory frame_allocator() = default; }; -} // namespace teachos::memory +} // namespace kapi::memory #endif // TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP \ No newline at end of file diff --git a/kapi/include/kapi/memory/page.hpp b/kapi/include/kapi/memory/page.hpp index 57f4f09..55d6e75 100644 --- a/kapi/include/kapi/memory/page.hpp +++ b/kapi/include/kapi/memory/page.hpp @@ -6,7 +6,7 @@ #include "kapi/memory/address.hpp" #include "kapi/memory/chunk.hpp" -namespace teachos::memory +namespace kapi::memory { //! @qualifier kernel-defined @@ -34,6 +34,6 @@ namespace teachos::memory {} }; -} // namespace teachos::memory +} // namespace kapi::memory #endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/page_mapper.hpp b/kapi/include/kapi/memory/page_mapper.hpp index 2396249..07eabcf 100644 --- a/kapi/include/kapi/memory/page_mapper.hpp +++ b/kapi/include/kapi/memory/page_mapper.hpp @@ -10,7 +10,7 @@ #include -namespace teachos::memory +namespace kapi::memory { //! @qualifier platform-implemented @@ -74,12 +74,12 @@ namespace teachos::memory page_mapper() = default; }; -} // namespace teachos::memory +} // namespace kapi::memory namespace kstd::ext { template<> - struct is_bitfield_enum : std::true_type + struct is_bitfield_enum : std::true_type { }; } // namespace kstd::ext diff --git a/kapi/include/kapi/system.hpp b/kapi/include/kapi/system.hpp index e6b9826..6c5683a 100644 --- a/kapi/include/kapi/system.hpp +++ b/kapi/include/kapi/system.hpp @@ -4,7 +4,7 @@ #include #include -namespace teachos::system +namespace kapi::system { //! @qualifier kernel-defined @@ -15,6 +15,6 @@ namespace teachos::system //! @param message The message associated with the panic [[noreturn]] auto panic(std::string_view message, std::source_location = std::source_location::current()) -> void; -} // namespace teachos::system +} // namespace kapi::system #endif diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 7733c1b..97a0267 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,14 +1,15 @@ add_executable("kernel" - "src/main.cpp" + # Platform-independent KAPI implementation + "kapi/cio.cpp" + "kapi/memory.cpp" + "kapi/system.cpp" # KSTD OS Implementation - "src/kstd/os.cpp" - "src/kstd/print.cpp" + "kstd/os.cpp" + "kstd/print.cpp" - # Platform Independent KAPI implementation - "src/kapi/cio.cpp" - "src/kapi/memory.cpp" - "src/kapi/system.cpp" + # Kernel Implementation + "src/main.cpp" ) target_include_directories("kernel" PRIVATE diff --git a/kernel/kapi/cio.cpp b/kernel/kapi/cio.cpp new file mode 100644 index 0000000..d447a6a --- /dev/null +++ b/kernel/kapi/cio.cpp @@ -0,0 +1,36 @@ +#include "kapi/cio.hpp" + +#include +#include +#include + +namespace kapi::cio +{ + namespace + { + struct null_device final : public output_device + { + null_device static instance; + auto write(output_stream, std::string_view) -> void override {} + }; + + constinit null_device null_device::instance; + } // namespace + + constinit auto static active_device = static_cast(&null_device::instance); + + auto set_output_device(output_device & device) -> std::optional + { + if (&device == active_device) + { + return {}; + } + return std::exchange(active_device, &device); + } + + auto write(output_stream stream, std::string_view text) -> void + { + active_device->write(stream, text); + } + +} // namespace kapi::cio diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp new file mode 100644 index 0000000..7c9b1da --- /dev/null +++ b/kernel/kapi/memory.cpp @@ -0,0 +1,94 @@ +#include "kapi/memory.hpp" + +#include "kapi/system.hpp" + +#include +#include +#include + +namespace kapi::memory +{ + + namespace + { + struct bad_frame_allocator final : public frame_allocator + { + bad_frame_allocator static instance; + + auto allocate_many(std::size_t) noexcept -> std::optional> override + { + system::panic("Tried to allocate frames without an active allocator."); + } + + auto release_many(std::pair) -> void override + { + system::panic("Tried to release frames without an active allocator."); + } + }; + + struct bad_page_mapper final : public page_mapper + { + bad_page_mapper static instance; + + auto map(page, frame, flags) -> std::byte * override + { + system::panic("Tried to map a page without an active mapper."); + } + + auto unmap(page) -> void override + { + system::panic("Tried to unmap a page without an active mapper."); + } + + auto try_unmap(page) noexcept -> bool override + { + return false; + } + }; + + constinit bad_frame_allocator bad_frame_allocator::instance{}; + constinit bad_page_mapper bad_page_mapper::instance{}; + } // namespace + + constinit auto static active_frame_allocator = static_cast(&bad_frame_allocator::instance); + constinit auto static active_page_mapper = static_cast(&bad_page_mapper::instance); + + auto set_frame_allocator(frame_allocator & allocator) -> std::optional + { + if (&allocator == active_frame_allocator) + { + return {}; + } + return std::exchange(active_frame_allocator, &allocator); + } + + auto set_page_mapper(page_mapper & mapper) -> std::optional + { + if (&mapper == active_page_mapper) + { + return {}; + } + return std::exchange(active_page_mapper, &mapper); + } + + auto allocate_frame() -> std::optional + { + return active_frame_allocator->allocate(); + } + + auto allocate_many_frames(std::size_t count) -> std::optional> + { + return active_frame_allocator->allocate_many(count); + } + + auto map(page page, frame frame) -> std::byte * + { + return active_page_mapper->map(page, frame, page_mapper::flags::empty); + } + + auto unmap(page page) -> void + { + return active_page_mapper->unmap(page); + } + +} // namespace kapi::memory \ No newline at end of file diff --git a/kernel/kapi/system.cpp b/kernel/kapi/system.cpp new file mode 100644 index 0000000..a17d9b9 --- /dev/null +++ b/kernel/kapi/system.cpp @@ -0,0 +1,21 @@ +#include "kapi/system.hpp" + +#include "kapi/cpu.hpp" + +#include + +#include +#include + +namespace kapi::system +{ + + [[gnu::weak]] + auto panic(std::string_view message, std::source_location location) -> void + { + kstd::println(kstd::print_sink::stderr, "[PANIC] in {} : {} @ {}:{}", location.function_name(), message, + location.file_name(), location.line()); + cpu::halt(); + } + +} // namespace kapi::system diff --git a/kernel/kstd/os.cpp b/kernel/kstd/os.cpp new file mode 100644 index 0000000..21254c4 --- /dev/null +++ b/kernel/kstd/os.cpp @@ -0,0 +1,16 @@ +#include "kapi/system.hpp" + +#include + +#include +#include + +namespace kstd::os +{ + + auto panic(std::string_view message, std::source_location location) -> void + { + kapi::system::panic(message, location); + } + +} // namespace kstd::os \ No newline at end of file diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp new file mode 100644 index 0000000..c7d26ba --- /dev/null +++ b/kernel/kstd/print.cpp @@ -0,0 +1,145 @@ +#include "kapi/cio.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace kstd::os +{ + + namespace + { + struct write_buffer + { + using output_stream = kapi::cio::output_stream; + + constexpr auto static size = 128uz; + + write_buffer(write_buffer const &) = delete; + write_buffer(write_buffer &&) = delete; + auto operator=(write_buffer const &) -> write_buffer & = delete; + auto operator=(write_buffer &&) -> write_buffer & = delete; + + explicit write_buffer(output_stream stream) + : m_stream{stream} + {} + + ~write_buffer() noexcept + { + flush(); + } + + auto flush() noexcept -> void + { + if (m_position > 0) + { + std::string_view chunk{m_buffer.data(), m_position}; + kapi::cio::write(m_stream, chunk); + m_position = 0; + } + } + + auto static callback(void * object, std::string_view text) -> void + { + auto * self = static_cast(object); + for (char const character : text) + { + if (self->m_position >= size) + { + self->flush(); + } + self->m_buffer.at(self->m_position++) = character; + } + } + + private: + output_stream m_stream; + std::array m_buffer{}; + std::size_t m_position{}; + }; + + } // namespace + + auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void + { + auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr + : kapi::cio::output_stream::stdout}; + auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; + + auto current = format.begin(); + auto end = format.end(); + auto next_automatic_index = 0uz; + + while (current != end) + { + if (*current != '{') + { + auto start = current; + while (current != end && *current != '{') + { + std::advance(current, 1); + } + context.push(std::string_view(start, current - start)); + continue; + } + + if (std::next(current) != end && *(std::next(current)) == '{') + { + context.push('{'); + std::advance(current, 2); + continue; + } + + std::advance(current, 1); + + auto index = 0uz; + if (current != end && *current >= '0' && *current <= '9') + { + while (current != end && *current >= '0' && *current <= '9') + { + index = index * 10 + static_cast(*current - '0'); + std::advance(current, 1); + } + } + else + { + index = next_automatic_index++; + } + + auto remaining_fmt = std::string_view{current, static_cast(std::distance(current, end))}; + + auto const arg = args.get(index); + if (arg.format) + { + auto const after_specs = arg.format(arg.value, remaining_fmt, context); + auto const consumed = remaining_fmt.size() - after_specs.size(); + std::advance(current, consumed); + } + else + { + context.push("{?}"); + while (current != end && *current != '}') + std::advance(current, 1); + } + + if (current != end && *current == '}') + { + std::advance(current, 1); + } + else + { + context.push("{fmt-err}"); + while (current != end && *current != '}') + std::advance(current, 1); + if (current != end) + std::advance(current, 1); + } + } + } + +} // namespace kstd::os diff --git a/kernel/src/kapi/cio.cpp b/kernel/src/kapi/cio.cpp deleted file mode 100644 index 01c6420..0000000 --- a/kernel/src/kapi/cio.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "kapi/cio.hpp" - -#include -#include -#include - -namespace teachos::cio -{ - namespace - { - struct null_device final : public output_device - { - null_device static instance; - auto write(output_stream, std::string_view) -> void override {} - }; - - constinit null_device null_device::instance; - } // namespace - - constinit auto static active_device = static_cast(&null_device::instance); - - auto set_output_device(output_device & device) -> std::optional - { - if (&device == active_device) - { - return {}; - } - return std::exchange(active_device, &device); - } - - auto write(output_stream stream, std::string_view text) -> void - { - active_device->write(stream, text); - } - -} // namespace teachos::cio diff --git a/kernel/src/kapi/memory.cpp b/kernel/src/kapi/memory.cpp deleted file mode 100644 index ebd4c15..0000000 --- a/kernel/src/kapi/memory.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "kapi/memory.hpp" - -#include "kapi/system.hpp" - -#include -#include -#include - -namespace teachos::memory -{ - - namespace - { - struct bad_frame_allocator final : public frame_allocator - { - bad_frame_allocator static instance; - - auto allocate_many(std::size_t) noexcept -> std::optional> override - { - system::panic("Tried to allocate frames without an active allocator."); - } - - auto release_many(std::pair) -> void override - { - system::panic("Tried to release frames without an active allocator."); - } - }; - - struct bad_page_mapper final : public page_mapper - { - bad_page_mapper static instance; - - auto map(page, frame, flags) -> std::byte * override - { - system::panic("Tried to map a page without an active mapper."); - } - - auto unmap(page) -> void override - { - system::panic("Tried to unmap a page without an active mapper."); - } - - auto try_unmap(page) noexcept -> bool override - { - return false; - } - }; - - constinit bad_frame_allocator bad_frame_allocator::instance{}; - constinit bad_page_mapper bad_page_mapper::instance{}; - } // namespace - - constinit auto static active_frame_allocator = static_cast(&bad_frame_allocator::instance); - constinit auto static active_page_mapper = static_cast(&bad_page_mapper::instance); - - auto set_frame_allocator(frame_allocator & allocator) -> std::optional - { - if (&allocator == active_frame_allocator) - { - return {}; - } - return std::exchange(active_frame_allocator, &allocator); - } - - auto set_page_mapper(page_mapper & mapper) -> std::optional - { - if (&mapper == active_page_mapper) - { - return {}; - } - return std::exchange(active_page_mapper, &mapper); - } - - auto allocate_frame() -> std::optional - { - return active_frame_allocator->allocate(); - } - - auto allocate_many_frames(std::size_t count) -> std::optional> - { - return active_frame_allocator->allocate_many(count); - } - - auto map(page page, frame frame) -> std::byte * - { - return active_page_mapper->map(page, frame, page_mapper::flags::empty); - } - - auto unmap(page page) -> void - { - return active_page_mapper->unmap(page); - } - -} // namespace teachos::memory \ No newline at end of file diff --git a/kernel/src/kapi/system.cpp b/kernel/src/kapi/system.cpp deleted file mode 100644 index cdde049..0000000 --- a/kernel/src/kapi/system.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "kapi/system.hpp" - -#include "kapi/cpu.hpp" - -#include - -#include -#include - -namespace teachos::system -{ - - [[gnu::weak]] - auto panic(std::string_view message, std::source_location location) -> void - { - kstd::println(kstd::print_sink::stderr, "[PANIC] in {} : {} @ {}:{}", location.function_name(), message, - location.file_name(), location.line()); - cpu::halt(); - } - -} // namespace teachos::system diff --git a/kernel/src/kstd/os.cpp b/kernel/src/kstd/os.cpp deleted file mode 100644 index 5280f9c..0000000 --- a/kernel/src/kstd/os.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "kapi/system.hpp" - -#include - -#include -#include - -namespace kstd::os -{ - - auto panic(std::string_view message, std::source_location location) -> void - { - teachos::system::panic(message, location); - } - -} // namespace kstd::os \ No newline at end of file diff --git a/kernel/src/kstd/print.cpp b/kernel/src/kstd/print.cpp deleted file mode 100644 index 2665b9a..0000000 --- a/kernel/src/kstd/print.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "kapi/cio.hpp" - -#include -#include -#include - -#include -#include -#include -#include - -namespace kstd::os -{ - - namespace - { - struct write_buffer - { - using output_stream = teachos::cio::output_stream; - - constexpr auto static size = 128uz; - - write_buffer(write_buffer const &) = delete; - write_buffer(write_buffer &&) = delete; - auto operator=(write_buffer const &) -> write_buffer & = delete; - auto operator=(write_buffer &&) -> write_buffer & = delete; - - explicit write_buffer(output_stream stream) - : m_stream{stream} - {} - - ~write_buffer() noexcept - { - flush(); - } - - auto flush() noexcept -> void - { - if (m_position > 0) - { - std::string_view chunk{m_buffer.data(), m_position}; - teachos::cio::write(m_stream, chunk); - m_position = 0; - } - } - - auto static callback(void * object, std::string_view text) -> void - { - auto * self = static_cast(object); - for (char const character : text) - { - if (self->m_position >= size) - { - self->flush(); - } - self->m_buffer.at(self->m_position++) = character; - } - } - - private: - output_stream m_stream; - std::array m_buffer{}; - std::size_t m_position{}; - }; - - } // namespace - - auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void - { - auto writer = write_buffer{(sink == print_sink::stderr) ? teachos::cio::output_stream::stderr - : teachos::cio::output_stream::stdout}; - auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; - - auto current = format.begin(); - auto end = format.end(); - auto next_automatic_index = 0uz; - - while (current != end) - { - if (*current != '{') - { - auto start = current; - while (current != end && *current != '{') - { - std::advance(current, 1); - } - context.push(std::string_view(start, current - start)); - continue; - } - - if (std::next(current) != end && *(std::next(current)) == '{') - { - context.push('{'); - std::advance(current, 2); - continue; - } - - std::advance(current, 1); - - auto index = 0uz; - if (current != end && *current >= '0' && *current <= '9') - { - while (current != end && *current >= '0' && *current <= '9') - { - index = index * 10 + static_cast(*current - '0'); - std::advance(current, 1); - } - } - else - { - index = next_automatic_index++; - } - - auto remaining_fmt = std::string_view{current, static_cast(std::distance(current, end))}; - - auto const arg = args.get(index); - if (arg.format) - { - auto const after_specs = arg.format(arg.value, remaining_fmt, context); - auto const consumed = remaining_fmt.size() - after_specs.size(); - std::advance(current, consumed); - } - else - { - context.push("{?}"); - while (current != end && *current != '}') - std::advance(current, 1); - } - - if (current != end && *current == '}') - { - std::advance(current, 1); - } - else - { - context.push("{fmt-err}"); - while (current != end && *current != '}') - std::advance(current, 1); - if (current != end) - std::advance(current, 1); - } - } - } - -} // namespace kstd::os diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index f1e5dd0..8732fa2 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -6,11 +6,11 @@ auto main() -> int { - teachos::cio::init(); + kapi::cio::init(); kstd::println("[OS] IO subsystem initialized."); - teachos::memory::init(); + kapi::memory::init(); kstd::println("[OS] Memory subsystem initialized."); - teachos::system::panic("Returning from kernel main!"); + kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3