From cb60cdebdc36dd2358fe1ce06eec197e213af491 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 17:35:54 +0100 Subject: kapi/cpu: introduce CPU API --- arch/x86_64/include/arch/cpu/interrupts.hpp | 46 +++++----- arch/x86_64/kapi/cpu.cpp | 21 +++++ arch/x86_64/kapi/system.cpp | 10 +-- arch/x86_64/src/cpu/interrupts.cpp | 125 ++++++++++++++++++++-------- kapi/include/kapi/cpu.hpp | 7 ++ kapi/include/kapi/cpu/exception.hpp | 60 +++++++++++++ kernel/CMakeLists.txt | 2 + kernel/include/kernel/cpu.hpp | 14 ++++ kernel/kapi/cpu.cpp | 30 +++++++ kernel/src/cpu.cpp | 46 ++++++++++ kernel/src/main.cpp | 5 ++ 11 files changed, 301 insertions(+), 65 deletions(-) create mode 100644 kapi/include/kapi/cpu/exception.hpp create mode 100644 kernel/include/kernel/cpu.hpp create mode 100644 kernel/kapi/cpu.cpp create mode 100644 kernel/src/cpu.cpp diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 8f156a4..08ecd9c 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP #define TEACHOS_X86_64_CPU_INTERRUPTS_HPP +#include "kapi/memory.hpp" + #include "arch/cpu/segment_selector.hpp" #include @@ -57,36 +59,36 @@ namespace arch::cpu { struct { - std::uint64_t r15; - std::uint64_t r14; - std::uint64_t r13; - std::uint64_t r12; - std::uint64_t r11; - std::uint64_t r10; - std::uint64_t r9; - std::uint64_t r8; - std::uint64_t rdi; - std::uint64_t rsi; - std::uint64_t rbp; - std::uint64_t rdx; - std::uint64_t rcx; - std::uint64_t rbx; - std::uint64_t rax; + std::uint64_t r15{}; + std::uint64_t r14{}; + std::uint64_t r13{}; + std::uint64_t r12{}; + std::uint64_t r11{}; + std::uint64_t r10{}; + std::uint64_t r9{}; + std::uint64_t r8{}; + std::uint64_t rdi{}; + std::uint64_t rsi{}; + std::uint64_t rbp{}; + std::uint64_t rdx{}; + std::uint64_t rcx{}; + std::uint64_t rbx{}; + std::uint64_t rax{}; } handler_saved; struct { - std::uint64_t number; - std::uint64_t error_code; + std::uint64_t number{}; + std::uint64_t error_code{}; } interrupt; struct { - std::uint64_t rip; - std::uint64_t cs; - std::uint64_t rflags; - std::uint64_t rsp; - std::uint64_t ss; + kapi::memory::linear_address rip{}; + std::uint64_t cs{}; + std::uint64_t rflags{}; + kapi::memory::linear_address rsp{}; + std::uint64_t ss{}; } cpu_saved; }; diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 2a0f8f7..693d328 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -1,8 +1,29 @@ #include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include "arch/cpu/initialization.hpp" +#include "arch/cpu/interrupts.hpp" + +#include + namespace kapi::cpu { + auto init() -> void + { + auto static constinit is_initialized = std::atomic_flag{}; + + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] CPU has already been initialized."); + } + + arch::cpu::initialize_descriptors(); + arch::cpu::initialize_legacy_interrupts(); + arch::cpu::enable_interrupts(); + } + auto halt() -> void { asm volatile("1: hlt\njmp 1b"); diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp index 301169f..09c7152 100644 --- a/arch/x86_64/kapi/system.cpp +++ b/arch/x86_64/kapi/system.cpp @@ -1,16 +1,8 @@ #include "kapi/system.hpp" -#include "arch/cpu/initialization.hpp" -#include "arch/cpu/interrupts.hpp" - namespace kapi::system { - auto memory_initialized() -> void - { - arch::cpu::initialize_descriptors(); - arch::cpu::initialize_legacy_interrupts(); - arch::cpu::enable_interrupts(); - } + auto memory_initialized() -> void {} } // namespace kapi::system \ No newline at end of file diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 048c461..6f66bbd 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -1,6 +1,7 @@ #include "arch/cpu/interrupts.hpp" #include "kapi/cpu.hpp" +#include "kapi/memory.hpp" #include "arch/cpu/legacy_pic.hpp" #include "arch/cpu/segment_selector.hpp" @@ -14,38 +15,92 @@ namespace arch::cpu namespace { - constexpr auto isr_number_page_fault = 0x0e; - constexpr auto isr_number_legacy_start = 0x20; - constexpr auto isr_number_legacy_end = 0x29; - constexpr auto isr_number_cascade_start = 0x2c; - constexpr auto isr_number_cascade_end = 0x2f; - - auto handle_page_fault(interrupt_frame * frame) -> void + enum struct exception { - auto fault_address = std::uintptr_t{}; - asm volatile("mov %%cr2, %0" : "=r"(fault_address)); - - auto const present = (frame->interrupt.error_code & 0x1) != 0; - auto const write = (frame->interrupt.error_code & 0x2) != 0; - auto const user = (frame->interrupt.error_code & 0x4) != 0; + divide_error, + debug_exception, + non_maskable_interrupt, + breakpoint, + overflow, + bound_range_exceeded, + invalid_opcode, + device_not_available, + double_fault, + coprocessor_segment_overrun, + invalid_tss, + stack_segment_fault, + general_protection_fault, + page_fault, + x87_fpu_floating_point_error, + alignment_check, + machine_check, + simd_floating_point_error, + virtualization_exception, + control_protection_exception, + vmm_communication_exception, + security_exception, + }; + + constexpr auto pic_master_irq_start = 0x20; + constexpr auto pic_master_irq_end = pic_master_irq_start + 8; + constexpr auto pic_slave_irq_start = pic_master_irq_end; + constexpr auto pic_slave_irq_end = pic_slave_irq_start + 8; + + constexpr auto to_exception_type(exception e) + { + switch (e) + { + case exception::divide_error: + case exception::x87_fpu_floating_point_error: + case exception::simd_floating_point_error: + return kapi::cpu::exception::type::arithmetic_error; + case exception::breakpoint: + return kapi::cpu::exception::type::breakpoint; + case exception::invalid_opcode: + return kapi::cpu::exception::type::illegal_instruction; + case exception::stack_segment_fault: + return kapi::cpu::exception::type::memory_access_fault; + case exception::general_protection_fault: + return kapi::cpu::exception::type::privilege_violation; + case exception::alignment_check: + return kapi::cpu::exception::type::alignment_fault; + default: + return kapi::cpu::exception::type::unknown; + } + } - kstd::println(kstd::print_sink::stderr, "[x86_64:MMU] PAGE FAULT!"); - kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", fault_address); - kstd::println(kstd::print_sink::stderr, "\tPresent: {}", present); - kstd::println(kstd::print_sink::stderr, "\tWrite: {}", write); - kstd::println(kstd::print_sink::stderr, "\tUser: {}", user); - kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", frame->cpu_saved.rip); - kstd::println(kstd::print_sink::stderr, "\tHalting the system now!"); + auto dispatch_exception(interrupt_frame * frame) -> bool + { + auto type = to_exception_type(static_cast(frame->interrupt.number)); + auto fault_address = kapi::memory::linear_address{}; + auto instruction_pointer = frame->cpu_saved.rip; - kapi::cpu::halt(); + switch (type) + { + case kapi::cpu::exception::type::page_fault: + { + asm volatile("mov %%cr2, %0" : "=r"(fault_address)); + auto present = (frame->interrupt.error_code & 0x1) != 0; + auto write = (frame->interrupt.error_code & 0x2) != 0; + auto user = (frame->interrupt.error_code & 0x4) != 0; + + return kapi::cpu::get_exception_handler().handle( + {type, instruction_pointer, fault_address, present, write, user}); + } + default: + return kapi::cpu::get_exception_handler().handle({type, instruction_pointer}); + } } auto handle_legacy_interrupt(interrupt_frame * frame) -> void { kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number); + if (frame->interrupt.number >= pic_slave_irq_start) + { + pic_slave_control_port::write(pic_end_of_interrupt); + } pic_master_control_port::write(pic_end_of_interrupt); - pic_slave_control_port::write(pic_end_of_interrupt); } } // namespace @@ -55,23 +110,25 @@ namespace arch::cpu auto interrupt_dispatch(interrupt_frame * frame) -> void { - if ((frame->interrupt.number >= isr_number_legacy_start && frame->interrupt.number <= isr_number_legacy_end) || - (frame->interrupt.number >= isr_number_cascade_start && frame->interrupt.number <= isr_number_cascade_end)) + auto [number, code] = frame->interrupt; + + if (number < pic_master_irq_start && !dispatch_exception(frame)) { - handle_legacy_interrupt(frame); - return; + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + kapi::cpu::halt(); } - switch (frame->interrupt.number) + if ((number >= pic_master_irq_start && number < pic_master_irq_end) || + (number >= pic_slave_irq_start && number < pic_slave_irq_end)) { - case isr_number_page_fault: - handle_page_fault(frame); - break; - default: - kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {:#04x} received with code {:#04x}", - frame->interrupt.number, frame->interrupt.error_code); - kapi::cpu::halt(); + handle_legacy_interrupt(frame); + return; } + + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} received with code {:#04x}", + frame->interrupt.number, frame->interrupt.error_code); + kapi::cpu::halt(); } } diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 05b84b7..1dd7cfe 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP +#include "kapi/cpu/exception.hpp" + namespace kapi::cpu { //! @qualifier platform-defined @@ -8,6 +10,11 @@ namespace kapi::cpu //! //! This function terminates execution of the kernel. [[noreturn]] auto halt() -> void; + + //! @qualifier platform-defined + //! Perform early CPU initialization. + auto init() -> void; + } // namespace kapi::cpu #endif diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp new file mode 100644 index 0000000..09e15a7 --- /dev/null +++ b/kapi/include/kapi/cpu/exception.hpp @@ -0,0 +1,60 @@ +#ifndef TEACHOS_KAPI_CPU_EXCEPTION_HPP +#define TEACHOS_KAPI_CPU_EXCEPTION_HPP + +// IWYU pragma: private, include "kapi/cpu.hpp" + +#include "kapi/memory.hpp" + +#include + +namespace kapi::cpu +{ + + struct exception + { + enum class type : std::uint8_t + { + unknown, + page_fault, + alignment_fault, + memory_access_fault, + illegal_instruction, + privilege_violation, + arithmetic_error, + breakpoint, + single_step, + }; + + //! The type of exception. + type type{}; + + //! The instruction pointer at the time of the exception. + kapi::memory::linear_address instruction_pointer{}; + + //! The memory address that caused the exception. + kapi::memory::linear_address access_address{}; + + //! Whether the page was present at the time of the exception. + bool is_present{}; + + //! Whether the exception was caused by a write access. + bool is_write_access{}; + + //! Whether the exception was caused by a user mode access. + bool is_user_mode{}; + }; + + struct exception_handler + { + virtual ~exception_handler() = default; + + virtual auto handle(exception const & context) -> bool = 0; + }; + + auto get_exception_handler() -> exception_handler &; + + auto set_exception_handler(exception_handler & handler) -> void; + +} // namespace kapi::cpu + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 398022c..d9a5c75 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -2,6 +2,7 @@ add_executable("kernel" # Platform-independent KAPI implementation "kapi/boot_modules.cpp" "kapi/cio.cpp" + "kapi/cpu.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -10,6 +11,7 @@ add_executable("kernel" "kstd/print.cpp" # Kernel Implementation + "src/cpu.cpp" "src/main.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" diff --git a/kernel/include/kernel/cpu.hpp b/kernel/include/kernel/cpu.hpp new file mode 100644 index 0000000..d4e1ced --- /dev/null +++ b/kernel/include/kernel/cpu.hpp @@ -0,0 +1,14 @@ +#ifndef TEACHOS_KERNEL_CPU_HPP +#define TEACHOS_KERNEL_CPU_HPP + +namespace kernel::cpu +{ + + //! Initialize the kernel heap. + auto init() -> void; + + //! + +} // namespace kernel::cpu + +#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp new file mode 100644 index 0000000..2089098 --- /dev/null +++ b/kernel/kapi/cpu.cpp @@ -0,0 +1,30 @@ +#include "kapi/cpu.hpp" + +namespace kapi::cpu +{ + + namespace + { + struct null_exception_handler : exception_handler + { + auto handle(exception const &) -> bool override + { + return false; + } + } static constinit default_exception_handler; + + exception_handler * current_handler = &default_exception_handler; + + } // namespace + + auto get_exception_handler() -> exception_handler & + { + return *current_handler; + } + + auto set_exception_handler(exception_handler & handler) -> void + { + current_handler = &handler; + } + +} // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/src/cpu.cpp b/kernel/src/cpu.cpp new file mode 100644 index 0000000..fc460c9 --- /dev/null +++ b/kernel/src/cpu.cpp @@ -0,0 +1,46 @@ +#include "kernel/cpu.hpp" + +#include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include + +namespace kernel::cpu +{ + + namespace + { + struct exception_handler : kapi::cpu::exception_handler + { + auto handle(kapi::cpu::exception const & context) -> bool override + { + switch (context.type) + { + case kapi::cpu::exception::type::page_fault: + return handle_page_fault(context); + default: + return false; + } + } + + private: + auto handle_page_fault(kapi::cpu::exception const & context) -> bool + { + kstd::println(kstd::print_sink::stderr, "[OS:CPU] PAGE FAULT!"); + kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); + kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); + kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); + kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); + kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", context.instruction_pointer); + + kapi::system::panic("Halting the system due to an unrecoverable page fault."); + } + } static constinit handler; + } // namespace + + auto init() -> void + { + kapi::cpu::set_exception_handler(handler); + } + +} // namespace kernel::cpu \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index eb59402..0416ee9 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,8 +1,10 @@ #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" +#include "kapi/cpu.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include "kernel/cpu.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" @@ -90,6 +92,9 @@ auto main() -> int kapi::cio::init(); kstd::println("[OS] IO subsystem initialized."); + kapi::cpu::init(); + kernel::cpu::init(); + kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); kstd::println("[OS] Memory subsystem initialized."); -- cgit v1.2.3 From 3888a111be2e6b2a53b10752de36d58f18fc8874 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 17:39:33 +0100 Subject: x86_64: remove stale source code --- .../interrupt_descriptor_table/gate_descriptor.hpp | 69 -------------- .../interrupt_descriptor_table/idt_flags.hpp | 81 ---------------- .../interrupt_descriptor_table.hpp | 24 ----- .../interrupt_descriptor_table_pointer.hpp | 40 -------- .../interrupt_descriptor_table/ist_offset.hpp | 45 --------- .../segment_selector.hpp | 105 --------------------- .../generic_interrupt_handler.hpp | 34 ------- arch/x86_64/pre/include/arch/kernel/cpu/call.hpp | 30 ------ arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp | 27 ------ arch/x86_64/pre/include/arch/kernel/cpu/if.hpp | 21 ----- arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp | 64 ------------- arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp | 24 ----- .../interrupt_descriptor_table/gate_descriptor.cpp | 24 ----- .../interrupt_descriptor_table/idt_flags.cpp | 20 ---- .../interrupt_descriptor_table.cpp | 53 ----------- .../interrupt_descriptor_table_pointer.cpp | 13 --- .../interrupt_descriptor_table/ist_offset.cpp | 10 -- .../segment_selector.cpp | 24 ----- .../generic_interrupt_handler.cpp | 13 --- arch/x86_64/pre/src/kernel/cpu/call.cpp | 9 -- arch/x86_64/pre/src/kernel/cpu/idtr.cpp | 18 ---- arch/x86_64/pre/src/kernel/cpu/if.cpp | 13 --- arch/x86_64/pre/src/kernel/cpu/tr.cpp | 16 ---- 23 files changed, 777 deletions(-) delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp delete mode 100644 arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/call.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/if.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp delete mode 100644 arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/call.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/idtr.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/if.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/tr.cpp diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp deleted file mode 100644 index 07110c8..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP - -#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp" -#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp" -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - __extension__ typedef __int128 int128_t; - __extension__ typedef unsigned __int128 uint128_t; - - /** - * @brief Defines helper function for all states and the actual data the gate descriptor can have. - */ - struct [[gnu::packed]] gate_descriptor - { - /** - * @brief Default Constructor. - */ - gate_descriptor() = default; - - /** - * @brief Constructor. - * - * @note Created gate descriptor copies the given bytes into these components ending with a 32 bit reserved - * field that has to be used, because the 64-bit gate descriptor needs to be big enough for two 32-bit gate - * descriptor. - * - 16 bit Segment Selector - * - 3 bit Interrupt Stack Table Offset - * - 8 bit Type and Flags - * - 64 bit Offset - * - * @param flags Copies the bits set from the given data into the individual components of a gate - * descriptor. - */ - explicit gate_descriptor(uint128_t flags); - - /** - * @brief Constructor. - * - * @param selector, ist, flags, offset Copies the bits set from the given data into the individual components of - * a gate descriptor. - */ - gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset); - - /** - * @brief Allows to compare the underlying bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(gate_descriptor const & other) const -> bool = default; - - private: - // The order in private variables starts for the first variable being the rightmost bit. - uint16_t _offset_1 = {}; ///< Lower 16 bits of handler function address (0 - 15) - segment_selector _selector = {}; ///< Segment selector (16 - 31) - ist_offset _ist = {}; ///< Interrupt Stack Table offset (32 - 39) - idt_flags _flags = {}; ///< Gate Type and Flags (40 - 47) - uint64_t _offset_2 : 48 = {}; ///< Upper 48 bits of handler function address (48 - 95) - uint32_t : 32; ///< Reserved field used to ensure this struct is 128 bits big (96 - 127) - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp deleted file mode 100644 index 5104c36..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp +++ /dev/null @@ -1,81 +0,0 @@ - -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Defines helper function for all states that the access byte field of a segment descriptor can - * have. - */ - struct [[gnu::packed]] idt_flags - { - /** - * @brief Possible set bits in our underlying bits and the meaning when they are set. - */ - enum bitset : uint8_t - { - INTERRUPT_GATE = 0b01110, ///< The actual type of gate segment is a interrupt gate. - TRAP_GATE = 0b01111, ///< The actual type of gate segment is a trap gate. - DESCRIPTOR_LEVEL_KERNEL = - 0U << 5U, ///< Highest privileged level used by the kernel to allow for full access of resources. - DESCRIPTOR_LEVEL_ADMIN = - 1U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - DESCRIPTOR_LEVEL_PRIVILEGED_USER = - 2U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - DESCRIPTOR_LEVEL_USER = 3U << 5U, ///< Restricts access to only application and their specific memory. - PRESENT = 1U << 7U, ///< Present bit; Allows an entry to refer to a valid segment. - ///< Must be set (1) for any valid segment. - }; - - /** - * @brief Default Constructor. - */ - idt_flags() = default; - - /** - * @brief Constructor. - * - * @param flags Allows to set flags for the access byte field using the unscoped enum contained in this class, used - * to allow for direct integer conversion. This value is saved and can later be used to check whether certain flags - * are enabled or not using contains_flags method. - */ - idt_flags(uint8_t flags); - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data. - * - * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng - * data. Any additional bits that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying data has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying data. - */ - auto contains_flags(std::bitset<8U> other) const -> bool; - - /** - * @brief Allows to compare the underlying bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(idt_flags const & other) const -> bool = default; - - /** - * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data. - * - * @param other Additional bits that should be set. - */ - auto operator|=(std::bitset<8U> other) -> void; - - private: - uint8_t _flags = {}; ///< Underlying bits used to read the flags from. - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp deleted file mode 100644 index b388e0e..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP - -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Updates the IDTR with the created interrupt descriptor table. If it has not been created yet this - * method will create it. - */ - auto update_interrupt_descriptor_table_register() -> void; - - /** - * @brief Creates the interrupt descriptor table, with the minimum required configuration. If this method is called - * more than once, the previously created instance is returned instead. - * - * @return Reference to the created interrupt_descriptor_table. - */ - auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &; - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp deleted file mode 100644 index 7fe933b..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP - -#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp" -#include "arch/stl/vector.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - using interrupt_descriptor_table = stl::vector; - - /** - * @brief Represents a pointer to the Interrupt Descriptor Table (IDT). - * - * This structure is used to store the base address and length of the IDT. - */ - struct [[gnu::packed]] interrupt_descriptor_table_pointer - { - /** - * @brief Default constructor. - */ - interrupt_descriptor_table_pointer() = default; - - /** - * @brief Constructor. - */ - interrupt_descriptor_table_pointer(uint16_t table_length, gate_descriptor * address); - - /** - * @brief Defaulted three-way comparsion operator. - */ - auto operator<=>(interrupt_descriptor_table_pointer const & other) const -> std::strong_ordering = default; - - private: - uint16_t table_length = {}; ///< The amount of segment descriptor entries in the global descriptor table - 1. - gate_descriptor * address = {}; ///< Non-owning pointer to the IDT base address. - }; - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp deleted file mode 100644 index e45bcf4..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Defines helper function for all states that the interrupt stack table offset field of a gate descriptor can - * have. Is automatically increased to one byte in size, to include the following 5 reserved bits in the gate - * descriptor. - */ - struct [[gnu::packed]] ist_offset - { - /** - * @brief Default Constructor. - */ - ist_offset() = default; - - /** - * @brief Constructor. - * - * @param offset Offset into the interrupt stack table. A value of of 0 means we do not switch stacks, whereas 1 - 7 - * mean we switch to the n-th stack in the Interrupt Stack Table, contained in the TSS if the gate descriptor that - * contains this field is called. - */ - ist_offset(uint8_t offset); - - /** - * @brief Allows to compare the underlying set bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(ist_offset const & other) const -> bool = default; - - private: - uint8_t _ist : 3 = {}; ///< Offset into the interrupt stack table. A value of of 0 means we do not switch stacks, - ///< whereas 1 - 7 mean we switch to the n-th stack in the Interrupt Stack Table, contained - ///< in the TSS if the gate descriptor that contains this field is called. - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp deleted file mode 100644 index ea8c145..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Represents a segment selector in the x86_64 architecture, which points to a valid code segment in the global - * descriptor table. - * - * A segment selector is a 16-bit identifier used to select a segment descriptor - * from the Global Descriptor Table (GDT) or the Local Descriptor Table (LDT). - * It contains an index, a table indicator (TI), and a requested privilege level (RPL). - */ - struct [[gnu::packed]] segment_selector - { - /** - * @brief Possible set bits in our underlying bits and the meaning when they are set. - */ - enum bitset : uint8_t - { - REQUEST_LEVEL_KERNEL = - 0U << 0U, ///< Highest privileged level used by the kernel to allow for full access of resources. - REQUEST_LEVEL_ADMIN = - 1U << 0U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - REQUEST_LEVEL_PRIVILEGED_USER = - 2U << 0U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - REQUEST_LEVEL_USER = 3U << 0U, ///< Restricts access to only application and their specific memory. - LOCAL_DESCRIPTOR_TABLE = 1U << 2U, ///< Wheter the index referes to an entry in the local or global descriptor - ///< table. If enabled the index points to a local descriptor table, if it is - ///< cleared it referes to a global descriptor table instead. - }; - - /** - * @brief Default constructor. - */ - segment_selector() = default; - - /** - * @brief Constructor. - * - * @param index Index into the local or global descriptor table. Processor multiplies the index value by 8 (number - * of bytes in 32-bit segment descriptor) and adds the result to the base GDT or LDT address. - * @param flags Allows to set flags for the flags field using the unscoped enum contained in this class, used to - * allow for direct integer conversion. - */ - constexpr segment_selector(uint16_t index, uint8_t flags) - : _flags(flags) - , _index(index) - { - // Nothing to do. - } - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data. - * - * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng - * data. Any additional bits that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying data has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying data. - */ - auto contains_flags(std::bitset<3U> other) const -> bool; - - /** - * @brief Gets the index into the global descriptor table or the local descriptor table this segment selector is - * pointing too. - * - * @return Underlying value of the index field, bit 3 - 16. - */ - [[gnu::section(".user_text")]] - auto get_index() const -> uint16_t; - - /** - * @brief Defaulted three-way comparsion operator. - */ - auto operator<=>(segment_selector const & other) const -> std::strong_ordering = default; - - /** - * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data. - * - * @param other Additional bits that should be set. - */ - auto operator|=(std::bitset<3U> other) -> void; - - /** - * @brief Cast the underlying data into a combined 16-bit form, that contains all data. - * - * @return Underlying value combined into it's full size. - */ - operator uint16_t() const; - - private: - uint8_t _flags : 3 = {}; ///< Underlying bits used to read the flags from. - uint16_t _index - : 13 = {}; ///< Index into the local or global descriptor table. Processor multiplies the index value by 16 - ///< (number of bytes in segment descriptor) and adds the result to the base address. - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP diff --git a/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp b/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp deleted file mode 100644 index 15b35c1..0000000 --- a/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP -#define TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP - -#include - -namespace teachos::arch::interrupt_handling -{ - /** - * @brief Represents the CPU state during an interrupt. - * - * Some interrupts push an error code, while others do not. The full list - * of which vector number contains the error code can be found here: https://wiki.osdev.org/Exceptions - */ - struct [[gnu::packed]] interrupt_frame - { - // uint64_t error_code; ///< Error code only pushed by some exceptions, therefore it is commented out. - uint64_t ip; ///< Instruction pointer at the time of the interrupt. - uint64_t cs; ///< Code segment selector indicating privilege level. - uint64_t flags; ///< CPU flags (RFLAGS) storing processor state. - uint64_t sp; ///< Stack pointer at the time of the interrupt. - uint64_t ss; ///< Stack segment selector, usually unused in 64-bit mode. - }; - - /** - * @brief Generic interrupt handler function. - * - * @param frame Pointer to the interrupt frame containing CPU state. - */ - [[gnu::interrupt]] - auto generic_interrupt_handler(interrupt_frame * frame) -> void; - -} // namespace teachos::arch::interrupt_handling - -#endif // TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp deleted file mode 100644 index 3c43304..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP - -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Far Pointer. Address to function located in another code segment. - */ - struct far_pointer - { - void (*function)(); ///< Address of the function we want to call. (0-63) - context_switching::interrupt_descriptor_table::segment_selector - selector; ///< Segment selector pointing to the GDT entry we want to load into register CS. (64-79) - }; - - /** - * @brief Far call - A call to an instruction located in a different segment than the current code segment but at the - * same privilege level. - * - * @param pointer 64-bit operand size far pointer that we want to call. - */ - auto call(far_pointer pointer) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp deleted file mode 100644 index cb800d0..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP - -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Returns the value in the IDTR register. - * - * @return Value of IDTR register. - */ - auto store_interrupt_descriptor_table() - -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer; - - /** - * @brief Loads the interrupt_descriptor_table_pointer into the interrupt descriptor table register (IDTR). - */ - auto load_interrupt_descriptor_table( - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp deleted file mode 100644 index 48707dc..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Sets the interrupt flag (IF) in the EFLAGS register. - * This allows the processor to respond to maskable hardware interrupts. - */ - auto set_interrupt_flag() -> void; - - /** - * @brief Clears the interrupt flag (IF) in the EFLAGS register. - * This will stop the processor to respond to maskable hardware interrupts and needs to be done before changing the - * Interrupt Descriptor Table with lidt. - */ - auto clear_interrupt_flag() -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp deleted file mode 100644 index 99d6378..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Important flags that can be writen into the Extended Feature Enable Register (EFER). - * - * @note EFER is a model-specific register allowing to configure CPU extensions. Only the most important extensions - * are listed below, the rest are excluded for brevity. See https://en.wikipedia.org/wiki/Control_register#EFER for - * more information. - */ - enum class efer_flags : uint64_t - { - SCE = 1UL << 0UL, ///< System Call Extensions. - LME = 1UL << 8UL, ///< Long Mode Enabled. - LMA = 1UL << 10UL, ///< Long Mode Active. - NXE = 1UL << 11UL, ///< No-Execute Enable. - SVME = 1UL << 12UL, ///< Secure Virtual Machine Enable. - LMSLE = 1UL << 13UL, ///< Long Mode Segment Limit Enable. - FFXSR = 1UL << 14UL, ///< Fast FXSAVE/FXSTOR. - TCE = 1UL << 15UL, ///< Translation Cache Extension. - }; - - /** - * @brief Reads a 64-bit from the Model-Specific Register (MSR). - * - * @note This function reads the value of an MSR specified by the given address. It combines the lower and upper - * 32-bits of the MSR value read using the 'rdmsr' instruction and returns it as a 64-bit unsigned integer. - * - * @param msr The address of the MSR to read. - * @return The 64-bit value read from the MSR. - */ - auto read_msr(uint32_t msr) -> uint64_t; - - /** - * @brief Writes a 64-bit value to a Model-Specific Register (MSR). - * - * @note This function writes a 64-bit value to the MSR specified by the given address. - * It splits the 64-bit value into two 32-bit parts and writes them using the - * `wrmsr` instruction. - * - * @param msr The address of the MSR to write to. - * @param new_value The 64-bit value to write to the MSR. - */ - auto write_msr(uint32_t msr, uint64_t new_value) -> void; - - /** - * @brief Sets a specific bit in the Extended Feature Enable Register (EFER), which is a Model-Specific Register - * (MSR). - * - * @note This function reads the current value of the EFER register, ORs the specified - * bit with the current value, and writes the updated value back to the EFER register. - * - * @param flag The flag to set in the EFER register. - */ - auto set_efer_bit(efer_flags flag) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp deleted file mode 100644 index 7c856f1..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - - /** - * @brief Returns the value in the LTR register. - * - * @return Value of LTR register. - */ - auto store_task_register() -> uint16_t; - - /** - * @brief Loads the gdt offset to the tss segment descriptor into the task register (TR). - */ - auto load_task_register(uint16_t gdt_offset) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp deleted file mode 100644 index 28f289c..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - gate_descriptor::gate_descriptor(uint128_t flags) - : _offset_1(flags) - , _selector(flags >> 19U, flags >> 16U) - , _ist(flags >> 32U) - , _flags(flags >> 40U) - , _offset_2(flags >> 48U) - { - // Nothing to do. - } - - gate_descriptor::gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset) - : _offset_1(offset) - , _selector(selector) - , _ist(ist) - , _flags(flags) - , _offset_2(offset >> 16U) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp deleted file mode 100644 index f3b9d5e..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - idt_flags::idt_flags(uint8_t flags) - : _flags(flags) - { - // Nothing to do. - } - - auto idt_flags::contains_flags(std::bitset<8U> other) const -> bool - { - return (std::bitset<8U>{_flags} & other) == other; - } - - auto idt_flags::operator|=(std::bitset<8U> other) -> void - { - _flags |= other.to_ulong(); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp deleted file mode 100644 index 8640385..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp" - -#include "arch/exception_handling/assert.hpp" -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - namespace - { - /// @brief Amount of currently reserved interrupt indicies. - /// See https://wiki.osdev.org/Interrupt_Descriptor_Table#IDT_items for more information. - constexpr uint16_t RESERVED_INTERRUPT_COUNT = 256U; - - auto create_interrupt_descriptor_table() -> interrupt_descriptor_table - { - interrupt_descriptor_table interrupt_descriptor_table{RESERVED_INTERRUPT_COUNT}; - - uint64_t offset = reinterpret_cast(interrupt_handling::generic_interrupt_handler); - segment_selector selector{1U, segment_selector::REQUEST_LEVEL_KERNEL}; - ist_offset ist{0U}; - idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::INTERRUPT_GATE | idt_flags::PRESENT}; - - for (std::size_t i = 0; i < interrupt_descriptor_table.size(); i++) - { - interrupt_descriptor_table.at(i) = {selector, ist, flags, offset}; - } - - return interrupt_descriptor_table; - } - } // namespace - - auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table & - { - // Interrupt Descriptor Table needs to be kept alive - interrupt_descriptor_table static idt = create_interrupt_descriptor_table(); - return idt; - } - - auto update_interrupt_descriptor_table_register() -> void - { - decltype(auto) idt = get_or_create_interrupt_descriptor_table(); - - interrupt_descriptor_table_pointer idt_pointer{static_cast((idt.size() * sizeof(gate_descriptor)) - 1), - idt.data()}; - kernel::cpu::load_interrupt_descriptor_table(idt_pointer); - - auto const stored_gdt_pointer = kernel::cpu::store_interrupt_descriptor_table(); - arch::exception_handling::assert( - idt_pointer == stored_gdt_pointer, - "[Interrupt Descriptor Table] Loaded IDTR value is not the same as the stored value."); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp deleted file mode 100644 index 7bcbae6..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - interrupt_descriptor_table_pointer::interrupt_descriptor_table_pointer(uint16_t table_length, - gate_descriptor * address) - : table_length(table_length) - , address(address) - { - // Nothing to do. - } - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp deleted file mode 100644 index a70e75d..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - ist_offset::ist_offset(uint8_t index) - : _ist(index) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp deleted file mode 100644 index 25ba859..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - auto segment_selector::contains_flags(std::bitset<3U> other) const -> bool - { - return (std::bitset<3U>{_flags} & other) == other; - } - - auto segment_selector::get_index() const -> uint16_t - { - return _index; - } - - auto segment_selector::operator|=(std::bitset<3U> other) -> void - { - _flags |= other.to_ulong(); - } - - segment_selector::operator uint16_t() const - { - return *reinterpret_cast(this); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp deleted file mode 100644 index 9d061a8..0000000 --- a/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" - -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::interrupt_handling -{ - auto generic_interrupt_handler(interrupt_frame * frame) -> void - { - (void)frame; - video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - } -} // namespace teachos::arch::interrupt_handling diff --git a/arch/x86_64/pre/src/kernel/cpu/call.cpp b/arch/x86_64/pre/src/kernel/cpu/call.cpp deleted file mode 100644 index 98fa248..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/call.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "arch/kernel/cpu/call.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto call(far_pointer pointer) -> void - { - asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/idtr.cpp b/arch/x86_64/pre/src/kernel/cpu/idtr.cpp deleted file mode 100644 index 7aa20c1..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/idtr.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_interrupt_descriptor_table() - -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer - { - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer current_value{}; - asm("sidt %[output]" : [output] "=m"(current_value)); - return current_value; - } - - auto load_interrupt_descriptor_table( - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void - { - asm volatile("lidt %[input]" : /* no output from call */ : [input] "m"(idt_pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/if.cpp b/arch/x86_64/pre/src/kernel/cpu/if.cpp deleted file mode 100644 index 5d056fc..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/if.cpp +++ /dev/null @@ -1,13 +0,0 @@ -namespace teachos::arch::kernel::cpu -{ - auto set_interrupt_flag() -> void - { - asm volatile("sti"); - } - - auto clear_interrupt_flag() -> void - { - asm volatile("cli"); - } - -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/tr.cpp b/arch/x86_64/pre/src/kernel/cpu/tr.cpp deleted file mode 100644 index a435540..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/tr.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/kernel/cpu/tr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_task_register() -> uint16_t - { - uint16_t current_value{}; - asm("str %[output]" : [output] "=r"(current_value)); - return current_value; - } - - auto load_task_register(uint16_t gdt_offset) -> void - { - asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset)); - } -} // namespace teachos::arch::kernel::cpu -- cgit v1.2.3 From 42895684b631380c8aca94f82209297ac0c0e5f2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 17:44:21 +0100 Subject: kapi: extract interrupt enablement --- arch/x86_64/include/arch/cpu/interrupts.hpp | 3 --- arch/x86_64/kapi/cpu.cpp | 12 ++++++++++-- arch/x86_64/src/cpu/interrupts.cpp | 10 ---------- kapi/include/kapi/cpu.hpp | 3 ++- kapi/include/kapi/cpu/interrupts.hpp | 19 +++++++++++++++++++ kernel/src/main.cpp | 1 + 6 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 kapi/include/kapi/cpu/interrupts.hpp diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 08ecd9c..b9adb6e 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -111,9 +111,6 @@ namespace arch::cpu auto static read() -> interrupt_descriptor_table_register; }; - auto enable_interrupts() -> void; - auto disable_interrupts() -> void; - } // namespace arch::cpu #endif \ No newline at end of file diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 693d328..8ca3847 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -3,7 +3,6 @@ #include "kapi/system.hpp" #include "arch/cpu/initialization.hpp" -#include "arch/cpu/interrupts.hpp" #include @@ -21,7 +20,6 @@ namespace kapi::cpu arch::cpu::initialize_descriptors(); arch::cpu::initialize_legacy_interrupts(); - arch::cpu::enable_interrupts(); } auto halt() -> void @@ -30,4 +28,14 @@ namespace kapi::cpu __builtin_unreachable(); } + auto enable_interrupts() -> void + { + asm volatile("sti"); + } + + auto disable_interrupts() -> void + { + asm volatile("cli"); + } + } // namespace kapi::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 6f66bbd..dc236e6 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -168,14 +168,4 @@ namespace arch::cpu return idtr; } - auto enable_interrupts() -> void - { - asm volatile("sti"); - } - - auto disable_interrupts() -> void - { - asm volatile("cli"); - } - } // namespace arch::cpu \ No newline at end of file diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 1dd7cfe..ade954c 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,7 +1,8 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/cpu/exception.hpp" +#include "kapi/cpu/exception.hpp" // IWYU pragma: export +#include "kapi/cpu/interrupts.hpp" // IWYU pragma: export namespace kapi::cpu { diff --git a/kapi/include/kapi/cpu/interrupts.hpp b/kapi/include/kapi/cpu/interrupts.hpp new file mode 100644 index 0000000..26a215e --- /dev/null +++ b/kapi/include/kapi/cpu/interrupts.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KAPI_CPU_INTERRUPTS_HPP +#define TEACHOS_KAPI_CPU_INTERRUPTS_HPP + +// IWYU pragma: private, include "kapi/cpu.hpp" + +namespace kapi::cpu +{ + + //! @qualifier platform-defined + //! Enable external interrupts. + auto enable_interrupts() -> void; + + //! @qualifier platform-defined + //! Disable external interrupts. + auto disable_interrupts() -> void; + +} // namespace kapi::cpu + +#endif \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 0416ee9..6bd168c 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -94,6 +94,7 @@ auto main() -> int kapi::cpu::init(); kernel::cpu::init(); + kapi::cpu::enable_interrupts(); kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); -- cgit v1.2.3 From 9b879b06e202a41cdecc25e08ed5e69c57814141 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 07:44:57 +0100 Subject: kapi: add missing header to build --- kapi/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index b239adb..4c94829 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -8,6 +8,9 @@ target_sources("kapi" PUBLIC "include/kapi/boot_modules.hpp" "include/kapi/boot.hpp" "include/kapi/cio.hpp" + "include/kapi/cpu.hpp" + "include/kapi/cpu/interrupts.hpp" + "include/kapi/cpu/exception.hpp" "include/kapi/boot_module/boot_module.hpp" "include/kapi/boot_module/boot_module_registry.hpp" "include/kapi/memory.hpp" -- cgit v1.2.3 From fd6ac1cbfbe90fa807dca60657bb80ed43c78aee Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 07:45:08 +0100 Subject: kapi/cpu: improve documentation --- kapi/include/kapi/cpu/exception.hpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 09e15a7..6d39175 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -10,28 +10,40 @@ namespace kapi::cpu { + //! An exception originating from the CPU directly. + //! + //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not + //! originate from external hardware, but rather are a product of program execution. struct exception { + //! The type of the exception, which identifies the reason for it being raised. enum class type : std::uint8_t { + //! The reason for the exception is unknown or platform-specific unknown, + //! A page fault occurred page_fault, + //! A memory access (either in the data or instruction stream) violated it's alignment constraints. alignment_fault, + //! A memory access (either in the data or instruction stream) violated it's permissions. memory_access_fault, - illegal_instruction, + //! The precoditions for the execution of an instruction were not met. privilege_violation, + //! An arithmetic error occurred. arithmetic_error, + //! A breakpoint was hit in the instruction stream. breakpoint, + //! The CPU is single-stepping through the instruction stream. single_step, }; - //! The type of exception. + //! The type of this exception. type type{}; - //! The instruction pointer at the time of the exception. + //! The value of the instruction pointer at the time this exception was raised. kapi::memory::linear_address instruction_pointer{}; - //! The memory address that caused the exception. + //! The memory address that caused this exception. kapi::memory::linear_address access_address{}; //! Whether the page was present at the time of the exception. @@ -44,15 +56,26 @@ namespace kapi::cpu bool is_user_mode{}; }; + //! The abstract interface for exception handlers. + //! + //! The kernel must define an exception handler to be used during execution. struct exception_handler { virtual ~exception_handler() = default; + //! Handle an exception. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. virtual auto handle(exception const & context) -> bool = 0; }; + //! @qualifier kernel-defined + //! Get the currently active exception handler. auto get_exception_handler() -> exception_handler &; + //! @qualifier kernel-defined + //! Set the exception handler. auto set_exception_handler(exception_handler & handler) -> void; } // namespace kapi::cpu -- cgit v1.2.3 From 2f8c5ca6d5ab6131a148502e1d1be4ce2a65b339 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 07:47:04 +0100 Subject: kapi/cpu: add missing exception type --- kapi/include/kapi/cpu/exception.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 6d39175..9fc697a 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -27,6 +27,8 @@ namespace kapi::cpu alignment_fault, //! A memory access (either in the data or instruction stream) violated it's permissions. memory_access_fault, + //! An invalid instruction was present in the instruction stream. + illegal_instruction, //! The precoditions for the execution of an instruction were not met. privilege_violation, //! An arithmetic error occurred. -- cgit v1.2.3 From 363a6d701d4998137fcc123059f9749098ac7d75 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 08:12:49 +0100 Subject: x86_64/cpu: fix exception enum --- arch/x86_64/src/cpu/interrupts.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index dc236e6..e578aa2 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -28,15 +28,17 @@ namespace arch::cpu double_fault, coprocessor_segment_overrun, invalid_tss, + segment_not_present, stack_segment_fault, general_protection_fault, page_fault, - x87_fpu_floating_point_error, + x87_fpu_floating_point_error = 16, alignment_check, machine_check, simd_floating_point_error, virtualization_exception, control_protection_exception, + hypervisor_injection_exception = 28, vmm_communication_exception, security_exception, }; @@ -62,6 +64,8 @@ namespace arch::cpu return kapi::cpu::exception::type::memory_access_fault; case exception::general_protection_fault: return kapi::cpu::exception::type::privilege_violation; + case exception::page_fault: + return kapi::cpu::exception::type::page_fault; case exception::alignment_check: return kapi::cpu::exception::type::alignment_fault; default: -- cgit v1.2.3 From fd1c5a50bb35f772b8e37125188640447d4b3b2a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 13:24:03 +0100 Subject: kapi/cpu: enable formatting of exception types --- kapi/include/kapi/cpu/exception.hpp | 36 ++++++++++++++++++++++++++++++++++++ kernel/src/cpu.cpp | 3 +-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 9fc697a..00b02e7 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -5,6 +5,8 @@ #include "kapi/memory.hpp" +#include + #include namespace kapi::cpu @@ -82,4 +84,38 @@ namespace kapi::cpu } // namespace kapi::cpu +template<> +struct kstd::formatter +{ + constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void + { + switch (type) + { + case kapi::cpu::exception::type::unknown: + return ctx.push("unknown"); + case kapi::cpu::exception::type::page_fault: + return ctx.push("page fault"); + case kapi::cpu::exception::type::alignment_fault: + return ctx.push("alignment fault"); + case kapi::cpu::exception::type::memory_access_fault: + return ctx.push("memory access fault"); + case kapi::cpu::exception::type::illegal_instruction: + return ctx.push("illegal instruction"); + case kapi::cpu::exception::type::privilege_violation: + return ctx.push("privilege violation"); + case kapi::cpu::exception::type::arithmetic_error: + return ctx.push("arithmetic error"); + case kapi::cpu::exception::type::breakpoint: + return ctx.push("breakpoint"); + case kapi::cpu::exception::type::single_step: + return ctx.push("single step"); + } + } +}; + #endif \ No newline at end of file diff --git a/kernel/src/cpu.cpp b/kernel/src/cpu.cpp index fc460c9..11b6551 100644 --- a/kernel/src/cpu.cpp +++ b/kernel/src/cpu.cpp @@ -14,6 +14,7 @@ namespace kernel::cpu { auto handle(kapi::cpu::exception const & context) -> bool override { + kstd::println(kstd::print_sink::stderr, "[OS:CPU] {} @ {:#018x}", context.type, context.instruction_pointer); switch (context.type) { case kapi::cpu::exception::type::page_fault: @@ -26,12 +27,10 @@ namespace kernel::cpu private: auto handle_page_fault(kapi::cpu::exception const & context) -> bool { - kstd::println(kstd::print_sink::stderr, "[OS:CPU] PAGE FAULT!"); kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); - kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", context.instruction_pointer); kapi::system::panic("Halting the system due to an unrecoverable page fault."); } -- cgit v1.2.3 From a82416648d148152338dc612c25bf8dff428e773 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 16:39:13 +0100 Subject: kapi: introduce cpu::interrupt_handler --- arch/x86_64/kapi/cpu.cpp | 49 ++++++++++++++++++++++++++++++++++++ arch/x86_64/src/cpu/interrupts.cpp | 17 +++---------- kapi/include/kapi/cpu/interrupts.hpp | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 13 deletions(-) diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 8ca3847..b19ba21 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -4,11 +4,22 @@ #include "arch/cpu/initialization.hpp" +#include +#include + +#include #include +#include namespace kapi::cpu { + namespace + { + constexpr auto irq_offset = 32uz; + auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; + } // namespace + auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -38,4 +49,42 @@ namespace kapi::cpu asm volatile("cli"); } + auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + interrupt_handlers[irq_number - irq_offset].push_back(&handler); + } + + auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + kstd::println("[x86_64:CPU] TODO: support erasure from vector."); + } + + auto dispatch_interrupt(std::uint32_t irq_number) -> status + { + if (irq_number < irq_offset) + { + return status::unhandled; + } + + for (auto handler : interrupt_handlers[irq_number - irq_offset]) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + } // namespace kapi::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index e578aa2..1f65336 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -46,7 +46,6 @@ namespace arch::cpu constexpr auto pic_master_irq_start = 0x20; constexpr auto pic_master_irq_end = pic_master_irq_start + 8; constexpr auto pic_slave_irq_start = pic_master_irq_end; - constexpr auto pic_slave_irq_end = pic_slave_irq_start + 8; constexpr auto to_exception_type(exception e) { @@ -96,10 +95,8 @@ namespace arch::cpu } } - auto handle_legacy_interrupt(interrupt_frame * frame) -> void + auto acknowledge_pic_interrupt(interrupt_frame * frame) -> void { - kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number); - if (frame->interrupt.number >= pic_slave_irq_start) { pic_slave_control_port::write(pic_end_of_interrupt); @@ -120,19 +117,13 @@ namespace arch::cpu { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); - kapi::cpu::halt(); } - - if ((number >= pic_master_irq_start && number < pic_master_irq_end) || - (number >= pic_slave_irq_start && number < pic_slave_irq_end)) + else if (number >= pic_master_irq_start && kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) { - handle_legacy_interrupt(frame); - return; + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} received with code {:#04x}", - frame->interrupt.number, frame->interrupt.error_code); - kapi::cpu::halt(); + acknowledge_pic_interrupt(frame); } } diff --git a/kapi/include/kapi/cpu/interrupts.hpp b/kapi/include/kapi/cpu/interrupts.hpp index 26a215e..328e4a7 100644 --- a/kapi/include/kapi/cpu/interrupts.hpp +++ b/kapi/include/kapi/cpu/interrupts.hpp @@ -3,9 +3,32 @@ // IWYU pragma: private, include "kapi/cpu.hpp" +#include + namespace kapi::cpu { + enum class status : bool + { + unhandled, + handled, + }; + + //! The interface for all interrupt handlers. + struct interrupt_handler + { + virtual ~interrupt_handler() = default; + + //! Handle an interrupt with the given IRQ number. + // + //! This function will be called by the kernel in an interrupt context. As such, the function should complete its + //! task quickly and must take care when acquiring globally shared locks. + //! + //! @param irq_number The identifier of the interrupt request that triggered the handler. + //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. + virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; + }; + //! @qualifier platform-defined //! Enable external interrupts. auto enable_interrupts() -> void; @@ -14,6 +37,27 @@ namespace kapi::cpu //! Disable external interrupts. auto disable_interrupts() -> void; + //! @qualifier platform-defined + //! Register an interrupt handler for the given IRQ number. + //! + //! @param irq_number The IRQ number to register the handler for. + //! @param handler The interrupt handler to register. + auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; + + //! @qualifier platform-defined + //! Unregister a previously registered interrupt handler. + //! + //! @param irq_number The IRQ number to unregister the handler for. + //! @param handler The interrupt handler to unregister. + auto unregister_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; + + //! @qualifier platform-defined + //! Dispatch an interrupt to all registered handlers. + //! + //! @param irq_number The IRQ number to dispatch. + //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. + auto dispatch_interrupt(std::uint32_t irq_number) -> status; + } // namespace kapi::cpu #endif \ No newline at end of file -- cgit v1.2.3 From 953768ed7af8692818f742566864bfd264a824a2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 18:37:55 +0100 Subject: x86_64/cpu: fix interrupt gate types --- arch/x86_64/src/cpu/interrupts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 1f65336..1f12898 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -135,7 +135,7 @@ namespace arch::cpu .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) .m_code_segment = segment_selector{0, false, 1}, .interrupt_stack_table_selector = 0, - .gate_type = gate_type::interrupt_gate, + .gate_type = (i < 32 && i != 2) ? gate_type::trap_gate : gate_type::interrupt_gate, .descriptor_privilege_level = 0, .present = true, .offset_middle = -- cgit v1.2.3 From 6a1addc7663bfae3306abb8800d3e387b3f66e82 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 18:46:20 +0100 Subject: x86_64/cpu: improve exception logging --- arch/x86_64/src/cpu/interrupts.cpp | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 1f12898..4e52d71 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -43,6 +43,8 @@ namespace arch::cpu security_exception, }; + constexpr auto number_of_exception_vectors = 32u; + constexpr auto pic_master_irq_start = 0x20; constexpr auto pic_master_irq_end = pic_master_irq_start + 8; constexpr auto pic_slave_irq_start = pic_master_irq_end; @@ -72,6 +74,25 @@ namespace arch::cpu } } + constexpr auto has_error_code(exception e) + { + switch (e) + { + case exception::double_fault: + case exception::invalid_tss: + case exception::segment_not_present: + case exception::stack_segment_fault: + case exception::general_protection_fault: + case exception::page_fault: + case exception::alignment_check: + case exception::control_protection_exception: + case exception::security_exception: + return true; + default: + return false; + } + } + auto dispatch_exception(interrupt_frame * frame) -> bool { auto type = to_exception_type(static_cast(frame->interrupt.number)); @@ -113,12 +134,20 @@ namespace arch::cpu { auto [number, code] = frame->interrupt; - if (number < pic_master_irq_start && !dispatch_exception(frame)) + if (number < number_of_exception_vectors && !dispatch_exception(frame)) { - kstd::println(kstd::print_sink::stderr, - "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + if (has_error_code(static_cast(number))) + { + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + } } - else if (number >= pic_master_irq_start && kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) + else if (number >= number_of_exception_vectors && + kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } -- cgit v1.2.3 From 9e5788cc6bbc6291de6a6f542e4f2754e29dd75a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 14:30:05 +0100 Subject: ci: use preconfigured image --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05bba19..de9303d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,10 +6,7 @@ build: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 - before_script: - - apt update - - apt install -y clang-tidy cmake grub2-common grub-pc mtools ninja-build xorriso + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64.ci:latest script: - cmake --preset $PLATFORM - cmake --build --preset $PLATFORM-$TYPE -- cgit v1.2.3 From a8cc3a2ad00c7dbb756bf6bc4f02399c551bab07 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 14:35:37 +0100 Subject: ci: set artifact expiry time --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de9303d..7abc89e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,7 @@ build: - kernel.elf - kernel.sym - kernel.iso + expire_in: 1 week <<: *build_matrix bht: -- cgit v1.2.3 From 997f462b540cca32fd55aa7f40c6d4bfef79e367 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 14:39:14 +0100 Subject: ci: use preconfigured image for bht builds --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7abc89e..35af269 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,10 +22,7 @@ build: bht: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 - before_script: - - apt update - - apt install -y build-essential cmake ninja-build lcov libcatch2-dev + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/bht.ci:latest script: - cmake --preset bht - cmake --build --preset bht-dbg -- cgit v1.2.3 From 465d323cc89dc67d793148728d222621eea335c8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 15:03:52 +0100 Subject: ci: fix coverage regex --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35af269..b4296b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ bht: - ctest --preset bht-dbg - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - lcov --config-file .lcovrc --list coverage.info - coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' + coverage: '/Total:\|\s*(\d+(?:\.\d+)?)\%/' artifacts: paths: - coverage.info -- cgit v1.2.3 From 1cb599798a0b29302ab71d1ee0fb9febff8f6a75 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 15:24:10 +0100 Subject: kapi/cpu: update documentation of init() --- kapi/include/kapi/cpu.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index ade954c..712de16 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -14,6 +14,9 @@ namespace kapi::cpu //! @qualifier platform-defined //! Perform early CPU initialization. + //! + //! When this function returns, the CPU is in a state where interrupt could be enabled. This function must not enable + //! interrupts itself. auto init() -> void; } // namespace kapi::cpu -- cgit v1.2.3 From d56700342ea0266a6e49f9515eb83279f66b4fcf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 15:28:41 +0100 Subject: x86_64: split kapi::cpu implementation --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/kapi/cpu.cpp | 59 -------------------------------- arch/x86_64/kapi/cpu/interrupts.cpp | 67 +++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 59 deletions(-) create mode 100644 arch/x86_64/kapi/cpu/interrupts.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 89d9bc0..8ff81d8 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources("x86_64" PRIVATE "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/cpu/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index b19ba21..12edb0f 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -4,22 +4,11 @@ #include "arch/cpu/initialization.hpp" -#include -#include - -#include #include -#include namespace kapi::cpu { - namespace - { - constexpr auto irq_offset = 32uz; - auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; - } // namespace - auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -39,52 +28,4 @@ namespace kapi::cpu __builtin_unreachable(); } - auto enable_interrupts() -> void - { - asm volatile("sti"); - } - - auto disable_interrupts() -> void - { - asm volatile("cli"); - } - - auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - interrupt_handlers[irq_number - irq_offset].push_back(&handler); - } - - auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - kstd::println("[x86_64:CPU] TODO: support erasure from vector."); - } - - auto dispatch_interrupt(std::uint32_t irq_number) -> status - { - if (irq_number < irq_offset) - { - return status::unhandled; - } - - for (auto handler : interrupt_handlers[irq_number - irq_offset]) - { - if (handler && handler->handle_interrupt(irq_number) == status::handled) - { - return status::handled; - } - } - - return status::unhandled; - } - } // namespace kapi::cpu diff --git a/arch/x86_64/kapi/cpu/interrupts.cpp b/arch/x86_64/kapi/cpu/interrupts.cpp new file mode 100644 index 0000000..b98595c --- /dev/null +++ b/arch/x86_64/kapi/cpu/interrupts.cpp @@ -0,0 +1,67 @@ +#include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include +#include + +#include +#include + +namespace kapi::cpu +{ + + namespace + { + constexpr auto irq_offset = 32uz; + auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; + } // namespace + + auto enable_interrupts() -> void + { + asm volatile("sti"); + } + + auto disable_interrupts() -> void + { + asm volatile("cli"); + } + + auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + interrupt_handlers[irq_number - irq_offset].push_back(&handler); + } + + auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + kstd::println("[x86_64:CPU] TODO: support erasure from vector."); + } + + auto dispatch_interrupt(std::uint32_t irq_number) -> status + { + if (irq_number < irq_offset) + { + return status::unhandled; + } + + for (auto handler : interrupt_handlers[irq_number - irq_offset]) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + +} // namespace kapi::cpu \ No newline at end of file -- cgit v1.2.3 From 8d06763f47e7b7c93af2a55f6bd2dbc4aa9abfa2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:10:50 +0100 Subject: kapi/cpu: simplify exception handling --- arch/x86_64/src/cpu/interrupts.cpp | 5 ++--- kapi/include/kapi/cpu/exception.hpp | 27 +++++----------------- kernel/CMakeLists.txt | 1 - kernel/include/kernel/cpu.hpp | 14 ------------ kernel/kapi/cpu.cpp | 35 ++++++++++++++++------------- kernel/src/cpu.cpp | 45 ------------------------------------- kernel/src/main.cpp | 2 -- 7 files changed, 28 insertions(+), 101 deletions(-) delete mode 100644 kernel/include/kernel/cpu.hpp delete mode 100644 kernel/src/cpu.cpp diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 4e52d71..9ee3ce8 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -108,11 +108,10 @@ namespace arch::cpu auto write = (frame->interrupt.error_code & 0x2) != 0; auto user = (frame->interrupt.error_code & 0x4) != 0; - return kapi::cpu::get_exception_handler().handle( - {type, instruction_pointer, fault_address, present, write, user}); + return kapi::cpu::dispatch({type, instruction_pointer, fault_address, present, write, user}); } default: - return kapi::cpu::get_exception_handler().handle({type, instruction_pointer}); + return kapi::cpu::dispatch({type, instruction_pointer}); } } diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 00b02e7..d6e8511 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -31,7 +31,7 @@ namespace kapi::cpu memory_access_fault, //! An invalid instruction was present in the instruction stream. illegal_instruction, - //! The precoditions for the execution of an instruction were not met. + //! The preconditions for the execution of an instruction were not met. privilege_violation, //! An arithmetic error occurred. arithmetic_error, @@ -60,27 +60,12 @@ namespace kapi::cpu bool is_user_mode{}; }; - //! The abstract interface for exception handlers. - //! - //! The kernel must define an exception handler to be used during execution. - struct exception_handler - { - virtual ~exception_handler() = default; - - //! Handle an exception. - //! - //! @param context The exception context. - //! @return Whether the exception was handled. - virtual auto handle(exception const & context) -> bool = 0; - }; - - //! @qualifier kernel-defined - //! Get the currently active exception handler. - auto get_exception_handler() -> exception_handler &; - //! @qualifier kernel-defined - //! Set the exception handler. - auto set_exception_handler(exception_handler & handler) -> void; + //! Dispatch an exception to the appropriate handler. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. + auto dispatch(exception const & context) -> bool; } // namespace kapi::cpu diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d9a5c75..9b1e2ad 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -11,7 +11,6 @@ add_executable("kernel" "kstd/print.cpp" # Kernel Implementation - "src/cpu.cpp" "src/main.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" diff --git a/kernel/include/kernel/cpu.hpp b/kernel/include/kernel/cpu.hpp deleted file mode 100644 index d4e1ced..0000000 --- a/kernel/include/kernel/cpu.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef TEACHOS_KERNEL_CPU_HPP -#define TEACHOS_KERNEL_CPU_HPP - -namespace kernel::cpu -{ - - //! Initialize the kernel heap. - auto init() -> void; - - //! - -} // namespace kernel::cpu - -#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp index 2089098..13de584 100644 --- a/kernel/kapi/cpu.cpp +++ b/kernel/kapi/cpu.cpp @@ -1,30 +1,35 @@ #include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include + namespace kapi::cpu { namespace { - struct null_exception_handler : exception_handler + auto handle_page_fault(kapi::cpu::exception const & context) -> bool { - auto handle(exception const &) -> bool override - { - return false; - } - } static constinit default_exception_handler; - - exception_handler * current_handler = &default_exception_handler; + kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); + kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); + kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); + kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); + kapi::system::panic("Halting the system due to an unrecoverable page fault."); + } } // namespace - auto get_exception_handler() -> exception_handler & + auto dispatch(exception const & context) -> bool { - return *current_handler; - } - - auto set_exception_handler(exception_handler & handler) -> void - { - current_handler = &handler; + kstd::println(kstd::print_sink::stderr, "[OS:CPU] {} @ {:#018x}", context.type, context.instruction_pointer); + switch (context.type) + { + case kapi::cpu::exception::type::page_fault: + return handle_page_fault(context); + default: + return false; + } } } // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/src/cpu.cpp b/kernel/src/cpu.cpp deleted file mode 100644 index 11b6551..0000000 --- a/kernel/src/cpu.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "kernel/cpu.hpp" - -#include "kapi/cpu.hpp" -#include "kapi/system.hpp" - -#include - -namespace kernel::cpu -{ - - namespace - { - struct exception_handler : kapi::cpu::exception_handler - { - auto handle(kapi::cpu::exception const & context) -> bool override - { - kstd::println(kstd::print_sink::stderr, "[OS:CPU] {} @ {:#018x}", context.type, context.instruction_pointer); - switch (context.type) - { - case kapi::cpu::exception::type::page_fault: - return handle_page_fault(context); - default: - return false; - } - } - - private: - auto handle_page_fault(kapi::cpu::exception const & context) -> bool - { - kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); - kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); - kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); - kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); - - kapi::system::panic("Halting the system due to an unrecoverable page fault."); - } - } static constinit handler; - } // namespace - - auto init() -> void - { - kapi::cpu::set_exception_handler(handler); - } - -} // namespace kernel::cpu \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 6bd168c..45a4aae 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,7 +4,6 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "kernel/cpu.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" @@ -93,7 +92,6 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::cpu::init(); - kernel::cpu::init(); kapi::cpu::enable_interrupts(); kapi::memory::init(); -- cgit v1.2.3 From 2521d58e7a5c16595e401e1af7becb572ad35f53 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:25:35 +0100 Subject: kapi: dissolve cpu/exception.hpp into cpu.hpp --- kapi/include/kapi/cpu.hpp | 96 +++++++++++++++++++++++++++++++- kapi/include/kapi/cpu/exception.hpp | 106 ------------------------------------ 2 files changed, 95 insertions(+), 107 deletions(-) delete mode 100644 kapi/include/kapi/cpu/exception.hpp diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 712de16..041a5db 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,11 +1,64 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/cpu/exception.hpp" // IWYU pragma: export #include "kapi/cpu/interrupts.hpp" // IWYU pragma: export +#include "kapi/memory.hpp" + +#include + +#include namespace kapi::cpu { + + //! An exception originating from the CPU directly. + //! + //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not + //! originate from external hardware, but rather are a product of program execution. + struct exception + { + //! The type of the exception, which identifies the reason for it being raised. + enum class type : std::uint8_t + { + //! The reason for the exception is unknown or platform-specific + unknown, + //! A page fault occurred + page_fault, + //! A memory access (either in the data or instruction stream) violated it's alignment constraints. + alignment_fault, + //! A memory access (either in the data or instruction stream) violated it's permissions. + memory_access_fault, + //! An invalid instruction was present in the instruction stream. + illegal_instruction, + //! The preconditions for the execution of an instruction were not met. + privilege_violation, + //! An arithmetic error occurred. + arithmetic_error, + //! A breakpoint was hit in the instruction stream. + breakpoint, + //! The CPU is single-stepping through the instruction stream. + single_step, + }; + + //! The type of this exception. + type type{}; + + //! The value of the instruction pointer at the time this exception was raised. + kapi::memory::linear_address instruction_pointer{}; + + //! The memory address that caused this exception. + kapi::memory::linear_address access_address{}; + + //! Whether the page was present at the time of the exception. + bool is_present{}; + + //! Whether the exception was caused by a write access. + bool is_write_access{}; + + //! Whether the exception was caused by a user mode access. + bool is_user_mode{}; + }; + //! @qualifier platform-defined //! Halt the CPU. //! @@ -19,6 +72,47 @@ namespace kapi::cpu //! interrupts itself. auto init() -> void; + //! @qualifier kernel-defined + //! Dispatch an exception to the appropriate handler. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. + [[nodiscard]] auto dispatch(exception const & context) -> bool; + } // namespace kapi::cpu +template<> +struct kstd::formatter +{ + constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void + { + switch (type) + { + case kapi::cpu::exception::type::unknown: + return ctx.push("unknown"); + case kapi::cpu::exception::type::page_fault: + return ctx.push("page fault"); + case kapi::cpu::exception::type::alignment_fault: + return ctx.push("alignment fault"); + case kapi::cpu::exception::type::memory_access_fault: + return ctx.push("memory access fault"); + case kapi::cpu::exception::type::illegal_instruction: + return ctx.push("illegal instruction"); + case kapi::cpu::exception::type::privilege_violation: + return ctx.push("privilege violation"); + case kapi::cpu::exception::type::arithmetic_error: + return ctx.push("arithmetic error"); + case kapi::cpu::exception::type::breakpoint: + return ctx.push("breakpoint"); + case kapi::cpu::exception::type::single_step: + return ctx.push("single step"); + } + } +}; + #endif diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp deleted file mode 100644 index d6e8511..0000000 --- a/kapi/include/kapi/cpu/exception.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef TEACHOS_KAPI_CPU_EXCEPTION_HPP -#define TEACHOS_KAPI_CPU_EXCEPTION_HPP - -// IWYU pragma: private, include "kapi/cpu.hpp" - -#include "kapi/memory.hpp" - -#include - -#include - -namespace kapi::cpu -{ - - //! An exception originating from the CPU directly. - //! - //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not - //! originate from external hardware, but rather are a product of program execution. - struct exception - { - //! The type of the exception, which identifies the reason for it being raised. - enum class type : std::uint8_t - { - //! The reason for the exception is unknown or platform-specific - unknown, - //! A page fault occurred - page_fault, - //! A memory access (either in the data or instruction stream) violated it's alignment constraints. - alignment_fault, - //! A memory access (either in the data or instruction stream) violated it's permissions. - memory_access_fault, - //! An invalid instruction was present in the instruction stream. - illegal_instruction, - //! The preconditions for the execution of an instruction were not met. - privilege_violation, - //! An arithmetic error occurred. - arithmetic_error, - //! A breakpoint was hit in the instruction stream. - breakpoint, - //! The CPU is single-stepping through the instruction stream. - single_step, - }; - - //! The type of this exception. - type type{}; - - //! The value of the instruction pointer at the time this exception was raised. - kapi::memory::linear_address instruction_pointer{}; - - //! The memory address that caused this exception. - kapi::memory::linear_address access_address{}; - - //! Whether the page was present at the time of the exception. - bool is_present{}; - - //! Whether the exception was caused by a write access. - bool is_write_access{}; - - //! Whether the exception was caused by a user mode access. - bool is_user_mode{}; - }; - - //! @qualifier kernel-defined - //! Dispatch an exception to the appropriate handler. - //! - //! @param context The exception context. - //! @return Whether the exception was handled. - auto dispatch(exception const & context) -> bool; - -} // namespace kapi::cpu - -template<> -struct kstd::formatter -{ - constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) - { - return ctx.begin(); - } - - constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void - { - switch (type) - { - case kapi::cpu::exception::type::unknown: - return ctx.push("unknown"); - case kapi::cpu::exception::type::page_fault: - return ctx.push("page fault"); - case kapi::cpu::exception::type::alignment_fault: - return ctx.push("alignment fault"); - case kapi::cpu::exception::type::memory_access_fault: - return ctx.push("memory access fault"); - case kapi::cpu::exception::type::illegal_instruction: - return ctx.push("illegal instruction"); - case kapi::cpu::exception::type::privilege_violation: - return ctx.push("privilege violation"); - case kapi::cpu::exception::type::arithmetic_error: - return ctx.push("arithmetic error"); - case kapi::cpu::exception::type::breakpoint: - return ctx.push("breakpoint"); - case kapi::cpu::exception::type::single_step: - return ctx.push("single step"); - } - } -}; - -#endif \ No newline at end of file -- cgit v1.2.3 From 00a77644192642e06462c11479a5c0e9bd859e9a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:35:32 +0100 Subject: kapi: extract interrupts API --- arch/x86_64/CMakeLists.txt | 2 +- arch/x86_64/kapi/cpu/interrupts.cpp | 67 ----------------------------------- arch/x86_64/kapi/interrupts.cpp | 68 ++++++++++++++++++++++++++++++++++++ arch/x86_64/src/cpu/interrupts.cpp | 3 +- kapi/CMakeLists.txt | 3 +- kapi/include/kapi/cpu.hpp | 1 - kapi/include/kapi/cpu/interrupts.hpp | 63 --------------------------------- kapi/include/kapi/interrupts.hpp | 61 ++++++++++++++++++++++++++++++++ kernel/src/main.cpp | 3 +- 9 files changed, 135 insertions(+), 136 deletions(-) delete mode 100644 arch/x86_64/kapi/cpu/interrupts.cpp create mode 100644 arch/x86_64/kapi/interrupts.cpp delete mode 100644 kapi/include/kapi/cpu/interrupts.hpp create mode 100644 kapi/include/kapi/interrupts.hpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 8ff81d8..4427e4c 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -15,7 +15,7 @@ target_sources("x86_64" PRIVATE "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" - "kapi/cpu/interrupts.cpp" + "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/arch/x86_64/kapi/cpu/interrupts.cpp b/arch/x86_64/kapi/cpu/interrupts.cpp deleted file mode 100644 index b98595c..0000000 --- a/arch/x86_64/kapi/cpu/interrupts.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "kapi/cpu.hpp" -#include "kapi/system.hpp" - -#include -#include - -#include -#include - -namespace kapi::cpu -{ - - namespace - { - constexpr auto irq_offset = 32uz; - auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; - } // namespace - - auto enable_interrupts() -> void - { - asm volatile("sti"); - } - - auto disable_interrupts() -> void - { - asm volatile("cli"); - } - - auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - interrupt_handlers[irq_number - irq_offset].push_back(&handler); - } - - auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - kstd::println("[x86_64:CPU] TODO: support erasure from vector."); - } - - auto dispatch_interrupt(std::uint32_t irq_number) -> status - { - if (irq_number < irq_offset) - { - return status::unhandled; - } - - for (auto handler : interrupt_handlers[irq_number - irq_offset]) - { - if (handler && handler->handle_interrupt(irq_number) == status::handled) - { - return status::handled; - } - } - - return status::unhandled; - } - -} // namespace kapi::cpu \ No newline at end of file diff --git a/arch/x86_64/kapi/interrupts.cpp b/arch/x86_64/kapi/interrupts.cpp new file mode 100644 index 0000000..babc926 --- /dev/null +++ b/arch/x86_64/kapi/interrupts.cpp @@ -0,0 +1,68 @@ +#include "kapi/interrupts.hpp" + +#include "kapi/system.hpp" + +#include +#include + +#include +#include + +namespace kapi::interrupts +{ + + namespace + { + constexpr auto irq_offset = 32uz; + auto constinit handlers = std::array, 256 - irq_offset>{}; + } // namespace + + auto enable() -> void + { + asm volatile("sti"); + } + + auto disable() -> void + { + asm volatile("cli"); + } + + auto register_handler(std::uint32_t irq_number, handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + handlers[irq_number - irq_offset].push_back(&handler); + } + + auto unregister_handler(std::uint32_t irq_number, [[maybe_unused]] handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + kstd::println("[x86_64:CPU] TODO: support erasure from vector."); + } + + auto dispatch(std::uint32_t irq_number) -> status + { + if (irq_number < irq_offset) + { + return status::unhandled; + } + + for (auto handler : handlers[irq_number - irq_offset]) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + +} // namespace kapi::interrupts \ No newline at end of file diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 9ee3ce8..2f23f07 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -1,6 +1,7 @@ #include "arch/cpu/interrupts.hpp" #include "kapi/cpu.hpp" +#include "kapi/interrupts.hpp" #include "kapi/memory.hpp" #include "arch/cpu/legacy_pic.hpp" @@ -146,7 +147,7 @@ namespace arch::cpu } } else if (number >= number_of_exception_vectors && - kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) + kapi::interrupts::dispatch(number) == kapi::interrupts::status::unhandled) { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 4c94829..99b737c 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -9,8 +9,7 @@ target_sources("kapi" PUBLIC "include/kapi/boot.hpp" "include/kapi/cio.hpp" "include/kapi/cpu.hpp" - "include/kapi/cpu/interrupts.hpp" - "include/kapi/cpu/exception.hpp" + "include/kapi/interrupts.hpp" "include/kapi/boot_module/boot_module.hpp" "include/kapi/boot_module/boot_module_registry.hpp" "include/kapi/memory.hpp" diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 041a5db..c6aa6ff 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,7 +1,6 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/cpu/interrupts.hpp" // IWYU pragma: export #include "kapi/memory.hpp" #include diff --git a/kapi/include/kapi/cpu/interrupts.hpp b/kapi/include/kapi/cpu/interrupts.hpp deleted file mode 100644 index 328e4a7..0000000 --- a/kapi/include/kapi/cpu/interrupts.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef TEACHOS_KAPI_CPU_INTERRUPTS_HPP -#define TEACHOS_KAPI_CPU_INTERRUPTS_HPP - -// IWYU pragma: private, include "kapi/cpu.hpp" - -#include - -namespace kapi::cpu -{ - - enum class status : bool - { - unhandled, - handled, - }; - - //! The interface for all interrupt handlers. - struct interrupt_handler - { - virtual ~interrupt_handler() = default; - - //! Handle an interrupt with the given IRQ number. - // - //! This function will be called by the kernel in an interrupt context. As such, the function should complete its - //! task quickly and must take care when acquiring globally shared locks. - //! - //! @param irq_number The identifier of the interrupt request that triggered the handler. - //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. - virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; - }; - - //! @qualifier platform-defined - //! Enable external interrupts. - auto enable_interrupts() -> void; - - //! @qualifier platform-defined - //! Disable external interrupts. - auto disable_interrupts() -> void; - - //! @qualifier platform-defined - //! Register an interrupt handler for the given IRQ number. - //! - //! @param irq_number The IRQ number to register the handler for. - //! @param handler The interrupt handler to register. - auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; - - //! @qualifier platform-defined - //! Unregister a previously registered interrupt handler. - //! - //! @param irq_number The IRQ number to unregister the handler for. - //! @param handler The interrupt handler to unregister. - auto unregister_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; - - //! @qualifier platform-defined - //! Dispatch an interrupt to all registered handlers. - //! - //! @param irq_number The IRQ number to dispatch. - //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. - auto dispatch_interrupt(std::uint32_t irq_number) -> status; - -} // namespace kapi::cpu - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/interrupts.hpp b/kapi/include/kapi/interrupts.hpp new file mode 100644 index 0000000..fa4bc95 --- /dev/null +++ b/kapi/include/kapi/interrupts.hpp @@ -0,0 +1,61 @@ +#ifndef TEACHOS_KAPI_INTERRUPTS_HPP +#define TEACHOS_KAPI_INTERRUPTS_HPP + +#include + +namespace kapi::interrupts +{ + + enum class status : bool + { + unhandled, + handled, + }; + + //! The interface for all interrupt handlers. + struct handler + { + virtual ~handler() = default; + + //! Handle an interrupt with the given IRQ number. + // + //! This function will be called by the kernel in an interrupt context. As such, the function should complete its + //! task quickly and must take care when acquiring globally shared locks. + //! + //! @param irq_number The identifier of the interrupt request that triggered the handler. + //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. + virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; + }; + + //! @qualifier platform-defined + //! Enable external interrupts. + auto enable() -> void; + + //! @qualifier platform-defined + //! Disable external interrupts. + auto disable() -> void; + + //! @qualifier platform-defined + //! Register an interrupt handler for the given IRQ number. + //! + //! @param irq_number The IRQ number to register the handler for. + //! @param handler The interrupt handler to register. + auto register_handler(std::uint32_t irq_number, handler & handler) -> void; + + //! @qualifier platform-defined + //! Unregister a previously registered interrupt handler. + //! + //! @param irq_number The IRQ number to unregister the handler for. + //! @param handler The interrupt handler to unregister. + auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; + + //! @qualifier platform-defined + //! Dispatch an interrupt to all registered handlers. + //! + //! @param irq_number The IRQ number to dispatch. + //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. + auto dispatch(std::uint32_t irq_number) -> status; + +} // namespace kapi::interrupts + +#endif \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 45a4aae..6434045 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,6 +1,7 @@ #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" +#include "kapi/interrupts.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" @@ -92,7 +93,7 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::cpu::init(); - kapi::cpu::enable_interrupts(); + kapi::interrupts::enable(); kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); -- cgit v1.2.3 From f4dc64976049761a6f56dd55d9d0b651f1e9475f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:47:41 +0100 Subject: kapi: move interrupt handling to kernel --- arch/x86_64/kapi/interrupts.cpp | 52 ---------------------------------------- kapi/include/kapi/interrupts.hpp | 6 ++--- kernel/CMakeLists.txt | 1 + kernel/kapi/interrupts.cpp | 45 ++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 55 deletions(-) create mode 100644 kernel/kapi/interrupts.cpp diff --git a/arch/x86_64/kapi/interrupts.cpp b/arch/x86_64/kapi/interrupts.cpp index babc926..cf1defa 100644 --- a/arch/x86_64/kapi/interrupts.cpp +++ b/arch/x86_64/kapi/interrupts.cpp @@ -1,22 +1,8 @@ #include "kapi/interrupts.hpp" -#include "kapi/system.hpp" - -#include -#include - -#include -#include - namespace kapi::interrupts { - namespace - { - constexpr auto irq_offset = 32uz; - auto constinit handlers = std::array, 256 - irq_offset>{}; - } // namespace - auto enable() -> void { asm volatile("sti"); @@ -27,42 +13,4 @@ namespace kapi::interrupts asm volatile("cli"); } - auto register_handler(std::uint32_t irq_number, handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - handlers[irq_number - irq_offset].push_back(&handler); - } - - auto unregister_handler(std::uint32_t irq_number, [[maybe_unused]] handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - kstd::println("[x86_64:CPU] TODO: support erasure from vector."); - } - - auto dispatch(std::uint32_t irq_number) -> status - { - if (irq_number < irq_offset) - { - return status::unhandled; - } - - for (auto handler : handlers[irq_number - irq_offset]) - { - if (handler && handler->handle_interrupt(irq_number) == status::handled) - { - return status::handled; - } - } - - return status::unhandled; - } - } // namespace kapi::interrupts \ No newline at end of file diff --git a/kapi/include/kapi/interrupts.hpp b/kapi/include/kapi/interrupts.hpp index fa4bc95..f72ef8c 100644 --- a/kapi/include/kapi/interrupts.hpp +++ b/kapi/include/kapi/interrupts.hpp @@ -35,21 +35,21 @@ namespace kapi::interrupts //! Disable external interrupts. auto disable() -> void; - //! @qualifier platform-defined + //! @qualifier kernel-defined //! Register an interrupt handler for the given IRQ number. //! //! @param irq_number The IRQ number to register the handler for. //! @param handler The interrupt handler to register. auto register_handler(std::uint32_t irq_number, handler & handler) -> void; - //! @qualifier platform-defined + //! @qualifier kernel-defined //! Unregister a previously registered interrupt handler. //! //! @param irq_number The IRQ number to unregister the handler for. //! @param handler The interrupt handler to unregister. auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; - //! @qualifier platform-defined + //! @qualifier kernel-defined //! Dispatch an interrupt to all registered handlers. //! //! @param irq_number The IRQ number to dispatch. diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9b1e2ad..535c441 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -3,6 +3,7 @@ add_executable("kernel" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp new file mode 100644 index 0000000..e79453a --- /dev/null +++ b/kernel/kapi/interrupts.cpp @@ -0,0 +1,45 @@ +#include "kapi/interrupts.hpp" + +#include +#include + +#include +#include + +namespace kapi::interrupts +{ + + namespace + { + auto constinit handlers = std::array, 256>{}; + } // namespace + + auto register_handler(std::uint32_t irq_number, handler & handler) -> void + { + auto & handler_list = handlers.at(irq_number); + handler_list.push_back(&handler); + } + + auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void + { + static_cast(irq_number); + static_cast(handler); + kstd::println("[OS:interrupts] TODO: support erasure from vector."); + } + + auto dispatch(std::uint32_t irq_number) -> status + { + auto & handler_list = handlers.at(irq_number); + + for (auto handler : handler_list) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + +} // namespace kapi::interrupts \ No newline at end of file -- cgit v1.2.3 From a2834cc22b096e848448bb681ab7b517ecbe70b9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:54:07 +0100 Subject: build: simplify header scanning --- CMakeLists.txt | 4 ++++ kapi/CMakeLists.txt | 17 +++-------------- kernel/CMakeLists.txt | 9 +++++++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index febcf0e..d785d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,10 @@ option(TEACHOS_GENERATE_DOCS "Generate documentation during build" ON) # Global Build System Configuration #]============================================================================] +if(POLICY CMP0209) + cmake_policy(SET CMP0209 NEW) +endif() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(CMAKE_INTERPROCEDURAL_OPTIMIZATION YES) diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 99b737c..c9aa23f 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,24 +1,13 @@ add_library("kapi" INTERFACE) add_library("os::kapi" ALIAS "kapi") +file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + target_sources("kapi" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/kapi/boot_modules.hpp" - "include/kapi/boot.hpp" - "include/kapi/cio.hpp" - "include/kapi/cpu.hpp" - "include/kapi/interrupts.hpp" - "include/kapi/boot_module/boot_module.hpp" - "include/kapi/boot_module/boot_module_registry.hpp" - "include/kapi/memory.hpp" - "include/kapi/memory/address.hpp" - "include/kapi/memory/frame_allocator.hpp" - "include/kapi/memory/frame.hpp" - "include/kapi/memory/page_mapper.hpp" - "include/kapi/memory/page.hpp" - "include/kapi/system.hpp" + ${KAPI_HEADERS} ) target_include_directories("kapi" INTERFACE diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 535c441..3d3c7e2 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -56,6 +56,15 @@ set_property(TARGET "kernel" "${KERNEL_LINKER_SCRIPT}" ) +file(GLOB_RECURSE KERNEL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + +target_sources("kernel" PUBLIC + FILE_SET HEADERS + BASE_DIRS "include" + FILES + ${KERNEL_HEADERS} +) + target_disassemble("kernel") target_extract_debug_symbols("kernel") target_strip("kernel") -- cgit v1.2.3 From aa68f53d2502e0ea81c8e9c95e37d9847cb6cb16 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 17:15:26 +0100 Subject: arch/cpu: fix interrupt dispatch --- arch/x86_64/src/cpu/interrupts.cpp | 34 +++++++++++++++++++++------------- kernel/kapi/interrupts.cpp | 6 ++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 2f23f07..907f289 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -134,25 +134,33 @@ namespace arch::cpu { auto [number, code] = frame->interrupt; - if (number < number_of_exception_vectors && !dispatch_exception(frame)) + if (number < number_of_exception_vectors) { - if (has_error_code(static_cast(number))) + if (!dispatch_exception(frame)) { - kstd::println(kstd::print_sink::stderr, - "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); - } - else - { - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + if (has_error_code(static_cast(number))) + { + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + } } } - else if (number >= number_of_exception_vectors && - kapi::interrupts::dispatch(number) == kapi::interrupts::status::unhandled) + else { - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); - } + auto irq_number = number - number_of_exception_vectors; - acknowledge_pic_interrupt(frame); + if (kapi::interrupts::dispatch(irq_number) == kapi::interrupts::status::unhandled) + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} (IRQ{})", number, + irq_number); + } + + acknowledge_pic_interrupt(frame); + } } } diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index e79453a..27427bb 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -31,6 +31,12 @@ namespace kapi::interrupts { auto & handler_list = handlers.at(irq_number); + if (handler_list.empty()) + { + kstd::println(kstd::print_sink::stderr, "[OS:interrupts] No handler for IRQ{}", irq_number); + return status::unhandled; + } + for (auto handler : handler_list) { if (handler && handler->handle_interrupt(irq_number) == status::handled) -- cgit v1.2.3 From 4b094e2bd5a8e60ec1018d6aa90aa9da4adf49c9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:04:12 +0100 Subject: kstd/vector: implement single-element erase --- libs/kstd/include/kstd/vector | 16 ++++++++ libs/kstd/tests/src/vector.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 0d4aac8..74aefa9 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -690,6 +690,22 @@ namespace kstd return begin() + prefix_size; } + constexpr auto erase(const_iterator position) -> iterator + { + if (position == end()) + { + os::panic("[kstd:vector] Attempted to erase end()!"); + } + + auto prefix_size = std::ranges::distance(cbegin(), position); + + std::ranges::move(begin() + prefix_size + 1, end(), begin() + prefix_size); + std::allocator_traits::destroy(m_allocator, end() - 1); + --m_size; + + return begin() + prefix_size; + } + //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 0735c9a..3ff041f 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -649,6 +649,45 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.begin() + 1); } } + + WHEN("erasing the first element") + { + auto it = v.erase(v.cbegin()); + + THEN("the first element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 20); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("erasing a middle element") + { + auto it = v.erase(v.cbegin() + 1); + + THEN("the middle element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + auto it = v.erase(v.cend() - 1); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(it == v.end()); + } + } } } @@ -868,6 +907,58 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v[3].was_moved); } } + + WHEN("erasing an element in the middle") + { + for (auto& elem : v) + { + elem.was_copied = false; + elem.was_moved = false; + } + + auto it = v.erase(v.cbegin() + 1); + + THEN("the subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE_FALSE(v[0].was_copied); + + REQUIRE(v[1].value == 30); + REQUIRE(v[1].was_moved); + REQUIRE_FALSE(v[1].was_copied); + + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + for (auto& elem : v) + { + elem.was_copied = false; + elem.was_moved = false; + } + + auto it = v.erase(v.cend() - 1); + + THEN("no elements are moved, just the last element destroyed") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE_FALSE(v[0].was_copied); + + REQUIRE(v[1].value == 20); + REQUIRE_FALSE(v[1].was_moved); + REQUIRE_FALSE(v[1].was_copied); + + REQUIRE(it == v.end()); + } + } } } -- cgit v1.2.3 From f6100699cd93606147ebe94a777b6e4aff7c5f50 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:09:15 +0100 Subject: kstd/vector: add missing tests for insert --- libs/kstd/tests/src/vector.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 3ff041f..02b8786 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -650,6 +650,40 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("inserting an rvalue reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting an rvalue reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + WHEN("erasing the first element") { auto it = v.erase(v.cbegin()); -- cgit v1.2.3 From 096d7505cfc2d60e58a6dd4d80fd7f3638c9bb94 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:19:52 +0100 Subject: kstd/vector: increase test coverage --- .lcovrc | 2 +- libs/kstd/include/kstd/vector | 7 ++++++- libs/kstd/tests/src/vector.cpp | 44 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/.lcovrc b/.lcovrc index d19e80d..07da866 100644 --- a/.lcovrc +++ b/.lcovrc @@ -1,5 +1,5 @@ exclude = /usr/include/* -exclude = build/_deps/* +exclude = build/bht/_deps/* exclude = tests/* ignore_errors = unused,empty,inconsistent \ No newline at end of file diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 74aefa9..771fc87 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -548,7 +548,6 @@ namespace kstd if (new_capacity > max_size()) { kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); - return; } auto new_data = allocate_n(new_capacity); @@ -690,6 +689,12 @@ namespace kstd return begin() + prefix_size; } + //! Erase an element at a given position. + //! + //! @note This function will panic if position == end() + //! + //! @param position An interator pointing to the element to delete + //! @return An iterator pointing to the element after the deleted element constexpr auto erase(const_iterator position) -> iterator { if (position == end()) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 02b8786..913427c 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -475,6 +475,20 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.begin()); } } + + WHEN("inserting an lvalue element") + { + auto const value = 42; + auto it = v.insert(v.cbegin(), value); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 42); + REQUIRE(it == v.begin()); + } + } } GIVEN("A populated vector") @@ -597,6 +611,22 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("inserting an lvalue at the end") + { + auto const value = 40; + auto it = v.insert(v.cend(), value); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + WHEN("inserting when capacity is sufficient") { v.reserve(10); @@ -722,6 +752,14 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.end()); } } + + WHEN("erasing the end() iterator") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); + } + } } } @@ -944,7 +982,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("erasing an element in the middle") { - for (auto& elem : v) + for (auto & elem : v) { elem.was_copied = false; elem.was_moved = false; @@ -955,7 +993,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") THEN("the subsequent elements are move-assigned leftwards") { REQUIRE(v.size() == 2); - + REQUIRE(v[0].value == 10); REQUIRE_FALSE(v[0].was_moved); REQUIRE_FALSE(v[0].was_copied); @@ -970,7 +1008,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("erasing the last element") { - for (auto& elem : v) + for (auto & elem : v) { elem.was_copied = false; elem.was_moved = false; -- cgit v1.2.3 From 11c6d57e013832983bcd9bb965d470bf4c282ab6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:40:39 +0100 Subject: kstd/vector: implement range erase --- CMakeLists.txt | 18 +++++++------ kernel/kapi/interrupts.cpp | 7 ++--- libs/kstd/include/kstd/vector | 22 ++++++++++++++++ libs/kstd/tests/src/vector.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d785d4e..6cc8a2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,15 +18,17 @@ include("GenerateBootableIso") include("FetchContent") -FetchContent_Declare( - "Catch2" - URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" - URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS -) +if (NOT CMAKE_CROSSCOMPILING) + FetchContent_Declare( + "Catch2" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) -FetchContent_MakeAvailable("Catch2") + FetchContent_MakeAvailable("Catch2") +endif() #[============================================================================[ # Global Build System Options diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index 27427bb..e172e70 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -22,9 +23,9 @@ namespace kapi::interrupts auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void { - static_cast(irq_number); - static_cast(handler); - kstd::println("[OS:interrupts] TODO: support erasure from vector."); + auto & handler_list = handlers.at(irq_number); + auto [first, last] = std::ranges::remove(handler_list, &handler); + handler_list.erase(first, last); } auto dispatch(std::uint32_t irq_number) -> status diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 771fc87..9e41cb6 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -711,6 +711,28 @@ namespace kstd return begin() + prefix_size; } + //! Erase a range of elements from this vector. + //! + //! @param first The start of the range to erase. + //! @param last The end of the range to erase. + //! @return An iterator pointing to the element after the last deleted element. + constexpr auto erase(const_iterator first, const_iterator last) -> iterator + { + if (first == last) + { + return begin() + std::ranges::distance(cbegin(), first); + } + + auto prefix_size = std::ranges::distance(cbegin(), first); + auto element_count = std::ranges::distance(first, last); + + std::ranges::move(begin() + prefix_size + element_count, end(), begin() + prefix_size); + destroy_n(end() - element_count, element_count); + m_size -= element_count; + + return begin() + prefix_size; + } + //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 913427c..b7971f4 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -760,6 +760,33 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); } } + + WHEN("erasing a range of elements") + { + auto it = v.erase(v.cbegin() + 1, v.cend() - 1); + + THEN("the specified range is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing an empty range") + { + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1); + + THEN("the vector is unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(it == v.begin() + 1); + } + } } } @@ -1031,6 +1058,39 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(it == v.end()); } } + + WHEN("erasing a range of elements in the middle") + { + v.emplace_back(40); + v.emplace_back(50); + + for (auto & elem : v) + { + elem.was_copied = false; + elem.was_moved = false; + } + + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); + + THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 3); + + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE_FALSE(v[0].was_copied); + + REQUIRE(v[1].value == 40); + REQUIRE(v[1].was_moved); + REQUIRE_FALSE(v[1].was_copied); + + REQUIRE(v[2].value == 50); + REQUIRE(v[2].was_moved); + REQUIRE_FALSE(v[2].was_copied); + + REQUIRE(it == v.begin() + 1); + } + } } } -- cgit v1.2.3 From 3070bb45b9741165d786b2c5a018ee55c1a82db8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 27 Mar 2026 07:05:05 +0100 Subject: kernel/interrupts: switch to flat_map for dispatch --- kernel/kapi/interrupts.cpp | 21 +- libs/kstd/CMakeLists.txt | 1 + libs/kstd/include/kstd/bits/flat_map.hpp | 82 ++++++++ libs/kstd/include/kstd/flat_map | 320 +++++++++++++++++++++++++++++++ libs/kstd/tests/src/flat_map.cpp | 288 ++++++++++++++++++++++++++++ 5 files changed, 708 insertions(+), 4 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/flat_map.hpp create mode 100644 libs/kstd/include/kstd/flat_map create mode 100644 libs/kstd/tests/src/flat_map.cpp diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index e172e70..0e37bc3 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -1,10 +1,10 @@ #include "kapi/interrupts.hpp" +#include #include #include #include -#include #include namespace kapi::interrupts @@ -12,13 +12,20 @@ namespace kapi::interrupts namespace { - auto constinit handlers = std::array, 256>{}; + auto constinit handlers = kstd::flat_map>{}; } // namespace auto register_handler(std::uint32_t irq_number, handler & handler) -> void { - auto & handler_list = handlers.at(irq_number); - handler_list.push_back(&handler); + if (handlers.contains(irq_number)) + { + auto & handler_list = handlers.at(irq_number); + handler_list.push_back(&handler); + } + else + { + handlers.emplace(irq_number, kstd::vector{&handler}); + } } auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void @@ -30,6 +37,12 @@ namespace kapi::interrupts auto dispatch(std::uint32_t irq_number) -> status { + if (!handlers.contains(irq_number)) + { + kstd::println(kstd::print_sink::stderr, "[OS:interrupts] No handler for IRQ{}", irq_number); + return status::unhandled; + } + auto & handler_list = handlers.at(irq_number); if (handler_list.empty()) diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 06543ab..ff3c8cc 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,6 +44,7 @@ endif() if(NOT CMAKE_CROSSCOMPILING) add_executable("kstd_tests" + "tests/src/flat_map.cpp" "tests/src/vector.cpp" "tests/src/os_panic.cpp" ) diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp new file mode 100644 index 0000000..903841e --- /dev/null +++ b/libs/kstd/include/kstd/bits/flat_map.hpp @@ -0,0 +1,82 @@ +#ifndef KSTD_BITS_FLAT_MAP_HPP +#define KSTD_BITS_FLAT_MAP_HPP + +#include +#include +#include + +namespace kstd::bits +{ + + template + struct flat_map_iterator + { + using iterator_category = std::random_access_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using reference = std::pair; + using pointer = void; + + constexpr flat_map_iterator(KeyIterator key_iterator, MappedIterator mapped_iterator) + : m_key_iterator{key_iterator} + , m_mapped_iterator{mapped_iterator} + {} + + [[nodiscard]] auto key_iterator() const noexcept -> KeyIterator + { + return m_key_iterator; + } + + [[nodiscard]] constexpr auto operator*() const noexcept -> reference + { + return {*m_key_iterator, *m_mapped_iterator}; + } + + constexpr auto operator++() noexcept -> flat_map_iterator & + { + ++m_key_iterator; + ++m_mapped_iterator; + return *this; + } + + constexpr auto operator++(int) noexcept -> flat_map_iterator + { + auto copy = *this; + ++(*this); + return copy; + } + + constexpr auto operator--() noexcept -> flat_map_iterator & + { + --m_key_iterator; + --m_mapped_iterator; + return *this; + } + + constexpr auto operator--(int) noexcept -> flat_map_iterator + { + auto copy = *this; + --(*this); + return copy; + } + + [[nodiscard]] constexpr auto operator+(difference_type offset) const noexcept -> flat_map_iterator + { + return {m_key_iterator + offset, m_mapped_iterator + offset}; + } + + [[nodiscard]] constexpr auto operator-(flat_map_iterator const & other) const noexcept -> difference_type + { + return m_key_iterator - other.m_key_iterator; + } + + [[nodiscard]] constexpr auto operator<=>(flat_map_iterator const & other) const noexcept = default; + + private: + KeyIterator m_key_iterator{}; + MappedIterator m_mapped_iterator{}; + }; + +} // namespace kstd::bits + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/flat_map b/libs/kstd/include/kstd/flat_map new file mode 100644 index 0000000..6ffbbcf --- /dev/null +++ b/libs/kstd/include/kstd/flat_map @@ -0,0 +1,320 @@ +#ifndef KSTD_FLAT_MAP_HPP +#define KSTD_FLAT_MAP_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + template, + typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> + struct flat_map + { + using key_container_type = KeyContainerType; + using mapped_container_type = MappedContainerType; + using key_type = KeyType; + using mapped_type = MappedType; + using value_type = std::pair; + using key_compare = KeyCompare; + using reference = std::pair; + using const_reference = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = bits::flat_map_iterator; + using const_iterator = + bits::flat_map_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using containers = struct + { + key_container_type keys; + mapped_container_type values; + }; + + //! Compare two object of type value_type. + struct value_compare + { + constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool + { + return lhs.first < rhs.first; + } + }; + + //! Construct an empty flat map. + constexpr flat_map() + : flat_map{key_compare{}} + {} + + //! Construct an empty flat map using the given custom comparator. + //! + //! @param comparator The comparator to use for comparing keys. + constexpr explicit flat_map(key_compare const & comparator) + : m_containers{} + , m_comparator{comparator} + {} + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A const reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] constexpr auto at(K const & x) -> mapped_type & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.begin(), found.key_iterator()); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A const reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto at(K const & x) const -> mapped_type const & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator()); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() noexcept -> iterator + { + return iterator{m_containers.keys.begin(), m_containers.values.begin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() noexcept -> iterator + { + return iterator{m_containers.keys.end(), m_containers.values.end()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator + { + return reverse_iterator{end()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() noexcept -> reverse_iterator + { + return reverse_iterator{begin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Try to insert a new key-value pair into the map. + //! + //! @param args Arguments to use for constructing the key-value pair. + //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place. + template + auto emplace(Args &&... args) -> std::pair + requires std::constructible_from + { + auto value = value_type{std::forward(args)...}; + auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator); + + if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return { + iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}, + false + }; + } + + auto offset = std::distance(m_containers.keys.begin(), found); + auto key_iterator = m_containers.keys.begin() + offset; + auto mapped_iterator = m_containers.values.begin() + offset; + + auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first)); + auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second)); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Check if the map contains the given key. + //! + //! @param key The key to check. + //! @return true iff. the key is found, false otherwise. + [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool + { + return find(key) != cend(); + } + + private: + containers m_containers; + key_compare m_comparator; + }; +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/tests/src/flat_map.cpp b/libs/kstd/tests/src/flat_map.cpp new file mode 100644 index 0000000..cde136a --- /dev/null +++ b/libs/kstd/tests/src/flat_map.cpp @@ -0,0 +1,288 @@ +#include +#include + +#include + +#include + +SCENARIO("Flat Map initialization and construction", "[flat_map]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto map = kstd::flat_map{}; + + THEN("the Flat Map does not contain elements") + { + REQUIRE_FALSE(map.contains(1)); + } + } + } +} + +SCENARIO("Flat Map modifiers", "[flat_map]") +{ + GIVEN("An empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("emplacing a new element") + { + auto [it, inserted] = map.emplace(1, 100); + + THEN("the map contains the new element") + { + REQUIRE(inserted); + REQUIRE(map.contains(1)); + } + } + + WHEN("emplacing an existing element") + { + map.emplace(1, 100); + auto [it, inserted] = map.emplace(1, 200); + + THEN("the map does not insert the duplicate") + { + REQUIRE_FALSE(inserted); + REQUIRE(map.contains(1)); + } + } + } +} + +SCENARIO("Flat Map element access", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with at()") + { + auto & val = map.at(2); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2) == 200); + } + } + + WHEN("accessing a non-existent element with at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map") + { + auto map_builder = kstd::flat_map{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with const at()") + { + auto const & val = map.at(2); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with const at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Flat Map iterators", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = map.begin(); + REQUIRE(it != map.end()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = map.cbegin(); + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.cend()); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = map.rbegin(); + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = map.crbegin(); + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.crend()); + } + } + } + + GIVEN("an empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(map.begin() == map.end()); + REQUIRE(map.cbegin() == map.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(map.rbegin() == map.rend()); + REQUIRE(map.crbegin() == map.crend()); + } + } + } +} + +SCENARIO("Flat Map heterogeneous element access", "[flat_map]") +{ + GIVEN("A populated Flat Map with a transparent comparator") + { + auto map = kstd::flat_map>{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with a different key type via at()") + { + long const key = 2L; + auto & val = map.at(key); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2L) == 200); + } + } + + WHEN("accessing a non-existent element with a different key type via at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map with a transparent comparator") + { + auto map_builder = kstd::flat_map>{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with a different key type via const at()") + { + long const key = 2L; + auto const & val = map.at(key); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with a different key type via const at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } +} -- cgit v1.2.3