Skip to main content

strat9_kernel/sync/
irq.rs

1use crate::arch::x86_64;
2
3/// Typed proof that IRQs are masked on the current CPU.
4///
5/// The memory allocator consumes this token to prevent, at compile time,
6/// calls from contexts where an interrupt could re-enter the same lock
7/// and cause a deadlock.
8///
9/// Intentionally non-`Copy` and non-`Clone`: the token must not escape
10/// the IRQ-off context in which it was created.
11///
12/// # Creation paths
13///
14/// | Path | Visibility | Use case |
15/// |------|-----------|----------|
16/// | `verify()` | `pub` | Check RFLAGS; returns `Some` if IRQs already disabled |
17/// | `with_irqs_disabled()` | `pub` | Safe wrapper: disables IRQs, runs closure, restores |
18/// | `IrqDisabled::enter()` | `pub(super)` | Guardian: called by SpinLock on acquire |
19/// | `token_from_trusted_context()` | `pub(crate)` | Trait impls (e.g. `X86FrameAllocator`) that can't accept a token parameter |
20/// | `new_unchecked()` | `pub(super)` | Internal to `sync` module only; never call directly |
21#[derive(Debug)]
22pub struct IrqDisabledToken(());
23
24impl IrqDisabledToken {
25    /// Check the current interrupt state and return proof if IRQs are already disabled.
26    #[inline]
27    pub fn verify() -> Option<Self> {
28        if x86_64::interrupts_enabled() {
29            None
30        } else {
31            Some(Self(()))
32        }
33    }
34
35    /// Build the proof without re-checking `RFLAGS`.
36    ///
37    /// **Restricted to `pub(super)`**: only visible within the `sync` module.
38    /// External code must use `verify()`, `with_irqs_disabled()`, or
39    /// `token_from_trusted_context()` instead.
40    ///
41    /// # Safety
42    ///
43    /// The caller must guarantee that IRQs are indeed disabled on the current
44    /// CPU for the entire logical validity of the token.
45    #[inline]
46    pub(super) unsafe fn new_unchecked() -> Self {
47        Self(())
48    }
49
50    /// Create a token when the caller guarantees that IRQs are already disabled.
51    ///
52    /// **Purpose:** Implementing external traits (e.g. `X86FrameAllocator`)
53    /// whose signature cannot accept a token parameter.
54    ///
55    /// # Safety
56    ///
57    /// The caller must guarantee that IRQs are disabled on the current CPU.
58    /// This is a `pub(crate)` escape hatch : prefer `verify()` or
59    /// `with_irqs_disabled()` for all other use cases.
60    #[inline]
61    pub(crate) unsafe fn token_from_trusted_context() -> Self {
62        Self::new_unchecked()
63    }
64}
65
66/// Execute a closure with IRQs disabled, providing an `IrqDisabledToken` as proof.
67///
68/// Saves and disables IRQs before calling `f`, then restores the previous flag state.
69#[inline]
70pub fn with_irqs_disabled<R>(f: impl FnOnce(&IrqDisabledToken) -> R) -> R {
71    let saved = crate::arch::x86_64::save_flags_and_cli();
72    // SAFETY: save_flags_and_cli() has just disabled interrupts on this CPU;
73    // the token is dropped before restore_flags() re-enables them.
74    let token = unsafe { IrqDisabledToken::new_unchecked() };
75    let result = f(&token);
76    crate::arch::x86_64::restore_flags(saved);
77    result
78}