Skip to content

Commit 41c50dd

Browse files
committed
support for S-mode interrupts
1 parent b68560d commit 41c50dd

File tree

6 files changed

+118
-53
lines changed

6 files changed

+118
-53
lines changed

.github/workflows/riscv.yaml

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
on:
22
push:
3-
branches: [ master ]
3+
branches: [ master, riscv-pac ]
44
pull_request:
55
merge_group:
66

@@ -31,8 +31,12 @@ jobs:
3131
with:
3232
toolchain: ${{ matrix.toolchain }}
3333
targets: ${{ matrix.target }}
34-
- name: Build (no features)
34+
- name: Build (M-mode)
3535
run: cargo build --package riscv --target ${{ matrix.target }}
36+
- name: Build (M-mode, critical section)
37+
run: cargo build --package riscv --target ${{ matrix.target }} --features=critical-section-single-hart
38+
- name: Build (S-mode)
39+
run: cargo build --package riscv --target ${{ matrix.target }} --features=s-mode
3640
- name: Build (all features)
3741
run: cargo build --package riscv --target ${{ matrix.target }} --all-features
3842

riscv/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Added
1111

12+
- `s-mode` feature for reexporting `machine::supervisor` or `interrupt::supervisor` to `interrupt`
13+
- Support for supervisor-level interrupts in `interrupt::supervisor`
1214
- Add CI workflow to check that CHANGELOG.md file has been modified in PRs
1315
- Add `read_csr_as_rv32`, `set_rv32`, and `clear_rv32` macros
1416
- Add `mstatus::uxl` and `mstatus::sxl`
@@ -20,6 +22,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2022

2123
### Changed
2224

25+
- `critical-section` implementation depends on `s-mode` feature
26+
- machine-level interrupt functions moved to `interrupt::machine`
2327
- Cargo workspace for riscv and riscv-rt
2428
- Update `embedded-hal` dependency to v1.0.0-rc.2 (bumps MSRV to 1.60)
2529
- `misa::MXL` renamed to `misa::XLEN`

riscv/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ targets = [
2020
]
2121

2222
[features]
23+
s-mode = []
2324
critical-section-single-hart = ["critical-section/restore-state-bool"]
2425

2526
[dependencies]

riscv/src/critical_section.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
use critical_section::{set_impl, Impl, RawRestoreState};
22

33
use crate::interrupt;
4-
use crate::register::mstatus;
54

65
struct SingleHartCriticalSection;
76
set_impl!(SingleHartCriticalSection);
87

98
unsafe impl Impl for SingleHartCriticalSection {
9+
#[cfg(not(feature = "s-mode"))]
1010
unsafe fn acquire() -> RawRestoreState {
1111
let mut mstatus: usize;
1212
core::arch::asm!("csrrci {}, mstatus, 0b1000", out(reg) mstatus);
13-
core::mem::transmute::<_, mstatus::Mstatus>(mstatus).mie()
13+
core::mem::transmute::<_, crate::register::mstatus::Mstatus>(mstatus).mie()
14+
}
15+
16+
#[cfg(feature = "s-mode")]
17+
unsafe fn acquire() -> RawRestoreState {
18+
let mut sstatus: usize;
19+
core::arch::asm!("csrrci {}, sstatus, 0b0010", out(reg) sstatus);
20+
core::mem::transmute::<_, crate::register::sstatus::Sstatus>(sstatus).sie()
1421
}
1522

1623
unsafe fn release(was_active: RawRestoreState) {

riscv/src/interrupt.rs

+91-49
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,105 @@
11
//! Interrupts
22
33
// NOTE: Adapted from cortex-m/src/interrupt.rs
4-
use crate::register::mstatus;
5-
6-
/// Disables all interrupts in the current hart.
7-
#[inline]
8-
pub unsafe fn disable() {
9-
match () {
10-
#[cfg(riscv)]
11-
() => mstatus::clear_mie(),
12-
#[cfg(not(riscv))]
13-
() => unimplemented!(),
4+
5+
pub mod machine {
6+
use crate::register::mstatus;
7+
8+
/// Disables all interrupts in the current hart (machine mode).
9+
#[inline]
10+
pub fn disable() {
11+
unsafe { mstatus::clear_mie() }
1412
}
15-
}
1613

17-
/// Enables all the interrupts in the current hart.
18-
///
19-
/// # Safety
20-
///
21-
/// - Do not call this function inside a critical section.
22-
#[inline]
23-
pub unsafe fn enable() {
24-
match () {
25-
#[cfg(riscv)]
26-
() => mstatus::set_mie(),
27-
#[cfg(not(riscv))]
28-
() => unimplemented!(),
14+
/// Enables all the interrupts in the current hart (machine mode).
15+
///
16+
/// # Safety
17+
///
18+
/// Do not call this function inside a critical section.
19+
#[inline]
20+
pub unsafe fn enable() {
21+
mstatus::set_mie()
2922
}
30-
}
3123

32-
/// Execute closure `f` with interrupts disabled in the current hart.
33-
///
34-
/// This method does not synchronise multiple harts, so it is not suitable for
35-
/// using as a critical section. See the `critical-section` crate for a cross-platform
36-
/// way to enter a critical section which provides a `CriticalSection` token.
37-
///
38-
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
39-
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
40-
#[inline]
41-
pub fn free<F, R>(f: F) -> R
42-
where
43-
F: FnOnce() -> R,
44-
{
45-
let mstatus = mstatus::read();
46-
47-
// disable interrupts
48-
unsafe {
24+
/// Execute closure `f` with interrupts disabled in the current hart (machine mode).
25+
///
26+
/// This method does not synchronise multiple harts, so it is not suitable for
27+
/// using as a critical section. See the `critical-section` crate for a cross-platform
28+
/// way to enter a critical section which provides a `CriticalSection` token.
29+
///
30+
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
31+
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
32+
#[inline]
33+
pub fn free<F, R>(f: F) -> R
34+
where
35+
F: FnOnce() -> R,
36+
{
37+
let mstatus = mstatus::read();
38+
39+
// disable interrupts
4940
disable();
50-
}
5141

52-
let r = f();
42+
let r = f();
5343

54-
// If the interrupts were active before our `disable` call, then re-enable
55-
// them. Otherwise, keep them disabled
56-
if mstatus.mie() {
57-
unsafe {
58-
enable();
44+
// If the interrupts were active before our `disable` call, then re-enable
45+
// them. Otherwise, keep them disabled
46+
if mstatus.mie() {
47+
unsafe { enable() };
5948
}
49+
50+
r
51+
}
52+
}
53+
pub mod supervisor {
54+
use crate::register::sstatus;
55+
56+
/// Disables all interrupts in the current hart (supervisor mode).
57+
#[inline]
58+
pub fn disable() {
59+
unsafe { sstatus::clear_sie() }
6060
}
6161

62-
r
62+
/// Enables all the interrupts in the current hart (supervisor mode).
63+
///
64+
/// # Safety
65+
///
66+
/// Do not call this function inside a critical section.
67+
#[inline]
68+
pub unsafe fn enable() {
69+
sstatus::set_sie()
70+
}
71+
72+
/// Execute closure `f` with interrupts disabled in the current hart (supervisor mode).
73+
///
74+
/// This method does not synchronise multiple harts, so it is not suitable for
75+
/// using as a critical section. See the `critical-section` crate for a cross-platform
76+
/// way to enter a critical section which provides a `CriticalSection` token.
77+
///
78+
/// This crate provides an implementation for `critical-section` suitable for single-hart systems,
79+
/// based on disabling all interrupts. It can be enabled with the `critical-section-single-hart` feature.
80+
#[inline]
81+
pub fn free<F, R>(f: F) -> R
82+
where
83+
F: FnOnce() -> R,
84+
{
85+
let sstatus = sstatus::read();
86+
87+
// disable interrupts
88+
disable();
89+
90+
let r = f();
91+
92+
// If the interrupts were active before our `disable` call, then re-enable
93+
// them. Otherwise, keep them disabled
94+
if sstatus.sie() {
95+
unsafe { enable() };
96+
}
97+
98+
r
99+
}
63100
}
101+
102+
#[cfg(not(feature = "s-mode"))]
103+
pub use machine::*;
104+
#[cfg(feature = "s-mode")]
105+
pub use supervisor::*;

riscv/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@
1515
//!
1616
//! # Optional features
1717
//!
18+
//! ## `s-mode`
19+
//!
20+
//! This feature re-exports in `interrupt` S-mode interrupt functions defined in `interrupt::supervisor`.
21+
//! By default, the crate assumes that the target is running in M-mode.
22+
//! Thus, `interrupt` re-exports the M-mode functions defined in `interrupt::machine`.
23+
//!
1824
//! ## `critical-section-single-hart`
1925
//!
2026
//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section)
2127
//! implementation suitable for single-hart targets, based on disabling interrupts globally.
28+
//! This feature uses S-mode interrupt handling if the `s-mode` feature is enabled, and M-mode otherwise.
2229
//!
2330
//! It is **unsound** to enable it on multi-hart targets,
2431
//! and may cause functional problems in systems where some interrupts must NOT be disabled

0 commit comments

Comments
 (0)