Skip to main content

cortex_m/
psp.rs

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
7use core::{
8    cell::UnsafeCell,
9    sync::atomic::{AtomicBool, Ordering},
10};
11
12/// Represents access to a [`Stack`]
13pub struct StackHandle(*mut u32, usize);
14
15impl 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)]
32pub struct Stack<const N: usize> {
33    space: UnsafeCell<[u32; N]>,
34    taken: AtomicBool,
35}
36
37impl<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
69unsafe impl<const N: usize> Sync for Stack<N> {}
70
71impl<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///
82pub fn switch_to_unprivileged_psp(mut psp_stack: StackHandle, function: extern "C" fn() -> !) -> ! {
83    // set the stack limit
84    #[cfg(armv8m_main)]
85    unsafe {
86        crate::register::psplim::write(psp_stack.bottom() as u32);
87    }
88    // do the switch
89    unsafe {
90        crate::asm::enter_unprivileged_psp(psp_stack.top(), function);
91    }
92}
93
94/// Switch to running on the Process Stack Pointer (PSP), but remain in privileged mode
95pub fn switch_to_privileged_psp(mut psp_stack: StackHandle, function: extern "C" fn() -> !) -> ! {
96    // set the stack limit
97    #[cfg(armv8m_main)]
98    unsafe {
99        crate::register::psplim::write(psp_stack.bottom() as u32);
100    }
101    // do the switch
102    unsafe {
103        crate::asm::enter_privileged_psp(psp_stack.top(), function);
104    }
105}