aboutsummaryrefslogtreecommitdiff
path: root/kapi
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-09-30 11:43:54 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-09-30 11:43:54 +0200
commitee4096101da245a792cd4df68973585305d09087 (patch)
tree982df6acd03b7a13d24a9eae665d7905376e54b3 /kapi
parent5bad38935238da045d9fd467eae9dfd958deea34 (diff)
parenta8f2e1a0edc37cd1c06447c42ff94f3370e8a6bc (diff)
downloadkernel-ee4096101da245a792cd4df68973585305d09087.tar.xz
kernel-ee4096101da245a792cd4df68973585305d09087.zip
Merge branch 'fmorgner/implement-interrupt-locking' into 'develop'
kapi/interrupts: implement irq_lock See merge request teachos/kernel!61
Diffstat (limited to 'kapi')
-rw-r--r--kapi/kapi/interrupts.hpp21
-rw-r--r--kapi/kapi/interrupts/irq_lock.hpp163
-rw-r--r--kapi/kapi/interrupts/state.hpp26
3 files changed, 193 insertions, 17 deletions
diff --git a/kapi/kapi/interrupts.hpp b/kapi/kapi/interrupts.hpp
index 9e41b727..f5b992e2 100644
--- a/kapi/kapi/interrupts.hpp
+++ b/kapi/kapi/interrupts.hpp
@@ -1,6 +1,9 @@
#ifndef TEACHOS_KAPI_INTERRUPTS_HPP
#define TEACHOS_KAPI_INTERRUPTS_HPP
+#include <kapi/interrupts/irq_lock.hpp>
+#include <kapi/interrupts/state.hpp>
+
#include <kstd/memory.hpp>
#include <cstdint>
@@ -8,9 +11,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 +36,7 @@ namespace kapi::interrupts
virtual auto handle_interrupt(std::uint32_t irq_number, kstd::weak_ptr<void> context) -> status = 0;
};
- //! @}
-
- //! @addtogroup kapi-interrupts-kernel-defined
+ //! @name Kernel-defined API
//! @{
//! Register an interrupt handler for the given IRQ number.
@@ -62,17 +60,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/irq_lock.hpp b/kapi/kapi/interrupts/irq_lock.hpp
new file mode 100644
index 00000000..72a9a564
--- /dev/null
+++ b/kapi/kapi/interrupts/irq_lock.hpp
@@ -0,0 +1,163 @@
+#ifndef TEACHOS_KAPI_INTERRUPTS_IRQ_LOCK_HPP
+#define TEACHOS_KAPI_INTERRUPTS_IRQ_LOCK_HPP
+
+// IWYU pragma: private, include <kapi/interrupts.hpp>
+
+#include <kapi/cpu.hpp>
+#include <kapi/interrupts/state.hpp>
+#include <kapi/system.hpp>
+
+#include <kstd/mutex.hpp>
+
+namespace kapi::interrupts
+{
+
+ template<typename = void>
+ struct irq_lock;
+
+ //! @name Kernel-defined API
+ //! @{
+
+ //! A lock to temporarily disable IRQs.
+ template<>
+ struct irq_lock<void>
+ {
+ //! 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()}
+ {}
+
+ constexpr irq_lock(irq_lock const &) = delete;
+ constexpr irq_lock(irq_lock &&) = delete;
+
+ //! 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)
+ {
+ 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
+ {
+ 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
+ {
+ 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.
+ template<kstd::basic_lockable BasicLockable>
+ struct irq_lock<BasicLockable> : irq_lock<void>
+ {
+ 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<BasicLockable>)
+ {
+ if (!irq_lock<>::try_lock())
+ {
+ return false;
+ }
+
+ if (m_lockable.try_lock())
+ {
+ return true;
+ }
+
+ irq_lock<>::unlock();
+ return false;
+ }
+
+ private:
+ lockable_type & m_lockable;
+ };
+
+ //! @}
+
+ irq_lock() -> irq_lock<void>;
+
+ template<kstd::basic_lockable Lockable>
+ irq_lock(Lockable &) -> irq_lock<Lockable>;
+
+} // 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 <kapi/interrupts.hpp>
+
+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