diff --git a/.github/workflows/riscv.yaml b/.github/workflows/riscv.yaml index e2156dfe..c086564e 100644 --- a/.github/workflows/riscv.yaml +++ b/.github/workflows/riscv.yaml @@ -1,6 +1,6 @@ on: push: - branches: [ master ] + branches: [ master, riscv-pac ] pull_request: merge_group: @@ -31,8 +31,12 @@ jobs: with: toolchain: ${{ matrix.toolchain }} targets: ${{ matrix.target }} - - name: Build (no features) + - name: Build (M-mode) run: cargo build --package riscv --target ${{ matrix.target }} + - name: Build (M-mode, critical section) + run: cargo build --package riscv --target ${{ matrix.target }} --features=critical-section-single-hart + - name: Build (S-mode) + run: cargo build --package riscv --target ${{ matrix.target }} --features=s-mode - name: Build (all features) run: cargo build --package riscv --target ${{ matrix.target }} --all-features diff --git a/riscv/CHANGELOG.md b/riscv/CHANGELOG.md index ab12ce7b..9d5edebe 100644 --- a/riscv/CHANGELOG.md +++ b/riscv/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- `s-mode` feature for reexporting `machine::supervisor` or `interrupt::supervisor` to `interrupt` +- Support for supervisor-level interrupts in `interrupt::supervisor` - Add CI workflow to check that CHANGELOG.md file has been modified in PRs - Add `read_csr_as_rv32`, `set_rv32`, and `clear_rv32` macros - Add `mstatus::uxl` and `mstatus::sxl` @@ -20,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- `critical-section` implementation depends on `s-mode` feature +- machine-level interrupt functions moved to `interrupt::machine` - Cargo workspace for riscv and riscv-rt - Update `embedded-hal` dependency to v1.0.0-rc.2 (bumps MSRV to 1.60) - `misa::MXL` renamed to `misa::XLEN` diff --git a/riscv/Cargo.toml b/riscv/Cargo.toml index 8e126fe7..c2d7f3a5 100644 --- a/riscv/Cargo.toml +++ b/riscv/Cargo.toml @@ -20,6 +20,7 @@ targets = [ ] [features] +s-mode = [] critical-section-single-hart = ["critical-section/restore-state-bool"] [dependencies] diff --git a/riscv/src/critical_section.rs b/riscv/src/critical_section.rs index b52e2373..908daec7 100644 --- a/riscv/src/critical_section.rs +++ b/riscv/src/critical_section.rs @@ -1,16 +1,23 @@ use critical_section::{set_impl, Impl, RawRestoreState}; use crate::interrupt; -use crate::register::mstatus; struct SingleHartCriticalSection; set_impl!(SingleHartCriticalSection); unsafe impl Impl for SingleHartCriticalSection { + #[cfg(not(feature = "s-mode"))] unsafe fn acquire() -> RawRestoreState { let mut mstatus: usize; core::arch::asm!("csrrci {}, mstatus, 0b1000", out(reg) mstatus); - core::mem::transmute::<_, mstatus::Mstatus>(mstatus).mie() + core::mem::transmute::<_, crate::register::mstatus::Mstatus>(mstatus).mie() + } + + #[cfg(feature = "s-mode")] + unsafe fn acquire() -> RawRestoreState { + let mut sstatus: usize; + core::arch::asm!("csrrci {}, sstatus, 0b0010", out(reg) sstatus); + core::mem::transmute::<_, crate::register::sstatus::Sstatus>(sstatus).sie() } unsafe fn release(was_active: RawRestoreState) { diff --git a/riscv/src/interrupt.rs b/riscv/src/interrupt.rs index ee564efc..f83558b8 100644 --- a/riscv/src/interrupt.rs +++ b/riscv/src/interrupt.rs @@ -1,63 +1,105 @@ //! Interrupts // NOTE: Adapted from cortex-m/src/interrupt.rs -use crate::register::mstatus; - -/// Disables all interrupts in the current hart. -#[inline] -pub unsafe fn disable() { - match () { - #[cfg(riscv)] - () => mstatus::clear_mie(), - #[cfg(not(riscv))] - () => unimplemented!(), + +pub mod machine { + use crate::register::mstatus; + + /// Disables all interrupts in the current hart (machine mode). + #[inline] + pub fn disable() { + unsafe { mstatus::clear_mie() } } -} -/// Enables all the interrupts in the current hart. -/// -/// # Safety -/// -/// - Do not call this function inside a critical section. -#[inline] -pub unsafe fn enable() { - match () { - #[cfg(riscv)] - () => mstatus::set_mie(), - #[cfg(not(riscv))] - () => unimplemented!(), + /// 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. -/// -/// 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: F) -> R -where - F: FnOnce() -> R, -{ - let mstatus = mstatus::read(); - - // disable interrupts - unsafe { + /// 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: F) -> R + where + F: FnOnce() -> R, + { + let mstatus = mstatus::read(); + + // disable interrupts disable(); - } - let r = f(); + let r = f(); - // If the interrupts were active before our `disable` call, then re-enable - // them. Otherwise, keep them disabled - if mstatus.mie() { - unsafe { - enable(); + // If the interrupts were active before our `disable` call, then re-enable + // them. Otherwise, keep them disabled + if mstatus.mie() { + unsafe { enable() }; } + + r + } +} +pub mod supervisor { + use crate::register::sstatus; + + /// Disables all interrupts in the current hart (supervisor mode). + #[inline] + pub fn disable() { + unsafe { sstatus::clear_sie() } } - r + /// Enables all the interrupts in the current hart (supervisor mode). + /// + /// # Safety + /// + /// Do not call this function inside a critical section. + #[inline] + pub unsafe fn enable() { + sstatus::set_sie() + } + + /// 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. + #[inline] + pub fn free(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 + } } + +#[cfg(not(feature = "s-mode"))] +pub use machine::*; +#[cfg(feature = "s-mode")] +pub use supervisor::*; diff --git a/riscv/src/lib.rs b/riscv/src/lib.rs index 8128fd81..7eacac7a 100644 --- a/riscv/src/lib.rs +++ b/riscv/src/lib.rs @@ -15,10 +15,17 @@ //! //! # Optional features //! +//! ## `s-mode` +//! +//! This feature re-exports in `interrupt` S-mode interrupt functions defined in `interrupt::supervisor`. +//! By default, the crate assumes that the target is running in M-mode. +//! Thus, `interrupt` re-exports the M-mode functions defined in `interrupt::machine`. +//! //! ## `critical-section-single-hart` //! //! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section) //! implementation suitable for single-hart targets, based on disabling interrupts globally. +//! This feature uses S-mode interrupt handling if the `s-mode` feature is enabled, and M-mode otherwise. //! //! It is **unsound** to enable it on multi-hart targets, //! and may cause functional problems in systems where some interrupts must NOT be disabled