diff --git a/Kernel/Arch/Interrupts.h b/Kernel/Arch/Interrupts.h index 14e11147ad..ec90c2b48b 100644 --- a/Kernel/Arch/Interrupts.h +++ b/Kernel/Arch/Interrupts.h @@ -20,6 +20,7 @@ class GenericInterruptHandler; GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number); void register_generic_interrupt_handler(u8 number, GenericInterruptHandler&); void unregister_generic_interrupt_handler(u8 number, GenericInterruptHandler&); +ErrorOr reserve_interrupt_handlers(u8 number_of_irqs); void initialize_interrupts(); diff --git a/Kernel/Arch/aarch64/Interrupts.cpp b/Kernel/Arch/aarch64/Interrupts.cpp index d1b15af2bb..28ede90d1f 100644 --- a/Kernel/Arch/aarch64/Interrupts.cpp +++ b/Kernel/Arch/aarch64/Interrupts.cpp @@ -220,4 +220,12 @@ void initialize_interrupts() } } +// Sets the reserved flag on `number_of_irqs` if it finds unused interrupt handler on +// a contiguous range. +ErrorOr reserve_interrupt_handlers([[maybe_unused]] u8 number_of_irqs) +{ + TODO(); + return Error::from_errno(EINVAL); +} + } diff --git a/Kernel/Arch/x86_64/Interrupts.cpp b/Kernel/Arch/x86_64/Interrupts.cpp index b736da7036..15a524539d 100644 --- a/Kernel/Arch/x86_64/Interrupts.cpp +++ b/Kernel/Arch/x86_64/Interrupts.cpp @@ -44,6 +44,8 @@ namespace Kernel { READONLY_AFTER_INIT static DescriptorTablePointer s_idtr; READONLY_AFTER_INIT static IDTEntry s_idt[256]; +// This spinlock is used to reserve IRQs that can be later used by interrupt mechanism such as MSIx +static Spinlock s_interrupt_handler_lock {}; static GenericInterruptHandler* s_interrupt_handler[GENERIC_INTERRUPT_HANDLERS_COUNT]; static GenericInterruptHandler* s_disabled_interrupt_handler[2]; @@ -334,6 +336,53 @@ static void revert_to_unused_handler(u8 interrupt_number) handler->register_interrupt_handler(); } +static bool is_unused_handler(GenericInterruptHandler* handler_slot) +{ + return (handler_slot->type() == HandlerType::UnhandledInterruptHandler) && !handler_slot->reserved(); +} + +// Sets the reserved flag on `number_of_irqs` if it finds unused interrupt handler on +// a contiguous range. +ErrorOr reserve_interrupt_handlers(u8 number_of_irqs) +{ + bool found_range = false; + u8 first_irq = 0; + SpinlockLocker locker(s_interrupt_handler_lock); + for (int start_irq = 0; start_irq < GENERIC_INTERRUPT_HANDLERS_COUNT; start_irq++) { + auto*& handler_slot = s_interrupt_handler[start_irq]; + VERIFY(handler_slot != nullptr); + + if (!is_unused_handler(handler_slot)) + continue; + + found_range = true; + for (auto off = 1; off < number_of_irqs; off++) { + auto*& handler = s_interrupt_handler[start_irq + off]; + VERIFY(handler_slot != nullptr); + + if (!is_unused_handler(handler)) { + found_range = false; + break; + } + } + + if (found_range == true) { + first_irq = start_irq; + break; + } + } + + if (!found_range) + return Error::from_errno(EAGAIN); + + for (auto irq = first_irq; irq < number_of_irqs; irq++) { + auto*& handler_slot = s_interrupt_handler[irq]; + handler_slot->set_reserved(); + } + + return first_irq; +} + void register_disabled_interrupt_handler(u8 number, GenericInterruptHandler& handler) { if (number == 15) {