summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--na/include/lume/irq.h84
-rw-r--r--na/kernel/irq.cc106
2 files changed, 190 insertions, 0 deletions
diff --git a/na/include/lume/irq.h b/na/include/lume/irq.h
new file mode 100644
index 0000000..f5f34e4
--- /dev/null
+++ b/na/include/lume/irq.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+#pragma once
+
+/*
+ * IRQ Manager — Dynamic Interrupt Dispatch
+ *
+ * Drivers register ISR callbacks at init time via register_handler().
+ * The MI trap layer calls dispatch() when an external interrupt arrives.
+ * dispatch() runs in interrupt context — handlers must be short and
+ * non-blocking.
+ *
+ * Lock contract:
+ * - lock_ protects register/unregister (cold path).
+ * - dispatch() is lock-free: handler pointers are published with RELEASE
+ * semantics and read with ACQUIRE in dispatch().
+ * - Handlers must not acquire VmSpace::lock or sleep.
+ *
+ * Reference: docs/specs/trap.md §3.3
+ */
+
+#include <lume/atomic.h>
+#include <lume/types.h>
+
+/* IRQ handler callback signature */
+using IrqHandler = void (*)(uint32 irq, void *arg);
+
+/* Abstract Interrupt Controller Interface */
+struct IrqChip {
+ const char* name;
+ uint32 (*claim)();
+ void (*complete)(uint32 irq);
+ void (*init_ap)();
+ void (*enable)(uint32 irq);
+ void (*disable)(uint32 irq);
+ void (*set_priority)(uint32 irq, uint32 priority);
+};
+
+class IrqManager {
+public:
+ void init();
+
+ /* Register an ISR for the given IRQ number.
+ * Returns 0 on success, -EINVAL if irq out of range, -EBUSY if already
+ * registered. */
+ int register_handler(uint32 irq, IrqHandler handler, void *arg);
+
+ /* Unregister the ISR for the given IRQ number. */
+ void unregister_handler(uint32 irq);
+
+ /* Dispatch an interrupt to the registered handler.
+ * Called from MI trap layer in interrupt context. Lock-free. */
+ void dispatch(uint32 irq);
+
+ /* Set the active interrupt controller chip */
+ void set_chip(const IrqChip* chip);
+ const IrqChip* get_chip() const { return chip_; }
+
+ /* Architecture-independent claim and complete for the active IRQ chip */
+ uint32 claim() const;
+ void complete(uint32 irq) const;
+ void init_ap() const;
+ void enable(uint32 irq) const;
+ void disable(uint32 irq) const;
+ void set_priority(uint32 irq, uint32 priority) const;
+
+ /* Check if a handler is registered for the given IRQ. */
+ bool has_handler(uint32 irq) const;
+
+ static constexpr uint32 kMaxIrqs = 64;
+
+private:
+ struct IrqEntry {
+ lume::atomic<uint64> handler_ptr; // Stored as uint64 for atomic access
+ void *arg = nullptr;
+ };
+
+ const IrqChip* chip_ = nullptr;
+ IrqEntry entries_[kMaxIrqs];
+};
+
+/* Global IRQ manager singleton */
+extern IrqManager g_irq_manager;
diff --git a/na/kernel/irq.cc b/na/kernel/irq.cc
new file mode 100644
index 0000000..4de468a
--- /dev/null
+++ b/na/kernel/irq.cc
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+/*
+ * IRQ Manager Implementation
+ *
+ * Lock-free dispatch path for interrupt context safety.
+ * Registration/unregistration is protected by atomic publish.
+ *
+ * Reference: docs/specs/trap.md §3.3
+ */
+
+#include <lume/console.h>
+#include <lume/errno.h>
+#include <lume/irq.h>
+
+/* Global singleton */
+IrqManager g_irq_manager;
+
+void IrqManager::init() {
+ for (uint32 i = 0; i < kMaxIrqs; i++) {
+ entries_[i].handler_ptr.store(0, __ATOMIC_RELAXED);
+ entries_[i].arg = nullptr;
+ }
+}
+
+int IrqManager::register_handler(uint32 irq, IrqHandler handler, void *arg) {
+ if (irq >= kMaxIrqs)
+ return -EINVAL;
+
+ if (!handler)
+ return -EINVAL;
+
+ /* Check if already registered (CAS from 0 to handler) */
+ uint64 expected = 0;
+ uint64 desired = reinterpret_cast<uint64>(handler);
+
+ /* Set arg first (before publishing handler) so dispatch sees valid arg */
+ entries_[irq].arg = arg;
+
+ if (!entries_[irq].handler_ptr.compare_exchange(expected, desired,
+ __ATOMIC_ACQ_REL)) {
+ /* Already registered by someone else */
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void IrqManager::unregister_handler(uint32 irq) {
+ if (irq >= kMaxIrqs)
+ return;
+
+ /* Clear handler with release semantics */
+ entries_[irq].handler_ptr.store(0, __ATOMIC_RELEASE);
+ entries_[irq].arg = nullptr;
+}
+
+void IrqManager::dispatch(uint32 irq) {
+ if (irq >= kMaxIrqs)
+ return;
+
+ /* Load handler with acquire semantics — pairs with register's release */
+ uint64 h = entries_[irq].handler_ptr.load(__ATOMIC_ACQUIRE);
+ if (h == 0) {
+ /* Spurious or unregistered IRQ — silently ignore */
+ return;
+ }
+
+ auto handler = reinterpret_cast<IrqHandler>(h);
+ handler(irq, entries_[irq].arg);
+}
+
+bool IrqManager::has_handler(uint32 irq) const {
+ if (irq >= kMaxIrqs)
+ return false;
+ return entries_[irq].handler_ptr.load(__ATOMIC_ACQUIRE) != 0;
+}
+
+void IrqManager::set_chip(const IrqChip* chip) {
+ chip_ = chip;
+}
+
+uint32 IrqManager::claim() const {
+ return chip_ ? chip_->claim() : 0;
+}
+
+void IrqManager::complete(uint32 irq) const {
+ if (chip_) chip_->complete(irq);
+}
+
+void IrqManager::init_ap() const {
+ if (chip_ && chip_->init_ap) chip_->init_ap();
+}
+
+void IrqManager::enable(uint32 irq) const {
+ if (chip_ && chip_->enable) chip_->enable(irq);
+}
+
+void IrqManager::disable(uint32 irq) const {
+ if (chip_ && chip_->disable) chip_->disable(irq);
+}
+
+void IrqManager::set_priority(uint32 irq, uint32 priority) const {
+ if (chip_ && chip_->set_priority) chip_->set_priority(irq, priority);
+}