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.
This commit is contained in:
Pankaj Raghav 2023-04-28 14:33:10 +02:00 committed by Jelle Raaijmakers
parent a5ec5f07fa
commit 91da264a4c
3 changed files with 58 additions and 0 deletions

View file

@ -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<u8> reserve_interrupt_handlers(u8 number_of_irqs);
void initialize_interrupts();

View file

@ -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<u8> reserve_interrupt_handlers([[maybe_unused]] u8 number_of_irqs)
{
TODO();
return Error::from_errno(EINVAL);
}
}

View file

@ -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<LockRank::None> 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<u8> 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) {