summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorTimon Kruiper <timonkruiper@gmail.com>2022-05-30 10:14:46 +0200
committerLinus Groh <mail@linusgroh.de>2022-06-02 13:14:12 +0100
commit3cf8d3361e347da561f7a67dffa988969cc08914 (patch)
treeeaae9eba38cf9646dfb717857a4fc0afccc58f00 /Kernel
parentc959344c00e1b68d95cf57e5ed7b05259359d99b (diff)
downloadserenity-3cf8d3361e347da561f7a67dffa988969cc08914.zip
Kernel: Add support for handling interrupts on aarch64
There is currently some code duplication between the aarch64 implementation and the x86 one, but this initial implementation works for now. :^)
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/Arch/aarch64/Interrupts.cpp119
-rw-r--r--Kernel/Arch/aarch64/vector_table.S3
2 files changed, 114 insertions, 8 deletions
diff --git a/Kernel/Arch/aarch64/Interrupts.cpp b/Kernel/Arch/aarch64/Interrupts.cpp
index 5a69b1a0c3..a14db353f5 100644
--- a/Kernel/Arch/aarch64/Interrupts.cpp
+++ b/Kernel/Arch/aarch64/Interrupts.cpp
@@ -5,27 +5,132 @@
*/
#include <Kernel/Arch/Interrupts.h>
+#include <Kernel/Arch/aarch64/InterruptManagement.h>
+#include <Kernel/Interrupts/GenericInterruptHandler.h>
+#include <Kernel/Interrupts/SharedIRQHandler.h>
+#include <Kernel/Interrupts/UnhandledInterruptHandler.h>
+
+struct TrapFrame;
namespace Kernel {
-GenericInterruptHandler& get_interrupt_handler(u8)
+static Array<GenericInterruptHandler*, 64> s_interrupt_handlers;
+
+extern "C" void handle_interrupt(TrapFrame const* const);
+extern "C" void handle_interrupt(TrapFrame const* const)
+{
+ for (auto& interrupt_controller : InterruptManagement::the().controllers()) {
+ auto pending_interrupts = interrupt_controller->pending_interrupts();
+
+ // TODO: Add these interrupts as a source of entropy for randomness.
+ u8 irq = 0;
+ while (pending_interrupts) {
+ if ((pending_interrupts & 0b1) != 0b1) {
+ irq += 1;
+ pending_interrupts >>= 1;
+ continue;
+ }
+
+ // TODO: Consider not passing the RegisterState into the handle_interrupt()
+ // function, since no IRQHandler seems to be using the registers.
+ RegisterState regs {};
+
+ auto* handler = s_interrupt_handlers[irq];
+ VERIFY(handler);
+ handler->increment_invoking_counter();
+ handler->handle_interrupt(regs);
+ handler->eoi();
+
+ irq += 1;
+ pending_interrupts >>= 1;
+ }
+ }
+}
+
+// FIXME: Share the code below with Arch/x86/common/Interrupts.cpp
+// While refactoring, the interrupt handlers can also be moved into the InterruptManagement class.
+GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number)
+{
+ auto*& handler_slot = s_interrupt_handlers[interrupt_number];
+ VERIFY(handler_slot != nullptr);
+ return *handler_slot;
+}
+
+static void revert_to_unused_handler(u8 interrupt_number)
{
- VERIFY_NOT_REACHED();
+ auto handler = new UnhandledInterruptHandler(interrupt_number);
+ handler->register_interrupt_handler();
}
-void register_generic_interrupt_handler(u8, GenericInterruptHandler&)
+void register_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler)
{
- VERIFY_NOT_REACHED();
+ auto*& handler_slot = s_interrupt_handlers[interrupt_number];
+ if (handler_slot != nullptr) {
+ if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) {
+ if (handler_slot) {
+ auto* unhandled_handler = static_cast<UnhandledInterruptHandler*>(handler_slot);
+ unhandled_handler->unregister_interrupt_handler();
+ delete unhandled_handler;
+ }
+ handler_slot = &handler;
+ return;
+ }
+ if (handler_slot->is_shared_handler() && !handler_slot->is_sharing_with_others()) {
+ VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler);
+ static_cast<SharedIRQHandler*>(handler_slot)->register_handler(handler);
+ return;
+ }
+ if (!handler_slot->is_shared_handler()) {
+ if (handler_slot->type() == HandlerType::SpuriousInterruptHandler) {
+ // FIXME: Add support for spurious interrupts on aarch64
+ VERIFY_NOT_REACHED();
+ }
+ VERIFY(handler_slot->type() == HandlerType::IRQHandler);
+ auto& previous_handler = *handler_slot;
+ handler_slot = nullptr;
+ SharedIRQHandler::initialize(interrupt_number);
+ VERIFY(handler_slot);
+ static_cast<SharedIRQHandler*>(handler_slot)->register_handler(previous_handler);
+ static_cast<SharedIRQHandler*>(handler_slot)->register_handler(handler);
+ return;
+ }
+ VERIFY_NOT_REACHED();
+ } else {
+ handler_slot = &handler;
+ }
}
-void unregister_generic_interrupt_handler(u8, GenericInterruptHandler&)
+void unregister_generic_interrupt_handler(u8 interrupt_number, GenericInterruptHandler& handler)
{
- VERIFY_NOT_REACHED();
+ auto*& handler_slot = s_interrupt_handlers[interrupt_number];
+ VERIFY(handler_slot != nullptr);
+ if (handler_slot->type() == HandlerType::UnhandledInterruptHandler) {
+ return;
+ }
+ if (handler_slot->is_shared_handler() && !handler_slot->is_sharing_with_others()) {
+ VERIFY(handler_slot->type() == HandlerType::SharedIRQHandler);
+ auto* shared_handler = static_cast<SharedIRQHandler*>(handler_slot);
+ shared_handler->unregister_handler(handler);
+ if (!shared_handler->sharing_devices_count()) {
+ handler_slot = nullptr;
+ revert_to_unused_handler(interrupt_number);
+ }
+ return;
+ }
+ if (!handler_slot->is_shared_handler()) {
+ VERIFY(handler_slot->type() == HandlerType::IRQHandler);
+ handler_slot = nullptr;
+ revert_to_unused_handler(interrupt_number);
+ return;
+ }
}
void initialize_interrupts()
{
- VERIFY_NOT_REACHED();
+ for (u8 i = 0; i < s_interrupt_handlers.size(); ++i) {
+ auto* handler = new UnhandledInterruptHandler(i);
+ handler->register_interrupt_handler();
+ }
}
}
diff --git a/Kernel/Arch/aarch64/vector_table.S b/Kernel/Arch/aarch64/vector_table.S
index dde2f602c0..13e6b7d400 100644
--- a/Kernel/Arch/aarch64/vector_table.S
+++ b/Kernel/Arch/aarch64/vector_table.S
@@ -26,6 +26,7 @@
.endm
.extern exception_common
+.extern handle_interrupt
//
// Save all register states to the current stack
@@ -161,7 +162,7 @@ synchronous_current_elsp_el0:
irq_current_elsp_el0:
save_current_context
- bl exception_common
+ bl handle_interrupt
restore_previous_context
eret