Skip to main content

mik32_runtime/
interrupt.rs

1//! Machine interrupt control for MIK32.
2//!
3//! The runtime disables and restores the global machine interrupt enable bit
4//! (`mstatus.MIE`) together with machine external interrupts (`mie.MEIE`).
5//! This matches the interrupt model used by MIK32 applications.
6
7use core::arch::asm;
8
9const MSTATUS_MIE: usize = 1 << 3;
10const MIE_MEIE: usize = 1 << 11;
11
12/// Previous interrupt state returned by [`disable`].
13///
14/// Keep this value and pass it back to [`restore`] when leaving a manual
15/// critical section.
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17#[repr(transparent)]
18pub struct RestoreState {
19    bits: usize,
20}
21
22impl RestoreState {
23    /// Returns `true` if global machine interrupts were enabled.
24    #[inline]
25    pub const fn machine_interrupts_enabled(self) -> bool {
26        self.bits & MSTATUS_MIE != 0
27    }
28
29    /// Returns `true` if machine external interrupts were enabled.
30    #[inline]
31    pub const fn machine_external_interrupt_enabled(self) -> bool {
32        self.bits & MIE_MEIE != 0
33    }
34
35    #[inline]
36    const fn from_raw_parts(mstatus: usize, mie: usize) -> Self {
37        Self {
38            bits: (mstatus & MSTATUS_MIE) | (mie & MIE_MEIE),
39        }
40    }
41
42    #[inline]
43    #[cfg(feature = "critical-section")]
44    const fn bits(self) -> usize {
45        self.bits
46    }
47
48    #[inline]
49    #[cfg(feature = "critical-section")]
50    const unsafe fn from_bits(bits: usize) -> Self {
51        Self {
52            bits: bits & (MSTATUS_MIE | MIE_MEIE),
53        }
54    }
55}
56
57/// Disables global machine interrupts and machine external interrupts.
58///
59/// Returns the previous state, which can later be passed to [`restore`].
60#[inline]
61pub fn disable() -> RestoreState {
62    let mstatus: usize;
63    let mie: usize;
64
65    // SAFETY: CSR access is a single-hart machine-mode operation. The function
66    // only clears interrupt-enable bits and returns the previous values.
67    unsafe {
68        asm!(
69            "csrrc {mstatus}, mstatus, {mstatus_mie}",
70            "csrrc {mie}, mie, {mie_meie}",
71            mstatus = out(reg) mstatus,
72            mie = out(reg) mie,
73            mstatus_mie = in(reg) MSTATUS_MIE,
74            mie_meie = in(reg) MIE_MEIE,
75            options(nostack),
76        );
77    }
78
79    RestoreState::from_raw_parts(mstatus, mie)
80}
81
82/// Enables global machine interrupts and machine external interrupts.
83///
84/// # Safety
85///
86/// Enabling interrupts can resume interrupt handlers that access shared state.
87/// Call this only when doing so cannot violate aliasing or critical-section
88/// invariants.
89#[inline]
90pub unsafe fn enable() {
91    // SAFETY: The caller guarantees that enabling interrupts is sound for the
92    // current program state.
93    unsafe {
94        asm!(
95            "csrrs zero, mie, {mie_meie}",
96            "csrrs zero, mstatus, {mstatus_mie}",
97            mstatus_mie = in(reg) MSTATUS_MIE,
98            mie_meie = in(reg) MIE_MEIE,
99            options(nostack),
100        );
101    }
102}
103
104/// Restores the interrupt state returned by [`disable`].
105///
106/// # Safety
107///
108/// `state` must be the value returned by a matching [`disable`] call for the
109/// current critical section. Restoring an unrelated state may enable interrupts
110/// while protected data is still borrowed.
111#[inline]
112pub unsafe fn restore(state: RestoreState) {
113    if state.machine_external_interrupt_enabled() {
114        // SAFETY: The caller guarantees that restoring this state is sound.
115        unsafe {
116            asm!(
117                "csrrs zero, mie, {mask}",
118                mask = in(reg) MIE_MEIE,
119                options(nostack),
120            );
121        }
122    } else {
123        // SAFETY: Clearing an interrupt-enable bit is safe for memory safety.
124        unsafe {
125            asm!(
126                "csrrc zero, mie, {mask}",
127                mask = in(reg) MIE_MEIE,
128                options(nostack),
129            );
130        }
131    }
132
133    if state.machine_interrupts_enabled() {
134        // SAFETY: The caller guarantees that restoring this state is sound.
135        unsafe {
136            asm!(
137                "csrrs zero, mstatus, {mask}",
138                mask = in(reg) MSTATUS_MIE,
139                options(nostack),
140            );
141        }
142    } else {
143        // SAFETY: Clearing an interrupt-enable bit is safe for memory safety.
144        unsafe {
145            asm!(
146                "csrrc zero, mstatus, {mask}",
147                mask = in(reg) MSTATUS_MIE,
148                options(nostack),
149            );
150        }
151    }
152}
153
154/// Runs `f` with global machine interrupts and machine external interrupts
155/// disabled, then restores the previous state.
156#[inline]
157pub fn free<R>(f: impl FnOnce() -> R) -> R {
158    let state = disable();
159    let result = f();
160
161    // SAFETY: `state` was obtained from the matching `disable` call above.
162    unsafe {
163        restore(state);
164    }
165
166    result
167}
168
169#[cfg(feature = "critical-section")]
170mod critical_section_impl {
171    use super::{RestoreState, disable, restore};
172    use critical_section::{Impl, RawRestoreState, set_impl};
173
174    struct SingleHartCriticalSection;
175
176    set_impl!(SingleHartCriticalSection);
177
178    unsafe impl Impl for SingleHartCriticalSection {
179        unsafe fn acquire() -> RawRestoreState {
180            disable().bits()
181        }
182
183        unsafe fn release(state: RawRestoreState) {
184            // SAFETY: `state` is the raw value returned by `acquire`.
185            unsafe {
186                restore(RestoreState::from_bits(state));
187            }
188        }
189    }
190}