Skip to content

Commit 149d917

Browse files
committed
ECALL and nested interrupts
1 parent 0a021ea commit 149d917

File tree

3 files changed

+109
-2
lines changed

3 files changed

+109
-2
lines changed

riscv/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
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+
- Add `asm::ecall()`, a wrapper for implementing an `ecall` instruction
13+
- Add `nested` function for nested ISRs in `interrupt::machine` and `interrupt::supervisor`
1214
- `s-mode` feature for reexporting `interrupt::machine` or `interrupt::supervisor` to `interrupt`
1315
- Support for supervisor-level interrupts in `interrupt::supervisor`
1416
- Add CI workflow to check that CHANGELOG.md file has been modified in PRs

riscv/src/asm.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,28 @@ pub unsafe fn sfence_vma(asid: usize, addr: usize) {
110110
}
111111
}
112112

113+
/// `ECALL` instruction wrapper
114+
///
115+
/// Generates an exception for a service request to the execution environment.
116+
/// When executed in U-mode, S-mode, or M-mode, it generates an environment-call-from-U-mode
117+
/// exception, environment-call-from-S-mode exception, or environment-call-from-M-mode exception,
118+
/// respectively, and performs no other operation.
119+
///
120+
/// # Note
121+
///
122+
/// The ECALL instruction will **NOT** save and restore the stack pointer, as it triggers an exception.
123+
/// The stack pointer must be saved and restored accordingly by the exception handler.
124+
#[inline]
125+
pub unsafe fn ecall() {
126+
match () {
127+
#[cfg(riscv)]
128+
() => core::arch::asm!("ecall", options(nostack)),
129+
130+
#[cfg(not(riscv))]
131+
() => unimplemented!(),
132+
}
133+
}
134+
113135
/// Blocks the program for *at least* `cycles` CPU cycles.
114136
///
115137
/// This is implemented in assembly so its execution time is independent of the optimization

riscv/src/interrupt.rs

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
// NOTE: Adapted from cortex-m/src/interrupt.rs
44

55
pub mod machine {
6-
use crate::register::mstatus;
6+
use crate::register::{mepc, mstatus};
77

88
/// Disables all interrupts in the current hart (machine mode).
99
#[inline]
1010
pub fn disable() {
11+
// SAFETY: It is safe to disable interrupts
1112
unsafe { mstatus::clear_mie() }
1213
}
1314

@@ -49,13 +50,55 @@ pub mod machine {
4950

5051
r
5152
}
53+
54+
/// Execute closure `f` with interrupts enabled in the current hart (machine mode).
55+
///
56+
/// This method is assumed to be called within an interrupt handler, and allows
57+
/// nested interrupts to occur. After the closure `f` is executed, the [`mstatus`]
58+
/// and [`mepc`] registers are properly restored to their previous values.
59+
///
60+
/// # Safety
61+
///
62+
/// - Do not call this function inside a critical section.
63+
/// - This method is assumed to be called within an interrupt handler.
64+
/// - Make sure to clear the interrupt flag that caused the interrupt before calling
65+
/// this method. Otherwise, the interrupt will be re-triggered before executing `f`.
66+
#[inline]
67+
pub unsafe fn nested<F, R>(f: F) -> R
68+
where
69+
F: FnOnce() -> R,
70+
{
71+
let mstatus = mstatus::read();
72+
let mepc = mepc::read();
73+
74+
// enable interrupts to allow nested interrupts
75+
enable();
76+
77+
let r = f();
78+
79+
// If the interrupts were inactive before our `enable` call, then re-disable
80+
// them. Otherwise, keep them enabled
81+
if !mstatus.mie() {
82+
disable();
83+
}
84+
85+
// Restore MSTATUS.PIE, MSTATUS.MPP, and SEPC
86+
if !mstatus.mpie() {
87+
mstatus::set_mpie();
88+
}
89+
mstatus::set_mpp(mstatus.mpp());
90+
mepc::write(mepc);
91+
92+
r
93+
}
5294
}
5395
pub mod supervisor {
54-
use crate::register::sstatus;
96+
use crate::register::{sepc, sstatus};
5597

5698
/// Disables all interrupts in the current hart (supervisor mode).
5799
#[inline]
58100
pub fn disable() {
101+
// SAFETY: It is safe to disable interrupts
59102
unsafe { sstatus::clear_sie() }
60103
}
61104

@@ -97,6 +140,46 @@ pub mod supervisor {
97140

98141
r
99142
}
143+
144+
/// Execute closure `f` with interrupts enabled in the current hart (supervisor mode).
145+
/// This method is assumed to be called within an interrupt handler, and allows
146+
/// nested interrupts to occur. After the closure `f` is executed, the [`sstatus`]
147+
/// and [`sepc`] registers are properly restored to their previous values.
148+
///
149+
/// # Safety
150+
///
151+
/// - Do not call this function inside a critical section.
152+
/// - This method is assumed to be called within an interrupt handler.
153+
/// - Make sure to clear the interrupt flag that caused the interrupt before calling
154+
/// this method. Otherwise, the interrupt will be re-triggered before executing `f`.
155+
#[inline]
156+
pub unsafe fn nested<F, R>(f: F) -> R
157+
where
158+
F: FnOnce() -> R,
159+
{
160+
let sstatus = sstatus::read();
161+
let sepc = sepc::read();
162+
163+
// enable interrupts to allow nested interrupts
164+
enable();
165+
166+
let r = f();
167+
168+
// If the interrupts were inactive before our `enable` call, then re-disable
169+
// them. Otherwise, keep them enabled
170+
if !sstatus.sie() {
171+
disable();
172+
}
173+
174+
// Restore SSTATUS.SPIE, SSTATUS.SPP, and SEPC
175+
if !sstatus.spie() {
176+
sstatus::set_spie();
177+
}
178+
sstatus::set_spp(sstatus.spp());
179+
sepc::write(sepc);
180+
181+
r
182+
}
100183
}
101184

102185
#[cfg(not(feature = "s-mode"))]

0 commit comments

Comments
 (0)