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}