Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: support for S-mode #165

Merged
merged 1 commit into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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