aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86_64/kapi/cpu.cpp49
-rw-r--r--arch/x86_64/src/cpu/interrupts.cpp17
-rw-r--r--kapi/include/kapi/cpu/interrupts.hpp44
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 <kstd/print>
+#include <kstd/vector>
+
+#include <array>
#include <atomic>
+#include <cstdint>
namespace kapi::cpu
{
+ namespace
+ {
+ constexpr auto irq_offset = 32uz;
+ auto constinit interrupt_handlers = std::array<kstd::vector<interrupt_handler *>, 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 <cstdint>
+
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