diff --git a/cortex-m/CHANGELOG.md b/cortex-m/CHANGELOG.md index 7508e6cb..34f4f4b8 100644 --- a/cortex-m/CHANGELOG.md +++ b/cortex-m/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - MSRV is 1.61 to match cortex-m-rt crate +- Add `enter_unprivileged` function to switch to unprivileged mode (on the Process Stack, or `PSP`) ## [v0.7.7] - 2023-01-03 diff --git a/cortex-m/src/asm.rs b/cortex-m/src/asm.rs index 4dc1ab07..fa5cfce5 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -171,6 +171,39 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32) } +/// Switch to unprivileged mode. +/// +/// Sets CONTROL.SPSEL (setting the program stack to be the active +/// stack) and CONTROL.nPRIV (setting unprivileged mode), updates the +/// program stack pointer to the address in `psp`, then jumps to the +/// address in `entry`. +/// +/// # Safety +/// +/// * `psp` and `entry` must point to valid stack memory and executable code, +/// respectively. +/// * `psp` must be 8 bytes aligned and point to stack top as stack grows +/// towards lower addresses. +/// * The size of the stack provided here must be large enough for your +/// program - stack overflows are obviously UB. If your processor supports +/// it, you may wish to set the `PSPLIM` register to guard against this. +#[cfg(cortex_m)] +#[inline(always)] +pub unsafe fn enter_unprivileged(psp: *const u32, entry: fn() -> !) -> ! { + core::arch::asm!( + "mrs {tmp}, CONTROL", + "orr {tmp}, #3", + "msr PSP, {psp}", + "msr CONTROL, {tmp}", + "isb", + "bx {ent}", + tmp = in(reg) 0, + psp = in(reg) psp, + ent = in(reg) entry, + options(noreturn, nomem, nostack) + ); +} + /// Bootstrap. /// /// Clears CONTROL.SPSEL (setting the main stack to be the active stack),