Skip to content

Commit

Permalink
Merge pull request #165 from rust-embedded/smode-riscv
Browse files Browse the repository at this point in the history
`riscv`: support for S-mode
  • Loading branch information
romancardenas authored Dec 20, 2023
2 parents 0452207 + 8e07693 commit 0a021ea
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 52 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/riscv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- `s-mode` feature for reexporting `interrupt::machine` 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`
Expand Down
1 change: 1 addition & 0 deletions riscv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ targets = [
]

[features]
s-mode = []
critical-section-single-hart = ["critical-section/restore-state-bool"]

[dependencies]
Expand Down
11 changes: 9 additions & 2 deletions riscv/src/critical_section.rs
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
140 changes: 91 additions & 49 deletions riscv/src/interrupt.rs
Original file line number Diff line number Diff line change
@@ -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, R>(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, R>(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, 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
}
}

#[cfg(not(feature = "s-mode"))]
pub use machine::*;
#[cfg(feature = "s-mode")]
pub use supervisor::*;
7 changes: 7 additions & 0 deletions riscv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0a021ea

Please sign in to comment.