From e9b880648c18d3680ae10cc5660f0dbe981af5d8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 10:41:36 +0200 Subject: kapi/interrupt: rework enablement API --- kapi/kapi/interrupts.hpp | 19 ++----------------- kapi/kapi/interrupts/state.hpp | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 kapi/kapi/interrupts/state.hpp (limited to 'kapi') diff --git a/kapi/kapi/interrupts.hpp b/kapi/kapi/interrupts.hpp index 9e41b727..f9ff22f9 100644 --- a/kapi/kapi/interrupts.hpp +++ b/kapi/kapi/interrupts.hpp @@ -1,5 +1,6 @@ #ifndef TEACHOS_KAPI_INTERRUPTS_HPP #define TEACHOS_KAPI_INTERRUPTS_HPP +#include #include @@ -8,9 +9,6 @@ namespace kapi::interrupts { - //! @addtogroup kapi-interrupts - //! @{ - //! A status that indicates whether an interrupt was handled by a handler. enum struct status : bool { @@ -36,9 +34,7 @@ namespace kapi::interrupts virtual auto handle_interrupt(std::uint32_t irq_number, kstd::weak_ptr context) -> status = 0; }; - //! @} - - //! @addtogroup kapi-interrupts-kernel-defined + //! @name Kernel-defined API //! @{ //! Register an interrupt handler for the given IRQ number. @@ -62,17 +58,6 @@ namespace kapi::interrupts //! @} - //! @addtogroup kapi-interrupts-platform-defined - //! @{ - - //! Enable external interrupts. - auto enable() -> void; - - //! Disable external interrupts. - auto disable() -> void; - - //! @} - } // namespace kapi::interrupts #endif \ No newline at end of file diff --git a/kapi/kapi/interrupts/state.hpp b/kapi/kapi/interrupts/state.hpp new file mode 100644 index 00000000..a33a871b --- /dev/null +++ b/kapi/kapi/interrupts/state.hpp @@ -0,0 +1,26 @@ +#ifndef TEACHOS_KAPI_INTERRUPTS_STATE_HPP +#define TEACHOS_KAPI_INTERRUPTS_STATE_HPP + +// IWYU pragma: private, include + +namespace kapi::interrupts +{ + + //! @name Platform-defined API + //! @{ + + //! Check if interrupts are currently enabled or not. + //! + //! @return @p true iff. interrupts are currently enabled, @p false otherwise. + auto enabled() -> bool; + + //! Enable or disable interrupts. + //! + //! @param value @p true if interrupts shall be enabled, @p false otherwise. + auto enabled(bool value) -> void; + + //! @} + +} // namespace kapi::interrupts + +#endif \ No newline at end of file -- cgit v1.2.3 From c35fe2ecfe1eba2911d155ad5ade41ca18a765ff Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 10:43:50 +0200 Subject: kapi/interrupts: implement IRQ lock --- kapi/kapi/interrupts.hpp | 2 + kapi/kapi/interrupts/irq_lock.hpp | 108 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 kapi/kapi/interrupts/irq_lock.hpp (limited to 'kapi') diff --git a/kapi/kapi/interrupts.hpp b/kapi/kapi/interrupts.hpp index f9ff22f9..f5b992e2 100644 --- a/kapi/kapi/interrupts.hpp +++ b/kapi/kapi/interrupts.hpp @@ -1,5 +1,7 @@ #ifndef TEACHOS_KAPI_INTERRUPTS_HPP #define TEACHOS_KAPI_INTERRUPTS_HPP + +#include #include #include diff --git a/kapi/kapi/interrupts/irq_lock.hpp b/kapi/kapi/interrupts/irq_lock.hpp new file mode 100644 index 00000000..1b6f6987 --- /dev/null +++ b/kapi/kapi/interrupts/irq_lock.hpp @@ -0,0 +1,108 @@ +#ifndef TEACHOS_KAPI_INTERRUPTS_IRQ_LOCK_HPP +#define TEACHOS_KAPI_INTERRUPTS_IRQ_LOCK_HPP + +// IWYU pragma: private, include + +#include + +#include + +namespace kapi::interrupts +{ + + template + struct irq_lock; + + //! @name Kernel-defined API + //! @{ + + //! A lock to temporarily disable IRQs. + template<> + struct irq_lock + { + constexpr irq_lock() = default; + constexpr irq_lock(irq_lock const &) = delete; + constexpr irq_lock(irq_lock &&) = delete; + + constexpr auto operator=(irq_lock const &) = delete; + constexpr auto operator=(irq_lock &&) = delete; + + //! Save the current IRQ state and disable further IRQs. + auto lock() -> void + { + m_old_irq_state = enabled(); + enabled(false); + } + + //! Restore the previously saved IRQ state. + auto unlock() -> void + { + enabled(m_old_irq_state); + } + + //! Save the current IRQ state and disable further IRQs. + auto try_lock() -> bool + { + lock(); + return true; + } + + private: + bool m_old_irq_state{}; + }; + + //! A lock to temporarily disable IRQs while also acquiring a further lock. + template + struct irq_lock : irq_lock + { + using lockable_type = BasicLockable; + + explicit irq_lock(BasicLockable & lockable) + : m_lockable{lockable} + {} + + //! Save the current IRQ state, disable further IRQs, and lock this lock's lockable object. + auto lock() -> void + { + irq_lock<>::lock(); + m_lockable.lock(); + } + + //! Unlock this lock's lockable objects and restore the previously saved IRQ state. + auto unlock() -> void + { + m_lockable.unlock(); + irq_lock<>::unlock(); + } + + //! Save the current IRQ state, disable further IRQs, and attempt to lock this lock's lockable object. + //! + //! This function will restore the previous IRQ state if this lock's lockable object cannot be locked. + //! + //! @return @p true iff. this lock's lockable object could successfully be locked, @p false otherwise. + [[nodiscard]] auto try_lock() -> bool + requires(kstd::lockable) + { + irq_lock<>::lock(); + if (m_lockable.try_lock()) + { + return true; + } + irq_lock<>::unlock(); + return false; + } + + private: + lockable_type & m_lockable; + }; + + //! @} + + irq_lock() -> irq_lock; + + template + irq_lock(Lockable &) -> irq_lock; + +} // namespace kapi::interrupts + +#endif \ No newline at end of file -- cgit v1.2.3 From 97d99228466b742133bf12e00197be9b11fcc561 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 11:57:55 +0200 Subject: kapi/interrupts: improve irq_lock protections The previous implementation suffered from four problems: 1. try_lock in the basic irq_lock always succeeded. 2. lock in the basic irq_lock always succeeded. 3. try_lock in the wrapper irq_lock did not use the base try_lock. 4. a foreign CPU could wrongly unlock a remote irq_lock. These issues are mitigated in this patch. --- kapi/kapi/interrupts/irq_lock.hpp | 58 +++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) (limited to 'kapi') diff --git a/kapi/kapi/interrupts/irq_lock.hpp b/kapi/kapi/interrupts/irq_lock.hpp index 1b6f6987..57c0ef7f 100644 --- a/kapi/kapi/interrupts/irq_lock.hpp +++ b/kapi/kapi/interrupts/irq_lock.hpp @@ -3,7 +3,9 @@ // IWYU pragma: private, include +#include #include +#include #include @@ -20,35 +22,76 @@ namespace kapi::interrupts template<> struct irq_lock { - constexpr irq_lock() = default; + constexpr irq_lock() + : m_owner{kapi::cpu::current_id()} + {} + constexpr irq_lock(irq_lock const &) = delete; constexpr irq_lock(irq_lock &&) = delete; + ~irq_lock() + { + assert_owner(); + if (m_locked) + { + kapi::system::panic("[OS:INT] IRQ lock destroyed while it is still locked!"); + } + }; + constexpr auto operator=(irq_lock const &) = delete; constexpr auto operator=(irq_lock &&) = delete; //! Save the current IRQ state and disable further IRQs. auto lock() -> void { - m_old_irq_state = enabled(); - enabled(false); + if (!try_lock()) + { + kapi::system::panic("[OS:INT] IRQ lock reacquired while it is already held!"); + } } //! Restore the previously saved IRQ state. auto unlock() -> void { + assert_owner(); + + if (!m_locked) + { + kapi::system::panic("[OS:INT] IRQ lock unlocked while it is not being held!"); + } + enabled(m_old_irq_state); + m_locked = false; } //! Save the current IRQ state and disable further IRQs. auto try_lock() -> bool { - lock(); + assert_owner(); + auto old_state = enabled(); + enabled(false); + if (m_locked) + { + enabled(old_state); + return false; + } + m_locked = true; + m_old_irq_state = old_state; return true; } private: + auto assert_owner() -> void + { + if (m_owner != kapi::cpu::current_id()) + { + kapi::system::panic("[OS:INT] Tried to modify IRQ lock on a CPU that does not own it!"); + } + } + + bool m_locked{}; bool m_old_irq_state{}; + kapi::cpu::id m_owner{}; }; //! A lock to temporarily disable IRQs while also acquiring a further lock. @@ -83,11 +126,16 @@ namespace kapi::interrupts [[nodiscard]] auto try_lock() -> bool requires(kstd::lockable) { - irq_lock<>::lock(); + if (!irq_lock<>::try_lock()) + { + return false; + } + if (m_lockable.try_lock()) { return true; } + irq_lock<>::unlock(); return false; } -- cgit v1.2.3 From e259f732d9bf38e390588f07884c31ece95f5b7b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 29 Sep 2026 12:37:26 +0200 Subject: kapi/interrupts: admit irq_lock dtor may panic --- kapi/kapi/interrupts/irq_lock.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'kapi') diff --git a/kapi/kapi/interrupts/irq_lock.hpp b/kapi/kapi/interrupts/irq_lock.hpp index 57c0ef7f..72a9a564 100644 --- a/kapi/kapi/interrupts/irq_lock.hpp +++ b/kapi/kapi/interrupts/irq_lock.hpp @@ -22,6 +22,9 @@ namespace kapi::interrupts template<> struct irq_lock { + //! Create a new IRQ lock + //! + //! This function records the creator CPU of the new lock to ensure instances are not passed across cores. constexpr irq_lock() : m_owner{kapi::cpu::current_id()} {} @@ -29,7 +32,11 @@ namespace kapi::interrupts constexpr irq_lock(irq_lock const &) = delete; constexpr irq_lock(irq_lock &&) = delete; - ~irq_lock() + //! Destroy this IRQ lock. + //! + //! @warning This functions panics if the CPU executing is not the creator of this lock, or the lock is still + //! locked. + ~irq_lock() noexcept(false) { assert_owner(); if (m_locked) -- cgit v1.2.3