Skip to content

Commit 1436479

Browse files
authored
Merge pull request #608 from thejpster/add-unprivileged-mode
Add helper function to enter unprivileged mode
2 parents a3ff54b + 155ab85 commit 1436479

File tree

5 files changed

+212
-17
lines changed

5 files changed

+212
-17
lines changed

cortex-m/src/asm.rs

Lines changed: 67 additions & 15 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,19 +194,71 @@ 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: fn() -> !) -> ! {
198-
core::arch::asm!(
199-
"mrs {tmp}, CONTROL",
200-
"orr {tmp}, #3",
201-
"msr PSP, {psp}",
202-
"msr CONTROL, {tmp}",
203-
"isb",
204-
"bx {ent}",
205-
tmp = in(reg) 0,
206-
psp = in(reg) psp,
207-
ent = in(reg) entry,
208-
options(noreturn, nomem, nostack)
209-
);
197+
pub unsafe fn enter_unprivileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
198+
use crate::register::control::{Control, Npriv, Spsel};
199+
const CONTROL_FLAGS: u32 = {
200+
Control::from_bits(0)
201+
.with_npriv(Npriv::Unprivileged)
202+
.with_spsel(Spsel::Psp)
203+
.bits()
204+
};
205+
unsafe {
206+
core::arch::asm!(
207+
"msr PSP, {psp}",
208+
"mrs {tmp}, CONTROL",
209+
"orrs {tmp}, {flags}",
210+
"msr CONTROL, {tmp}",
211+
"isb",
212+
"bx {ent}",
213+
tmp = in(reg) 0,
214+
flags = in(reg) CONTROL_FLAGS,
215+
psp = in(reg) psp,
216+
ent = in(reg) entry,
217+
options(noreturn, nostack)
218+
);
219+
}
220+
}
221+
222+
/// Switch to using the Process Stack, but remain in Privileged Mode
223+
///
224+
/// Sets CONTROL.SPSEL (setting the Process Stack to be the active stack) but
225+
/// leaves CONTROL.nPRIV alone, updates the program stack pointer to the
226+
/// address in `psp`, then jumps to the address in `entry`.
227+
///
228+
/// # Safety
229+
///
230+
/// * `psp` and `entry` must point to valid stack memory and executable code,
231+
/// respectively.
232+
/// * `psp` must be 8 bytes aligned and point to stack top as stack grows
233+
/// towards lower addresses.
234+
/// * The size of the stack provided here must be large enough for your
235+
/// program - stack overflows are obviously UB. If your processor supports
236+
/// it, you may wish to set the `PSPLIM` register to guard against this.
237+
#[cfg(cortex_m)]
238+
#[inline(always)]
239+
pub unsafe fn enter_privileged_psp(psp: *const u32, entry: extern "C" fn() -> !) -> ! {
240+
use crate::register::control::{Control, Npriv, Spsel};
241+
const CONTROL_FLAGS: u32 = {
242+
Control::from_bits(0)
243+
.with_npriv(Npriv::Privileged)
244+
.with_spsel(Spsel::Psp)
245+
.bits()
246+
};
247+
unsafe {
248+
core::arch::asm!(
249+
"msr PSP, {psp}",
250+
"mrs {tmp}, CONTROL",
251+
"orrs {tmp}, {flags}",
252+
"msr CONTROL, {tmp}",
253+
"isb",
254+
"bx {ent}",
255+
tmp = in(reg) 0,
256+
flags = in(reg) CONTROL_FLAGS,
257+
psp = in(reg) psp,
258+
ent = in(reg) entry,
259+
options(noreturn, nostack)
260+
);
261+
}
210262
}
211263

212264
/// Bootstrap.

cortex-m/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ pub mod interrupt;
111111
pub mod itm;
112112
pub mod peripheral;
113113
pub mod prelude;
114+
pub mod psp;
114115
pub mod register;
115116

116117
pub use crate::peripheral::Peripherals;

cortex-m/src/psp.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//! Process Stack Pointer support
2+
3+
// This is a useful lint for functions like 'asm::wfi()' but it's not a useful
4+
// lint here.
5+
#![allow(clippy::missing_inline_in_public_items)]
6+
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 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 bottom of the stack
23+
pub fn bottom(&mut self) -> *mut u32 {
24+
self.0
25+
}
26+
}
27+
28+
/// A stack you can use as your Process Stack (PSP)
29+
///
30+
/// The const-param N is the size **in 32-bit words**
31+
#[repr(align(8), C)]
32+
pub struct Stack<const N: usize> {
33+
space: UnsafeCell<[u32; N]>,
34+
taken: AtomicBool,
35+
}
36+
37+
impl<const N: usize> Stack<N> {
38+
/// Const-initialise a Stack
39+
///
40+
/// Use a turbofish to specify the size, like:
41+
///
42+
/// ```rust
43+
/// # use cortex_m::psp::Stack;
44+
/// static PSP_STACK: Stack::<4096> = Stack::new();
45+
/// fn example() {
46+
/// let handle = PSP_STACK.take_handle();
47+
/// // ...
48+
/// }
49+
/// ```
50+
pub const fn new() -> Stack<N> {
51+
Stack {
52+
space: UnsafeCell::new([0; N]),
53+
taken: AtomicBool::new(false),
54+
}
55+
}
56+
57+
/// Return the top of the stack
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+
64+
let start = self.space.get() as *mut u32;
65+
StackHandle(start, N)
66+
}
67+
}
68+
69+
unsafe impl<const N: usize> Sync for Stack<N> {}
70+
71+
impl<const N: usize> core::default::Default for Stack<N> {
72+
fn default() -> Self {
73+
Stack::new()
74+
}
75+
}
76+
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+
// set the stack limit
85+
#[cfg(armv8m_main)]
86+
unsafe {
87+
crate::register::psplim::write(psp_stack.bottom() as u32);
88+
}
89+
// do the switch
90+
unsafe {
91+
crate::asm::enter_unprivileged_psp(psp_stack.top(), function);
92+
}
93+
}
94+
95+
/// Switch to running on the Process Stack Pointer (PSP), but remain in privileged mode
96+
#[cfg(cortex_m)]
97+
pub fn switch_to_privileged_psp(mut psp_stack: StackHandle, function: extern "C" fn() -> !) -> ! {
98+
// set the stack limit
99+
#[cfg(armv8m_main)]
100+
unsafe {
101+
crate::register::psplim::write(psp_stack.bottom() as u32);
102+
}
103+
// do the switch
104+
unsafe {
105+
crate::asm::enter_privileged_psp(psp_stack.top(), function);
106+
}
107+
}

cortex-m/src/register/control.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ pub struct Control {
99
impl Control {
1010
/// Creates a `Control` value from raw bits.
1111
#[inline]
12-
pub fn from_bits(bits: u32) -> Self {
12+
pub const fn from_bits(bits: u32) -> Self {
1313
Self { bits }
1414
}
1515

1616
/// Returns the contents of the register as raw bits
1717
#[inline]
18-
pub fn bits(self) -> u32 {
18+
pub const fn bits(self) -> u32 {
1919
self.bits
2020
}
2121

@@ -39,6 +39,17 @@ impl Control {
3939
}
4040
}
4141

42+
/// Sets the thread mode privilege level value (nPRIV).
43+
#[inline]
44+
pub const fn with_npriv(self, npriv: Npriv) -> Self {
45+
let mask = 1 << 0;
46+
let bits = match npriv {
47+
Npriv::Unprivileged => self.bits | mask,
48+
Npriv::Privileged => self.bits & !mask,
49+
};
50+
Self { bits }
51+
}
52+
4253
/// Currently active stack pointer
4354
#[inline]
4455
pub fn spsel(self) -> Spsel {
@@ -59,6 +70,17 @@ impl Control {
5970
}
6071
}
6172

73+
/// Sets the SPSEL value.
74+
#[inline]
75+
pub const fn with_spsel(self, spsel: Spsel) -> Self {
76+
let mask = 1 << 1;
77+
let bits = match spsel {
78+
Spsel::Psp => self.bits | mask,
79+
Spsel::Msp => self.bits & !mask,
80+
};
81+
Self { bits }
82+
}
83+
6284
/// Whether context floating-point is currently active
6385
#[inline]
6486
pub fn fpca(self) -> Fpca {

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.offset_from(bottom) };
100+
assert_eq!(delta as usize, super::STACK_SIZE_WORDS);
101+
}
89102
}

0 commit comments

Comments
 (0)