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}