-
Notifications
You must be signed in to change notification settings - Fork 165
Add enter_unprivileged() function #594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add enter_unprivileged() function #594
Conversation
93ae33a
to
89152c5
Compare
cortex-m/src/asm.rs
Outdated
/// code, respectively. | ||
#[cfg(cortex_m)] | ||
#[inline(always)] | ||
pub fn enter_unprivileged(psp: &u32, entry: fn() -> !) -> ! { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think PSP should be either a raw pointer, or a new type that requires appropriate alignment. Conceptually this is similar to https://docs.rs/rp2040-hal/latest/rp2040_hal/multicore/struct.Core.html#method.spawn and we could borrow the StackAllocation
idea.
Unfortunately I don't immediately recall if PSP needs to be 4 or 8 byte aligned. We should check, and document.
The function also needs to be marked unsafe!
89152c5
to
f8f611f
Compare
Jonathan Pallant ***@***.***> writes:
@jonathanpallant requested changes on this pull request.
> @@ -244,6 +244,36 @@ pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 {
nr
}
+/// 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.
+#[cfg(cortex_m)]
+#[inline(always)]
+pub fn enter_unprivileged(psp: &u32, entry: fn() -> !) -> ! {
I think PSP should be either a raw pointer, or a new type that requires appropriate alignment. Conceptually this is similar to https://docs.rs/rp2040-hal/latest/rp2040_hal/multicore/struct.Core.html#method.spawn and we could borrow the `StackAllocation` idea.
Unfortunately I don't immediately recall if PSP needs to be 4 or 8 byte aligned. We should check, and document.
The function also needs to be marked unsafe!
Ok, I made the function unsafe and updated the constraints. I hope this
makes it clearer that the caller has to ensure them.
|
As this function will cause entry to the given function, the stack must have eight byte alignment - if I'm reading this correctly. |
This adds a function to switch to program stack and unprivileged mode. Fixes rust-embedded#583
f8f611f
to
f26be34
Compare
Jonathan Pallant ***@***.***> writes:
jonathanpallant left a comment (rust-embedded/cortex-m#594)
https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#6212stack-constraints-at-a-public-interface
> #### 6.2.1.2 Stack constraints at a public interface
>
> The stack must also conform to the following constraint at a public interface:
>
> SP mod 8 = 0. The stack must be double-word aligned.
As this function will cause entry to the given function, the stack
must have eight byte alignment - if I'm reading this correctly.
Thanks for the pointer. I corrected the comment and pushed again.
To be honest, I do not completely understand the alignment
constraints. 6.2.1.1 says the stack must be 4 bytes aligned. 6.2.1.2
says 8 bytes. As we are a "public interface", we should go with 8 bytes as
you correctly noted. But I am confused.
|
Well it must always be 4 byte aligned, and if it is 8 byte aligned it is still 4 byte aligned as well. The 4 byte alignment I think is an Arm processor requirement the 8 byte over-alignment is from the Arm Architecture Procedure Call Standard - which is a thing we agree to do, but the processor does not care whether we do or we don't. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but we should write a test case (maybe using QEMU?)
Some test code: //! Check we can switch to the PSP.
#![no_std]
#![no_main]
use core::cell::UnsafeCell;
// Contains our panic handler
use qemu_thumbv7em as _;
// Does our demft output
use defmt_semihosting as _;
/// A stack you can use as your Process Stack (PSP)
///
/// The const-param N is the size **in 32-bit words**
#[repr(align(8), C)]
struct PspStack<const N: usize> {
space: UnsafeCell<[u32; N]>,
}
impl<const N: usize> PspStack<N> {
/// Const-initialise a PspStack
///
/// Use a turbofish to specify the size, like:
///
/// ```rust
/// static PSP_STACK: PspStack::<4096> = PspStack::new();
/// ```
pub const fn new() -> PspStack<N> {
PspStack {
space: UnsafeCell::new([0; N]),
}
}
/// Return the top of the PSP stack
pub fn get_top(&self) -> *mut u32 {
let start = self.space.get() as *mut u32;
let end = unsafe { start.add(N) };
end
}
}
unsafe impl<const N: usize> Sync for PspStack<N> {}
static PSP_STACK: PspStack<4096> = PspStack::new();
#[cortex_m_rt::entry]
fn main() -> ! {
let x = 5;
defmt::info!(
"Using MSP. addr(x) = {=usize:08x}",
core::ptr::addr_of!(x) as usize
);
unsafe {
let psp_stack = PSP_STACK.get_top();
defmt::info!("PSP stack is at {=usize:08x}", psp_stack as usize);
enter_unprivileged(psp_stack, user_mode);
}
}
fn user_mode() -> ! {
let x = 5;
defmt::info!(
"Using PSP. addr(x) = {=usize:08x}",
core::ptr::addr_of!(x) as usize
);
panic!("All finished");
}
/// 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.
#[inline(always)]
pub unsafe fn enter_unprivileged(psp: *const u32, entry: fn() -> !) -> ! {
unsafe {
core::arch::asm!(
"mrs {tmp}, CONTROL",
"orr {tmp}, #2",
"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)
);
}
} Produces:
|
Having done this exercise, I now realise it's important to point out that the stack descends, and so they should pass a pointer to the top of the stack region. In fact, the word just above the stack region - because the SP is decremented before the first write. That is non-obvious. We should probably just include the |
This adds a function to switch to program stack and unprivileged mode.
Fixes #583