Skip to main content

riscv/interrupt/
machine.rs

1use crate::{
2    interrupt::Trap,
3    register::{mcause, mepc, mie, mip, mstatus},
4    result::{Error, Result},
5    CoreInterruptNumber, ExceptionNumber, InterruptNumber,
6};
7
8/// Standard M-mode RISC-V interrupts
9#[derive(Copy, Clone, Debug, PartialEq, Eq)]
10#[repr(usize)]
11pub enum Interrupt {
12    SupervisorSoft = 1,
13    MachineSoft = 3,
14    SupervisorTimer = 5,
15    MachineTimer = 7,
16    SupervisorExternal = 9,
17    MachineExternal = 11,
18}
19
20/// SAFETY: `Interrupt` represents the standard RISC-V interrupts
21unsafe impl InterruptNumber for Interrupt {
22    const MAX_INTERRUPT_NUMBER: usize = Self::MachineExternal as usize;
23
24    #[inline]
25    fn number(self) -> usize {
26        self as usize
27    }
28
29    #[inline]
30    fn from_number(value: usize) -> Result<Self> {
31        match value {
32            1 => Ok(Self::SupervisorSoft),
33            3 => Ok(Self::MachineSoft),
34            5 => Ok(Self::SupervisorTimer),
35            7 => Ok(Self::MachineTimer),
36            9 => Ok(Self::SupervisorExternal),
37            11 => Ok(Self::MachineExternal),
38            _ => Err(Error::InvalidVariant(value)),
39        }
40    }
41}
42
43/// SAFETY: `Interrupt` represents the standard RISC-V core interrupts
44unsafe impl CoreInterruptNumber for Interrupt {}
45
46/// Standard M-mode RISC-V exceptions
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48#[repr(usize)]
49pub enum Exception {
50    InstructionMisaligned = 0,
51    InstructionFault = 1,
52    IllegalInstruction = 2,
53    Breakpoint = 3,
54    LoadMisaligned = 4,
55    LoadFault = 5,
56    StoreMisaligned = 6,
57    StoreFault = 7,
58    UserEnvCall = 8,
59    SupervisorEnvCall = 9,
60    MachineEnvCall = 11,
61    InstructionPageFault = 12,
62    LoadPageFault = 13,
63    StorePageFault = 15,
64}
65
66/// SAFETY: `Exception` represents the standard RISC-V exceptions
67unsafe impl ExceptionNumber for Exception {
68    const MAX_EXCEPTION_NUMBER: usize = Self::StorePageFault as usize;
69
70    #[inline]
71    fn number(self) -> usize {
72        self as usize
73    }
74
75    #[inline]
76    fn from_number(value: usize) -> Result<Self> {
77        match value {
78            0 => Ok(Self::InstructionMisaligned),
79            1 => Ok(Self::InstructionFault),
80            2 => Ok(Self::IllegalInstruction),
81            3 => Ok(Self::Breakpoint),
82            4 => Ok(Self::LoadMisaligned),
83            5 => Ok(Self::LoadFault),
84            6 => Ok(Self::StoreMisaligned),
85            7 => Ok(Self::StoreFault),
86            8 => Ok(Self::UserEnvCall),
87            9 => Ok(Self::SupervisorEnvCall),
88            11 => Ok(Self::MachineEnvCall),
89            12 => Ok(Self::InstructionPageFault),
90            13 => Ok(Self::LoadPageFault),
91            15 => Ok(Self::StorePageFault),
92            _ => Err(Error::InvalidVariant(value)),
93        }
94    }
95}
96
97/// Checks if a specific core interrupt source is enabled in the current hart (machine mode).
98#[inline]
99pub fn is_interrupt_enabled<I: CoreInterruptNumber>(interrupt: I) -> bool {
100    mie::read().is_enabled(interrupt)
101}
102
103/// Disables interrupts for a specific core interrupt source in the current hart (machine mode).
104#[inline]
105pub fn disable_interrupt<I: CoreInterruptNumber>(interrupt: I) {
106    mie::disable(interrupt);
107}
108
109/// Enables interrupts for a specific core interrupt source in the current hart (machine mode).
110///
111/// # Note
112///
113/// Interrupts will only be triggered if globally enabled in the hart. To do this, use [`enable`].
114///
115/// # Safety
116///
117/// Enabling interrupts might break critical sections or other synchronization mechanisms.
118/// Ensure that this is called in a safe context where interrupts can be enabled.
119#[inline]
120pub unsafe fn enable_interrupt<I: CoreInterruptNumber>(interrupt: I) {
121    mie::enable(interrupt);
122}
123
124/// Checks if a specific core interrupt source is pending in the current hart (machine mode).
125#[inline]
126pub fn is_interrupt_pending<I: CoreInterruptNumber>(interrupt: I) -> bool {
127    mip::read().is_pending(interrupt)
128}
129
130/// Disables interrupts globally in the current hart (machine mode).
131#[inline]
132pub fn disable() {
133    // SAFETY: It is safe to disable interrupts
134    unsafe { mstatus::clear_mie() }
135}
136
137/// Enables interrupts globally in the current hart (machine mode).
138///
139/// # Note
140///
141/// Only enabled interrupt sources will be triggered.
142/// To enable specific interrupt sources, use [`enable_interrupt`].
143///
144/// # Safety
145///
146/// Enabling interrupts might break critical sections or other synchronization mechanisms.
147/// Ensure that this is called in a safe context where interrupts can be enabled.
148#[inline]
149pub unsafe fn enable() {
150    mstatus::set_mie()
151}
152
153/// Retrieves the cause of a trap in the current hart (machine mode).
154///
155/// This function expects the target-specific interrupt and exception types.
156/// If the raw cause is not a valid interrupt or exception for the target, it returns an error.
157#[inline]
158pub fn try_cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Result<Trap<I, E>> {
159    mcause::read().cause().try_into()
160}
161
162/// Retrieves the cause of a trap in the current hart (machine mode).
163///
164/// This function expects the target-specific interrupt and exception types.
165/// If the raw cause is not a valid interrupt or exception for the target, it panics.
166#[inline]
167pub fn cause<I: CoreInterruptNumber, E: ExceptionNumber>() -> Trap<I, E> {
168    try_cause().unwrap()
169}
170
171/// Execute closure `f` with interrupts disabled in the current hart (machine mode).
172///
173/// This method does not synchronise multiple harts, so it is not suitable for
174/// using as a critical section. See the `critical-section` crate for a cross-platform
175/// way to enter a critical section which provides a `CriticalSection` token.
176///
177/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
178/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
179#[inline]
180pub fn free<F, R>(f: F) -> R
181where
182    F: FnOnce() -> R,
183{
184    let mstatus = mstatus::read();
185
186    // disable interrupts
187    disable();
188
189    let r = f();
190
191    // If the interrupts were active before our `disable` call, then re-enable
192    // them. Otherwise, keep them disabled
193    if mstatus.mie() {
194        unsafe { enable() };
195    }
196
197    r
198}
199
200/// Execute closure `f` with interrupts enabled in the current hart (machine mode).
201///
202/// This method is assumed to be called within an interrupt handler, and allows
203/// nested interrupts to occur. After the closure `f` is executed, the [`mstatus`]
204/// and [`mepc`] registers are properly restored to their previous values.
205///
206/// # Safety
207///
208/// - Do not call this function inside a critical section.
209/// - This method is assumed to be called within an interrupt handler.
210/// - Make sure to clear the interrupt flag that caused the interrupt before calling
211///   this method. Otherwise, the interrupt will be re-triggered before executing `f`.
212#[inline]
213pub unsafe fn nested<F, R>(f: F) -> R
214where
215    F: FnOnce() -> R,
216{
217    let mstatus = mstatus::read();
218    let mepc = mepc::read();
219
220    // enable interrupts to allow nested interrupts
221    enable();
222
223    let r = f();
224
225    // If the interrupts were inactive before our `enable` call, then re-disable
226    // them. Otherwise, keep them enabled
227    if !mstatus.mie() {
228        disable();
229    }
230
231    // Restore MSTATUS.PIE, MSTATUS.MPP, and SEPC
232    if mstatus.mpie() {
233        mstatus::set_mpie();
234    }
235    mstatus::set_mpp(mstatus.mpp());
236    mepc::write(mepc);
237
238    r
239}
240
241#[cfg(test)]
242mod test {
243    use super::*;
244    use Exception::*;
245    use Interrupt::*;
246
247    #[test]
248    fn test_interrupt() {
249        assert_eq!(Interrupt::from_number(1), Ok(SupervisorSoft));
250        assert_eq!(Interrupt::from_number(2), Err(Error::InvalidVariant(2)));
251        assert_eq!(Interrupt::from_number(3), Ok(MachineSoft));
252        assert_eq!(Interrupt::from_number(4), Err(Error::InvalidVariant(4)));
253        assert_eq!(Interrupt::from_number(5), Ok(SupervisorTimer));
254        assert_eq!(Interrupt::from_number(6), Err(Error::InvalidVariant(6)));
255        assert_eq!(Interrupt::from_number(7), Ok(MachineTimer));
256        assert_eq!(Interrupt::from_number(8), Err(Error::InvalidVariant(8)));
257        assert_eq!(Interrupt::from_number(9), Ok(SupervisorExternal));
258        assert_eq!(Interrupt::from_number(10), Err(Error::InvalidVariant(10)));
259        assert_eq!(Interrupt::from_number(11), Ok(MachineExternal));
260        assert_eq!(Interrupt::from_number(12), Err(Error::InvalidVariant(12)));
261
262        assert_eq!(SupervisorSoft.number(), 1);
263        assert_eq!(MachineSoft.number(), 3);
264        assert_eq!(SupervisorTimer.number(), 5);
265        assert_eq!(MachineTimer.number(), 7);
266        assert_eq!(SupervisorExternal.number(), 9);
267        assert_eq!(MachineExternal.number(), 11);
268
269        assert_eq!(MachineExternal.number(), Interrupt::MAX_INTERRUPT_NUMBER)
270    }
271
272    #[test]
273    fn test_exception() {
274        assert_eq!(Exception::from_number(0), Ok(InstructionMisaligned));
275        assert_eq!(Exception::from_number(1), Ok(InstructionFault));
276        assert_eq!(Exception::from_number(2), Ok(IllegalInstruction));
277        assert_eq!(Exception::from_number(3), Ok(Breakpoint));
278        assert_eq!(Exception::from_number(4), Ok(LoadMisaligned));
279        assert_eq!(Exception::from_number(5), Ok(LoadFault));
280        assert_eq!(Exception::from_number(6), Ok(StoreMisaligned));
281        assert_eq!(Exception::from_number(7), Ok(StoreFault));
282        assert_eq!(Exception::from_number(8), Ok(UserEnvCall));
283        assert_eq!(Exception::from_number(9), Ok(SupervisorEnvCall));
284        assert_eq!(Exception::from_number(10), Err(Error::InvalidVariant(10)));
285        assert_eq!(Exception::from_number(11), Ok(MachineEnvCall));
286        assert_eq!(Exception::from_number(12), Ok(InstructionPageFault));
287        assert_eq!(Exception::from_number(13), Ok(LoadPageFault));
288        assert_eq!(Exception::from_number(14), Err(Error::InvalidVariant(14)));
289        assert_eq!(Exception::from_number(15), Ok(StorePageFault));
290        assert_eq!(Exception::from_number(16), Err(Error::InvalidVariant(16)));
291
292        assert_eq!(InstructionMisaligned.number(), 0);
293        assert_eq!(InstructionFault.number(), 1);
294        assert_eq!(IllegalInstruction.number(), 2);
295        assert_eq!(Breakpoint.number(), 3);
296        assert_eq!(LoadMisaligned.number(), 4);
297        assert_eq!(LoadFault.number(), 5);
298        assert_eq!(StoreMisaligned.number(), 6);
299        assert_eq!(StoreFault.number(), 7);
300        assert_eq!(UserEnvCall.number(), 8);
301        assert_eq!(SupervisorEnvCall.number(), 9);
302        assert_eq!(MachineEnvCall.number(), 11);
303        assert_eq!(InstructionPageFault.number(), 12);
304        assert_eq!(LoadPageFault.number(), 13);
305        assert_eq!(StorePageFault.number(), 15);
306
307        assert_eq!(StorePageFault.number(), Exception::MAX_EXCEPTION_NUMBER)
308    }
309}