Skip to content

Commit d9c6076

Browse files
bors[bot]Dirbaio
andauthored
Merge #110
110: Add critical-section 1.0 implementation, fix multicore unsoundness. r=almindor a=Dirbaio ~~Requires #109~~ This adds a [critical-section](https://github.com/rust-embedded/critical-section) implementation for single-core chips, based on disabling all interrupts. `interrupt::free` is is unsound on multicore systems because it only disables interrupts in the current core. For multicore chips, a chip-specific critical section implementationis needed instead. Unsoundness is fixed by not returning the `CriticalSection` token. This is a breaking change. This is the riscv equivalent of rust-embedded/cortex-m#447 and rust-embedded/cortex-m#448 Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2 parents bb3945d + caec777 commit d9c6076

File tree

7 files changed

+78
-12
lines changed

7 files changed

+78
-12
lines changed

.github/workflows/ci.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ jobs:
3838
run: cargo check --target riscv64imac-unknown-none-elf
3939
- name: Run CI script for riscv64gc-unknown-none-elf under ${{ matrix.rust }}
4040
run: cargo check --target riscv64gc-unknown-none-elf
41+
- name: Run CI script for x86_64-unknown-linux-gnu under ${{ matrix.rust }} with critical-section-single-hart
42+
run: cargo check --target x86_64-unknown-linux-gnu --features critical-section-single-hart
43+
- name: Run CI script for riscv32imac-unknown-none-elf under ${{ matrix.rust }} with critical-section-single-hart
44+
run: cargo check --target riscv32imac-unknown-none-elf --features critical-section-single-hart
45+
- name: Run CI script for riscv64imac-unknown-none-elf under ${{ matrix.rust }} with critical-section-single-hart
46+
run: cargo check --target riscv64imac-unknown-none-elf --features critical-section-single-hart
47+
- name: Run CI script for riscv64gc-unknown-none-elf under ${{ matrix.rust }} with critical-section-single-hart
48+
run: cargo check --target riscv64gc-unknown-none-elf --features critical-section-single-hart
4149

4250
# On macOS and Windows, we at least make sure that the crate builds and links.
4351
build-other:
@@ -56,4 +64,4 @@ jobs:
5664
toolchain: stable
5765
override: true
5866
- name: Build crate for host OS
59-
run: cargo build
67+
run: cargo build --features critical-section-single-hart

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `critical-section-single-hart` feature which provides an implementation for the `critical_section` crate for single-hart systems, based on disabling all interrupts.
13+
1014
## [v0.9.0] - 2022-10-06
1115

1216
### Fixed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ targets = [
1717
"riscv64imac-unknown-none-elf", "riscv64gc-unknown-none-elf",
1818
]
1919

20+
[features]
21+
critical-section-single-hart = ["critical-section/restore-state-bool"]
22+
2023
[dependencies]
21-
bare-metal = "1.0.0"
2224
bit_field = "0.10.0"
25+
critical-section = "1.1.0"
2326
embedded-hal = "0.2.6"

src/critical_section.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use critical_section::{set_impl, Impl, RawRestoreState};
2+
3+
use crate::interrupt;
4+
use crate::register::mstatus;
5+
6+
struct SingleHartCriticalSection;
7+
set_impl!(SingleHartCriticalSection);
8+
9+
unsafe impl Impl for SingleHartCriticalSection {
10+
unsafe fn acquire() -> RawRestoreState {
11+
let was_active = mstatus::read().mie();
12+
interrupt::disable();
13+
was_active
14+
}
15+
16+
unsafe fn release(was_active: RawRestoreState) {
17+
// Only re-enable interrupts if they were enabled before the critical section.
18+
if was_active {
19+
interrupt::enable()
20+
}
21+
}
22+
}

src/interrupt.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
33
// NOTE: Adapted from cortex-m/src/interrupt.rs
44
use crate::register::mstatus;
5-
pub use bare_metal::{CriticalSection, Mutex};
65

7-
/// Disables all interrupts
6+
/// Disables all interrupts in the current hart.
87
#[inline]
98
pub unsafe fn disable() {
109
match () {
@@ -15,11 +14,11 @@ pub unsafe fn disable() {
1514
}
1615
}
1716

18-
/// Enables all the interrupts
17+
/// Enables all the interrupts in the current hart.
1918
///
2019
/// # Safety
2120
///
22-
/// - Do not call this function inside an `interrupt::free` critical section
21+
/// - Do not call this function inside a critical section.
2322
#[inline]
2423
pub unsafe fn enable() {
2524
match () {
@@ -30,13 +29,18 @@ pub unsafe fn enable() {
3029
}
3130
}
3231

33-
/// Execute closure `f` in an interrupt-free context.
32+
/// Execute closure `f` with interrupts disabled in the current hart.
3433
///
35-
/// This as also known as a "critical section".
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.
3640
#[inline]
3741
pub fn free<F, R>(f: F) -> R
3842
where
39-
F: FnOnce(&CriticalSection) -> R,
43+
F: FnOnce() -> R,
4044
{
4145
let mstatus = mstatus::read();
4246

@@ -45,7 +49,7 @@ where
4549
disable();
4650
}
4751

48-
let r = f(unsafe { &CriticalSection::new() });
52+
let r = f();
4953

5054
// If the interrupts were active before our `disable` call, then re-enable
5155
// them. Otherwise, keep them disabled

src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@
1212
//! - Access to core registers like `mstatus` or `mcause`.
1313
//! - Interrupt manipulation mechanisms.
1414
//! - Wrappers around assembly instructions like `WFI`.
15+
//!
16+
//! # Optional features
17+
//!
18+
//! ## `critical-section-single-hart`
19+
//!
20+
//! This feature enables a [`critical-section`](https://github.com/rust-embedded/critical-section)
21+
//! implementation suitable for single-hart targets, based on disabling interrupts globally.
22+
//!
23+
//! It is **unsound** to enable it on multi-hart targets,
24+
//! and may cause functional problems in systems where some interrupts must be not be disabled
25+
//! or critical sections are managed as part of an RTOS. In these cases, you should use
26+
//! a target-specific implementation instead, typically provided by a HAL or RTOS crate.
1527
1628
#![no_std]
1729

@@ -22,3 +34,13 @@ pub mod register;
2234

2335
#[macro_use]
2436
mod macros;
37+
38+
#[cfg(all(riscv, feature = "critical-section-single-hart"))]
39+
mod critical_section;
40+
41+
/// Used to reexport items for use in macros. Do not use directly.
42+
/// Not covered by semver guarantees.
43+
#[doc(hidden)]
44+
pub mod _export {
45+
pub use critical_section;
46+
}

src/macros.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
/// at most once in the whole lifetime of the program.
77
///
88
/// # Note
9-
/// this macro is unsound on multi-core systems
9+
///
10+
/// This macro requires a `critical-section` implementation to be set. For most single-hart systems,
11+
/// you can enable the `critical-section-single-hart` feature for this crate. For other systems, you
12+
/// have to provide one from elsewhere, typically your chip's HAL crate.
1013
///
1114
/// # Example
1215
///
@@ -29,7 +32,7 @@
2932
#[macro_export]
3033
macro_rules! singleton {
3134
(: $ty:ty = $expr:expr) => {
32-
$crate::interrupt::free(|_| {
35+
$crate::_export::critical_section::with(|_| {
3336
static mut VAR: Option<$ty> = None;
3437

3538
#[allow(unsafe_code)]

0 commit comments

Comments
 (0)