From 78dc8765facbbdd5f20ad0a9afff7405497b99b9 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 19 Sep 2025 09:27:29 -0700 Subject: [PATCH] feat(hal-core): sketching irq mutex stuff --- Cargo.lock | 1 + hal-core/Cargo.toml | 1 + hal-core/src/interrupt.rs | 23 +++++++ hal-core/src/interrupt/lock.rs | 116 +++++++++++++++++++++++++++++++++ src/tests.rs | 12 ++-- 5 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 hal-core/src/interrupt/lock.rs diff --git a/Cargo.lock b/Cargo.lock index 2a1ab9f8..4494e7c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -826,6 +826,7 @@ dependencies = [ "embedded-graphics-core", "maitake-sync", "mycelium-util", + "portable-atomic", "thiserror 2.0.9", "tracing 0.2.0", ] diff --git a/hal-core/Cargo.toml b/hal-core/Cargo.toml index 0560d8de..3b529675 100644 --- a/hal-core/Cargo.toml +++ b/hal-core/Cargo.toml @@ -14,3 +14,4 @@ thiserror = { workspace = true } maitake-sync = { path = "../maitake-sync", default-features = false } mycelium-util = { path = "../util" } embedded-graphics-core = { version = "0.3", optional = true } +portable-atomic = "1.2" diff --git a/hal-core/src/interrupt.rs b/hal-core/src/interrupt.rs index d14aca55..b3d01df4 100644 --- a/hal-core/src/interrupt.rs +++ b/hal-core/src/interrupt.rs @@ -1,7 +1,9 @@ use core::fmt; pub mod ctx; +mod lock; pub use self::ctx::Context; +pub use self::lock::IrqRawMutex; /// An interrupt controller for a platform. pub trait Control { @@ -40,6 +42,27 @@ pub trait Control { } } +pub trait MaskInterrupt { + /// Mask the interrupt on vector `V`. + unsafe fn mask_irq(&self, vector: V); + + /// Unmask the interrupt on vector `V`. + unsafe fn unmask_irq(&self, vector: V); +} + +impl MaskInterrupt for &T +where + T: MaskInterrupt, +{ + unsafe fn mask_irq(&self, vector: V) { + (*self).mask_irq(vector); + } + + unsafe fn unmask_irq(&self, vector: V) { + (*self).unmask_irq(vector); + } +} + pub trait Handlers { fn page_fault(cx: C) where diff --git a/hal-core/src/interrupt/lock.rs b/hal-core/src/interrupt/lock.rs new file mode 100644 index 00000000..4f9e5479 --- /dev/null +++ b/hal-core/src/interrupt/lock.rs @@ -0,0 +1,116 @@ +use super::MaskInterrupt; +use maitake_sync::{blocking::RawMutex, spin::Spinlock}; +use portable_atomic::{AtomicBool, Ordering}; + +/// A spinlock that also provides mutual exclusion against the interrupt handler +/// for a particular interrupt vector. +/// +/// While this spinlock is locked, the interrupt vector is masked, preventing +/// the interrupt handler from running. The interrupt is unmasked when the +/// spinlock is unlocked. +/// +/// This type requires exclusive control over the masking and unmasking of that +/// interrupt vector. +#[derive(Debug)] +pub struct IrqSpinlock { + lock: Spinlock, + ctrl: I, + vector: V, +} + +pub struct IrqSpinlockTable { + locks: [AtomicBool; VECTORS], + _v: core::marker::PhantomData, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ClaimLockError { + AlreadyClaimed, + NoSuchIrq, +} + +impl IrqSpinlock +where + I: MaskInterrupt, + V: Copy, +{ + /// # Safety + /// + /// Constructing an `IrqSpinlock` for a given interrupt vector requires + /// exclusive control over the masking and unmasking of that interrupt + /// vector. + pub unsafe fn new(ctrl: I, vector: V) -> Self { + Self { + lock: Spinlock::new(), + ctrl, + vector, + } + } +} + +unsafe impl RawMutex for IrqSpinlock +where + I: MaskInterrupt, + V: Copy, +{ + type GuardMarker = (); + + fn lock(&self) { + self.lock.lock(); + unsafe { + // Safety: having locked the spinlock means we have exclusive access + // to that vector. + self.ctrl.mask_irq(self.vector) + }; + } + + fn try_lock(&self) -> bool { + if !self.lock.try_lock() { + return false; + } + + unsafe { + // Safety: having locked the spinlock means we have exclusive access + // to that vector. + self.ctrl.mask_irq(self.vector) + }; + true + } + + unsafe fn unlock(&self) { + // Safety: the contract of `RawMutex::unlock` requires that the caller + // ensure that the mutex is locked, so this implies that we have + // exclusive access to that vector in the interrupt controller. + self.ctrl.unmask_irq(self.vector); + self.lock.unlock(); + } + + /// Returns `true` if the mutex is currently locked. + fn is_locked(&self) -> bool { + self.lock.is_locked() + } +} + +impl IrqSpinlockTable +where + V: Into + Copy, +{ + pub fn claim_lock(&self, vector: V, ctrl: I) -> Result, ClaimLockError> + where + I: MaskInterrupt, + { + let claimed = self + .locks + .get(vector.into()) + .ok_or(ClaimLockError::NoSuchIrq)?; + claimed + .compare_exchange(true, false, Ordering::AcqRel, Ordering::Acquire) + .map_err(|_| ClaimLockError::AlreadyClaimed)?; + Ok(unsafe { + // Safety: we have just claimed the vector, so unless the interrupt + // controller hands out access to it through other mechanisms, we + // have exclusive access to it. + IrqSpinlock::new(ctrl, vector) + }) + } +} diff --git a/src/tests.rs b/src/tests.rs index 447c1496..4a709a0e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -28,13 +28,13 @@ mod alloc { mycotest::decl_test! { fn alloc_big() { use alloc::vec::Vec; - let mut v = Vec::new(); - - for i in 0..2048 { - v.push(i); - } + // let mut v = Vec::new(); + panic!("lol"); + // for i in 0..2048 { + // v.push(i); + // } - tracing::info!(vec = ?v); + // tracing::info!(vec = ?v); } } }