Limiting access to priviledged special registers

Has anybody thought about ways of limiting accesses to priviledged special registers (or whatever ARM calls them in Cortex M)?

I.e. if I have a baremetal application, it always starts in the priviledged mode and I can access all the registers, and then perhaps I may have some tasks executing in unpriviledged mode where an access to priviledged registers triggers an exception. Is there any way of detecting this at compile time?

My problem is that on Cortex M there is only a dozen of such registers and maybe it is manageable, but I've been working on a proof-of-concept of porting Rust to another processor architecture, which is neither ARM nor RISC-V, and which has hundreds and hundreds of configuration-dependent special registers, and they have well-defined access rights for priviledged/unpriviledged SW (or kernel/user modes, as we call them).

In the cortex-m crate these registers are just hardcoded in the src/register/ directory. I don't want to hardcode them in my case, so I'm considering to auto-generate a register access crate from an IP-XACT XML file, similarly to how peripheral access crates like stm32-rs are built from SVD. But I have no clue how to deal with access rights. The only thought I have so far is to logically divide the crate into kernel-only and kernel-and-user registers and allow reexporting them separately, and then trust the programmer to use this correctly...


One way you can do this is through the way you transition between privileged/unprivileged code.

The idea being that you can access the privileged registers through some sort of token, but to transition from privileged to unprivileged you need to give up ownership of that token.

Imagine something like this:

// The application starts in privileged mode
let mut peripherals = stm32f30x::Peripherals::take().unwrap();

peripherals.some_privileged_register.write(|w| w.bits(42));

let (privileged, unprivileged) = peripherals.split();

run_in_unprivileged_mode(privileged, || {
  // we can still use the unprivileged registers
  unprivileged.GPIOA.write(|w| w.bits(1));

Then, the run_in_unprivileged_mode() function will set things up so any temporary jumps back into privileged code give you the privileged set of registers back (e.g. it gives your callback a &mut PrivilegedRegisters argument).

1 Like