From 91da264a4c35a0b79b924334067327d98e9c50fd Mon Sep 17 00:00:00 2001 From: Pankaj Raghav Date: Fri, 28 Apr 2023 14:33:10 +0200 Subject: [PATCH] Kernel: Add reserve_interrupt_handlers API MSI(x) interrupts need to reserve IRQs so that it can be programmed by the device. Add an API to reserve contiguous ranges of interrupt handlers so that it can used by PCI devices that use MSI(x) mechanism. This API needs to be implemented by aarch64 architecture. --- Kernel/Arch/Interrupts.h | 1 + Kernel/Arch/aarch64/Interrupts.cpp | 8 +++++ Kernel/Arch/x86_64/Interrupts.cpp | 49 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) 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) {