Skip to content

Commit

Permalink
Migrate interrupt and exception enums to riscv::interrupt
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Jun 5, 2024
1 parent f9c94b5 commit 36b29ec
Show file tree
Hide file tree
Showing 5 changed files with 592 additions and 542 deletions.
210 changes: 46 additions & 164 deletions riscv/src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,188 +2,70 @@
// NOTE: Adapted from cortex-m/src/interrupt.rs

pub mod machine {
use crate::register::{mepc, mstatus};
pub use riscv_pac::{CoreInterruptNumber, ExceptionNumber, InterruptNumber}; // re-export useful riscv-pac traits

/// Disables all interrupts in the current hart (machine mode).
#[inline]
pub fn disable() {
// SAFETY: It is safe to disable interrupts
unsafe { mstatus::clear_mie() }
}
pub mod machine;
pub mod supervisor;

/// Enables all the interrupts in the current hart (machine mode).
///
/// # Safety
///
/// Do not call this function inside a critical section.
#[inline]
pub unsafe fn enable() {
mstatus::set_mie()
}

/// Execute closure `f` with interrupts disabled in the current hart (machine mode).
///
/// This method does not synchronise multiple harts, so it is not suitable for
/// using as a critical section. See the `critical-section` crate for a cross-platform
/// way to enter a critical section which provides a `CriticalSection` token.
///
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
#[inline]
pub fn free<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let mstatus = mstatus::read();
#[cfg(not(feature = "s-mode"))]
pub use machine::*;
#[cfg(feature = "s-mode")]
pub use supervisor::*;

// disable interrupts
disable();
/// Trap Cause
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Trap<I, E> {
Interrupt(I),
Exception(E),
}

let r = f();
/// Trap Error
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TrapError {
InvalidInterrupt(usize),
InvalidException(usize),
}

// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
if mstatus.mie() {
unsafe { enable() };
impl Trap<usize, usize> {
/// Converts a target-specific trap cause to a generic trap cause
#[inline]
pub fn from<I: CoreInterruptNumber, E: ExceptionNumber>(trap: Trap<I, E>) -> Self {
match trap {
Trap::Interrupt(interrupt) => Trap::Interrupt(interrupt.number()),
Trap::Exception(exception) => Trap::Exception(exception.number()),
}

r
}

/// Execute closure `f` with interrupts enabled in the current hart (machine mode).
///
/// This method is assumed to be called within an interrupt handler, and allows
/// nested interrupts to occur. After the closure `f` is executed, the [`mstatus`]
/// and [`mepc`] registers are properly restored to their previous values.
///
/// # Safety
///
/// - Do not call this function inside a critical section.
/// - This method is assumed to be called within an interrupt handler.
/// - Make sure to clear the interrupt flag that caused the interrupt before calling
/// this method. Otherwise, the interrupt will be re-triggered before executing `f`.
/// Tries to convert the generic trap cause to a target-specific trap cause
#[inline]
pub unsafe fn nested<F, R>(f: F) -> R
pub fn try_into<I, E>(self) -> Result<Trap<I, E>, TrapError>
where
F: FnOnce() -> R,
I: CoreInterruptNumber,
E: ExceptionNumber,
{
let mstatus = mstatus::read();
let mepc = mepc::read();

// enable interrupts to allow nested interrupts
enable();

let r = f();

// If the interrupts were inactive before our `enable` call, then re-disable
// them. Otherwise, keep them enabled
if !mstatus.mie() {
disable();
match self {
Trap::Interrupt(code) => match I::from_number(code) {
Ok(interrupt) => Ok(Trap::Interrupt(interrupt)),
Err(code) => Err(TrapError::InvalidInterrupt(code)),
},
Trap::Exception(code) => match E::from_number(code) {
Ok(exception) => Ok(Trap::Exception(exception)),
Err(code) => Err(TrapError::InvalidException(code)),
},
}

// Restore MSTATUS.PIE, MSTATUS.MPP, and SEPC
if mstatus.mpie() {
mstatus::set_mpie();
}
mstatus::set_mpp(mstatus.mpp());
mepc::write(mepc);

r
}
}

pub mod supervisor {
use crate::register::{sepc, sstatus};

/// Disables all interrupts in the current hart (supervisor mode).
#[inline]
pub fn disable() {
// SAFETY: It is safe to disable interrupts
unsafe { sstatus::clear_sie() }
}

/// Enables all the interrupts in the current hart (supervisor mode).
///
/// # Safety
///
/// Do not call this function inside a critical section.
impl<I: CoreInterruptNumber, E: ExceptionNumber> Trap<I, E> {
/// Converts a target-specific trap cause to a generic trap cause
#[inline]
pub unsafe fn enable() {
sstatus::set_sie()
pub fn into(self) -> Trap<usize, usize> {
Trap::from(self)
}

/// Execute closure `f` with interrupts disabled in the current hart (supervisor mode).
///
/// This method does not synchronise multiple harts, so it is not suitable for
/// using as a critical section. See the `critical-section` crate for a cross-platform
/// way to enter a critical section which provides a `CriticalSection` token.
///
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
/// Tries to convert the generic trap cause to a target-specific trap cause
#[inline]
pub fn free<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let sstatus = sstatus::read();

// disable interrupts
disable();

let r = f();

// If the interrupts were active before our `disable` call, then re-enable
// them. Otherwise, keep them disabled
if sstatus.sie() {
unsafe { enable() };
}

r
}

/// Execute closure `f` with interrupts enabled in the current hart (supervisor mode).
/// This method is assumed to be called within an interrupt handler, and allows
/// nested interrupts to occur. After the closure `f` is executed, the [`sstatus`]
/// and [`sepc`] registers are properly restored to their previous values.
///
/// # Safety
///
/// - Do not call this function inside a critical section.
/// - This method is assumed to be called within an interrupt handler.
/// - Make sure to clear the interrupt flag that caused the interrupt before calling
/// this method. Otherwise, the interrupt will be re-triggered before executing `f`.
#[inline]
pub unsafe fn nested<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
let sstatus = sstatus::read();
let sepc = sepc::read();

// enable interrupts to allow nested interrupts
enable();

let r = f();

// If the interrupts were inactive before our `enable` call, then re-disable
// them. Otherwise, keep them enabled
if !sstatus.sie() {
disable();
}

// Restore SSTATUS.SPIE, SSTATUS.SPP, and SEPC
if sstatus.spie() {
sstatus::set_spie();
}
sstatus::set_spp(sstatus.spp());
sepc::write(sepc);

r
pub fn try_from(trap: Trap<usize, usize>) -> Result<Self, TrapError> {
trap.try_into()
}
}

#[cfg(not(feature = "s-mode"))]
pub use machine::*;
#[cfg(feature = "s-mode")]
pub use supervisor::*;
Loading

0 comments on commit 36b29ec

Please sign in to comment.