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}
43
44/// Disables global machine interrupts and machine external interrupts.
45///
46/// Returns the previous state, which can later be passed to [`restore`].
47#[inline]
48pub fn disable() -> RestoreState {
49    let mstatus: usize;
50    let mie: usize;
51
52    // SAFETY: CSR access is a single-hart machine-mode operation. The function
53    // only clears interrupt-enable bits and returns the previous values.
54    unsafe {
55        asm!(
56            "csrrc {mstatus}, mstatus, {mstatus_mie}",
57            "csrrc {mie}, mie, {mie_meie}",
58            mstatus = out(reg) mstatus,
59            mie = out(reg) mie,
60            mstatus_mie = in(reg) MSTATUS_MIE,
61            mie_meie = in(reg) MIE_MEIE,
62            options(nostack),
63        );
64    }
65
66    RestoreState::from_raw_parts(mstatus, mie)
67}
68
69/// Enables global machine interrupts and machine external interrupts.
70///
71/// # Safety
72///
73/// Enabling interrupts can resume interrupt handlers that access shared state.
74/// Call this only when doing so cannot violate aliasing or critical-section
75/// invariants.
76#[inline]
77pub unsafe fn enable() {
78    // SAFETY: The caller guarantees that enabling interrupts is sound for the
79    // current program state.
80    unsafe {
81        asm!(
82            "csrrs zero, mie, {mie_meie}",
83            "csrrs zero, mstatus, {mstatus_mie}",
84            mstatus_mie = in(reg) MSTATUS_MIE,
85            mie_meie = in(reg) MIE_MEIE,
86            options(nostack),
87        );
88    }
89}
90
91/// Restores the interrupt state returned by [`disable`].
92///
93/// # Safety
94///
95/// `state` must be the value returned by a matching [`disable`] call for the
96/// current critical section. Restoring an unrelated state may enable interrupts
97/// while protected data is still borrowed.
98#[inline]
99pub unsafe fn restore(state: RestoreState) {
100    if state.machine_external_interrupt_enabled() {
101        // SAFETY: The caller guarantees that restoring this state is sound.
102        unsafe {
103            asm!(
104                "csrrs zero, mie, {mask}",
105                mask = in(reg) MIE_MEIE,
106                options(nostack),
107            );
108        }
109    } else {
110        // SAFETY: Clearing an interrupt-enable bit is safe for memory safety.
111        unsafe {
112            asm!(
113                "csrrc zero, mie, {mask}",
114                mask = in(reg) MIE_MEIE,
115                options(nostack),
116            );
117        }
118    }
119
120    if state.machine_interrupts_enabled() {
121        // SAFETY: The caller guarantees that restoring this state is sound.
122        unsafe {
123            asm!(
124                "csrrs zero, mstatus, {mask}",
125                mask = in(reg) MSTATUS_MIE,
126                options(nostack),
127            );
128        }
129    } else {
130        // SAFETY: Clearing an interrupt-enable bit is safe for memory safety.
131        unsafe {
132            asm!(
133                "csrrc zero, mstatus, {mask}",
134                mask = in(reg) MSTATUS_MIE,
135                options(nostack),
136            );
137        }
138    }
139}
140
141/// Runs `f` with global machine interrupts and machine external interrupts
142/// disabled, then restores the previous state.
143#[inline]
144pub fn free<R>(f: impl FnOnce() -> R) -> R {
145    let state = disable();
146    let result = f();
147
148    // SAFETY: `state` was obtained from the matching `disable` call above.
149    unsafe {
150        restore(state);
151    }
152
153    result
154}
155
156#[cfg(feature = "critical-section")]
157mod critical_section_impl {
158    use super::MSTATUS_MIE;
159    use core::arch::asm;
160    use critical_section::{Impl, RawRestoreState, set_impl};
161
162    struct SingleHartCriticalSection;
163
164    set_impl!(SingleHartCriticalSection);
165
166    #[inline]
167    fn disable_global_interrupts() -> bool {
168        let mstatus: usize;
169
170        // SAFETY: CSR access is a single-hart machine-mode operation. Clearing
171        // mstatus.MIE masks interrupt handlers without changing mie.MEIE.
172        unsafe {
173            asm!(
174                "csrrc {mstatus}, mstatus, {mask}",
175                mstatus = out(reg) mstatus,
176                mask = in(reg) MSTATUS_MIE,
177                options(nostack),
178            );
179        }
180
181        mstatus & MSTATUS_MIE != 0
182    }
183
184    #[inline]
185    unsafe fn restore_global_interrupts(was_enabled: bool) {
186        if was_enabled {
187            // SAFETY: `was_enabled` is the raw value returned by `acquire`.
188            unsafe {
189                asm!(
190                    "csrrs zero, mstatus, {mask}",
191                    mask = in(reg) MSTATUS_MIE,
192                    options(nostack),
193                );
194            }
195        }
196    }
197
198    unsafe impl Impl for SingleHartCriticalSection {
199        unsafe fn acquire() -> RawRestoreState {
200            disable_global_interrupts()
201        }
202
203        unsafe fn release(state: RawRestoreState) {
204            // SAFETY: `state` is the raw value returned by `acquire`.
205            unsafe {
206                restore_global_interrupts(state);
207            }
208        }
209    }
210}