Skip to content

Commit 7699eef

Browse files
committed
Update the functions for using the PSP
Now offers both Priv and Unpriv modes, and has a handle to represent ownership of a static Stack object. The load/store check on `Stack::taken` is not perfectly thread safe, but it's probably good enough and doing better requires a critical-section or CAS atomics.
1 parent 176c74d commit 7699eef

File tree

3 files changed

+92
-10
lines changed

3 files changed

+92
-10
lines changed

cortex-m/src/asm.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,9 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
176176
call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32)
177177
}
178178

179-
/// Switch to unprivileged mode.
179+
/// Switch to unprivileged mode using the Process Stack
180180
///
181-
/// Sets CONTROL.SPSEL (setting the program stack to be the active
181+
/// Sets CONTROL.SPSEL (setting the Process Stack to be the active
182182
/// stack) and CONTROL.nPRIV (setting unprivileged mode), updates the
183183
/// program stack pointer to the address in `psp`, then jumps to the
184184
/// address in `entry`.
@@ -194,7 +194,7 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 {
194194
/// it, you may wish to set the `PSPLIM` register to guard against this.
195195
#[cfg(cortex_m)]
196196
#[inline(always)]
197-
pub unsafe fn enter_unprivileged(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
197+
pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
198198
unsafe {
199199
core::arch::asm!(
200200
"mrs {tmp}, CONTROL",
@@ -211,6 +211,36 @@ pub unsafe fn enter_unprivileged(psp: *const u32, entry: extern "C" fn() -> !) -
211211
}
212212
}
213213

214+
/// Switch to using the Process Stack, but remain in Privileged Mode
215+
///
216+
/// Sets CONTROL.SPSEL (setting the Process Stack to be the active stack) but
217+
/// leaves CONTROL.nPRIV alone, updates the program stack pointer to the
218+
/// address in `psp`, then jumps to the address in `entry`.
219+
///
220+
/// # Safety
221+
///
222+
/// * `psp` and `entry` must point to valid stack memory and executable code,
223+
/// respectively.
224+
/// * `psp` must be 8 bytes aligned and point to stack top as stack grows
225+
/// towards lower addresses.
226+
/// * The size of the stack provided here must be large enough for your
227+
/// program - stack overflows are obviously UB. If your processor supports
228+
/// it, you may wish to set the `PSPLIM` register to guard against this.
229+
#[cfg(cortex_m)]
230+
#[inline(always)]
231+
pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
232+
unsafe {
233+
core::arch::asm!(
234+
"msr PSP, {psp}",
235+
"isb",
236+
"bx {ent}",
237+
psp = in(reg) psp,
238+
ent = in(reg) entry,
239+
options(noreturn, nostack)
240+
);
241+
}
242+
}
243+
214244
/// Bootstrap.
215245
///
216246
/// Clears CONTROL.SPSEL (setting the main stack to be the active stack),

cortex-m/src/psp.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,34 @@
44
// lint here.
55
#![allow(clippy::missing_inline_in_public_items)]
66

7-
use core::cell::UnsafeCell;
7+
use core::{
8+
cell::UnsafeCell,
9+
sync::atomic::{AtomicBool, Ordering},
10+
};
11+
12+
/// Represents access to a [`Stack`]
13+
pub struct StackHandle(*mut u32, usize);
14+
15+
impl StackHandle {
16+
/// Get the pointer to the top of the stack
17+
pub const fn top(&mut self) -> *mut u32 {
18+
// SAFETY: The stack was this big when we constructed the handle
19+
unsafe { self.0.add(self.1) }
20+
}
21+
22+
/// Get the pointer to the top of the stack
23+
pub const fn bottom(&mut self) -> *mut u32 {
24+
self.0
25+
}
26+
}
827

928
/// A stack you can use as your Process Stack (PSP)
1029
///
1130
/// The const-param N is the size **in 32-bit words**
1231
#[repr(align(8), C)]
1332
pub struct Stack<const N: usize> {
1433
space: UnsafeCell<[u32; N]>,
34+
taken: AtomicBool,
1535
}
1636

1737
impl<const N: usize> Stack<N> {
@@ -22,17 +42,27 @@ impl<const N: usize> Stack<N> {
2242
/// ```rust
2343
/// # use cortex_m::psp::Stack;
2444
/// static PSP_STACK: Stack::<4096> = Stack::new();
45+
/// fn example() {
46+
/// let handle = PSP_STACK.take_handle();
47+
/// // ...
48+
/// }
2549
/// ```
2650
pub const fn new() -> Stack<N> {
2751
Stack {
2852
space: UnsafeCell::new([0; N]),
53+
taken: AtomicBool::new(false),
2954
}
3055
}
3156

3257
/// Return the top of the stack
33-
pub fn get_top(&self) -> *mut u32 {
58+
pub fn take_handle(&self) -> StackHandle {
59+
if self.taken.load(Ordering::Acquire) {
60+
panic!("Cannot get two handles to one stack!");
61+
}
62+
self.taken.store(true, Ordering::Release);
63+
3464
let start = self.space.get() as *mut u32;
35-
unsafe { start.add(N) }
65+
StackHandle(start, N)
3666
}
3767
}
3868

@@ -44,9 +74,18 @@ impl<const N: usize> core::default::Default for Stack<N> {
4474
}
4575
}
4676

47-
/// Switch to running on the PSP
77+
/// Switch to unprivileged mode running on the Process Stack Pointer (PSP)
78+
///
79+
/// In Unprivileged Mode, code can no longer perform privileged operations,
80+
/// such as disabling interrupts.
81+
///
82+
#[cfg(cortex_m)]
83+
pub fn switch_to_unprivileged_psp(mut psp_stack: StackHandle, function: extern "C" fn() -> !) -> ! {
84+
unsafe { crate::asm::enter_unprivileged_psp(psp_stack.top(), function) }
85+
}
86+
87+
/// Switch to running on the Process Stack Pointer (PSP), but remain in privileged mode
4888
#[cfg(cortex_m)]
49-
pub fn switch_to_psp<const N: usize>(psp_stack: &Stack<N>, function: extern "C" fn() -> !) -> ! {
50-
let stack_top = psp_stack.get_top();
51-
unsafe { crate::asm::enter_unprivileged(stack_top, function) }
89+
pub fn switch_to_privileged_psp(mut psp_stack: StackHandle, function: extern "C" fn() -> !) -> ! {
90+
unsafe { crate::asm::enter_privileged_psp(psp_stack.top(), function) }
5291
}

testsuite/src/main.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
1414

1515
static EXCEPTION_FLAG: AtomicBool = AtomicBool::new(false);
1616

17+
const STACK_SIZE_WORDS: usize = 1024;
18+
19+
static STACK: cortex_m::psp::Stack<STACK_SIZE_WORDS> = cortex_m::psp::Stack::new();
20+
1721
#[cortex_m_rt::exception]
1822
fn PendSV() {
1923
EXCEPTION_FLAG.store(true, Ordering::SeqCst);
@@ -86,4 +90,13 @@ mod tests {
8690
});
8791
assert!(EXCEPTION_FLAG.load(Ordering::SeqCst));
8892
}
93+
94+
#[test]
95+
fn check_stack_handles() {
96+
let mut handle = super::STACK.take_handle();
97+
let top = handle.top();
98+
let bottom = handle.bottom();
99+
let delta = unsafe { top.byte_offset_from(bottom) };
100+
assert_eq!(delta as usize, super::STACK_SIZE_WORDS * 4);
101+
}
89102
}

0 commit comments

Comments
 (0)