Skip to content

Commit 4f36c10

Browse files
committed
peripheral: add SAU init helpers and jump_to_nonsecure for ARMv8-M
- Derive Copy, Clone, PartialEq, Eq on SauRegion and SauRegionAttribute - SAU::disable_allns(): set CTRL.ALLNS=1, ENABLE=0 (all memory Non-Secure) - SAU::init(regions): disable SAU, program up to 8 regions, re-enable - jump_to_nonsecure(ns_vtor): Secure→Non-Secure boot handoff via BXNS These cover the remaining ARMv8-M TrustZone boot sequence after SAU region programming: disabling the SAU for NS-only systems, bulk-initialising regions without manually looping set_region, and transferring control to the NS image.
1 parent 6baaf6a commit 4f36c10

1 file changed

Lines changed: 95 additions & 2 deletions

File tree

cortex-m/src/peripheral/sau.rs

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ bitfield! {
103103
}
104104

105105
/// Possible attribute of a SAU region.
106-
#[derive(Debug)]
106+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107107
pub enum SauRegionAttribute {
108108
/// SAU region is Secure
109109
Secure,
@@ -114,7 +114,7 @@ pub enum SauRegionAttribute {
114114
}
115115

116116
/// Description of a SAU region.
117-
#[derive(Debug)]
117+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118118
pub struct SauRegion {
119119
/// First address of the region, its 5 least significant bits must be set to zero.
120120
pub base_address: u32,
@@ -143,6 +143,48 @@ impl SAU {
143143
self._type.read().sregion()
144144
}
145145

146+
/// Disable the SAU and mark all memory Non-Secure (ALLNS mode).
147+
///
148+
/// Sets `CTRL.ALLNS = 1`, `CTRL.ENABLE = 0`. When the SAU is disabled with ALLNS set, the
149+
/// entire address space is treated as Non-Secure (subject to any IDAU overrides). Use this
150+
/// when running entirely in Non-Secure mode with no security boundary enforcement.
151+
///
152+
/// To re-enable security boundaries, call [`init`] or [`enable`] after programming regions.
153+
#[inline]
154+
pub fn disable_allns(&mut self) {
155+
unsafe {
156+
self.ctrl.write(Ctrl(0b10)); // ALLNS=1, ENABLE=0
157+
}
158+
}
159+
160+
/// Program SAU regions and enable the SAU.
161+
///
162+
/// This is a convenience wrapper around [`set_region`] + [`enable`]:
163+
/// 1. Disables the SAU temporarily.
164+
/// 2. Programs up to 8 regions from `regions` (extras silently ignored).
165+
/// 3. Re-enables the SAU.
166+
///
167+
/// Memory not covered by any enabled region is treated as Secure once the SAU is enabled.
168+
///
169+
/// To also enable the `SecureFault` exception so TrustZone violations surface as a dedicated
170+
/// fault rather than escalating to `HardFault`, call
171+
/// `scb.enable(cortex_m::peripheral::scb::Exception::SecureFault)` after this.
172+
///
173+
/// # Errors
174+
/// Region-programming errors (bad alignment, region number out of range) are silently ignored;
175+
/// `take(8)` already bounds the region count to the hardware maximum.
176+
#[inline]
177+
pub fn init(&mut self, regions: &[SauRegion]) {
178+
// Disable while reprogramming to avoid partial-update windows.
179+
unsafe {
180+
self.ctrl.write(Ctrl(0));
181+
}
182+
for (i, &region) in regions.iter().enumerate().take(8) {
183+
let _ = self.set_region(i as u8, region);
184+
}
185+
self.enable();
186+
}
187+
146188
/// Enable the SAU.
147189
#[inline]
148190
pub fn enable(&mut self) {
@@ -241,3 +283,54 @@ impl SAU {
241283
})
242284
}
243285
}
286+
287+
/// Transfer control to the Non-Secure application. Does not return.
288+
///
289+
/// This performs the standard Secure→Non-Secure boot handoff:
290+
/// 1. Sets `SCB_NS->VTOR` to `ns_vtor` so the Non-Secure world finds its vector table.
291+
/// 2. Loads `MSP_NS` from the first word of the NS vector table (the initial NS stack pointer).
292+
/// 3. Reads the NS reset handler address from the second word of the NS vector table.
293+
/// 4. Executes `BXNS` to atomically switch to Non-Secure state and jump to the handler.
294+
///
295+
/// # Safety
296+
/// - Must be called from the Secure world after all SAU/GTZC setup is complete.
297+
/// - `ns_vtor` must point to a valid Non-Secure vector table. The Cortex-M33 requires the VTOR
298+
/// to be at least 32-byte aligned; in practice 128-byte or 256-byte alignment is typical.
299+
/// - The NS reset handler at `*(ns_vtor + 4)` must be a valid Thumb function address (bit 0 set
300+
/// in the vector table entry, as per the ARM ABI convention for vector tables).
301+
/// - Available on ARMv8-M only (`thumbv8m.base` and `thumbv8m.main`).
302+
#[cfg(armv8m)]
303+
pub unsafe fn jump_to_nonsecure(ns_vtor: u32) -> ! {
304+
// SCB_NS->VTOR is the Non-Secure alias of the SCB VTOR register (0xE002_ED08).
305+
// Writing it tells the NS world where its vector table lives before we hand off.
306+
const SCB_NS_VTOR: *mut u32 = 0xE002_ED08 as *mut u32;
307+
unsafe {
308+
SCB_NS_VTOR.write_volatile(ns_vtor);
309+
}
310+
311+
// Load the initial NS stack pointer from the first word of the NS vector table
312+
// and write it into MSP_NS.
313+
let ns_sp = unsafe { core::ptr::read_volatile(ns_vtor as *const u32) };
314+
unsafe {
315+
core::arch::asm!(
316+
"msr msp_ns, {sp}",
317+
sp = in(reg) ns_sp,
318+
options(nomem, nostack, preserves_flags),
319+
);
320+
}
321+
322+
// Read the NS reset handler address from the second word of the NS vector table.
323+
// ARM ABI: bit 0 is set in the stored value (Thumb mode marker).
324+
// BXNS requires bit 0 = 0; if bit 0 is set, it raises SecureFault (SFSR.INVTRAN).
325+
let ns_reset = unsafe { core::ptr::read_volatile((ns_vtor as *const u32).add(1)) };
326+
327+
// BXNS atomically clears bit 0, switches the processor to Non-Secure state, and
328+
// branches to the NS reset handler. This instruction does not return.
329+
unsafe {
330+
core::arch::asm!(
331+
"bxns {entry}",
332+
entry = in(reg) ns_reset & !1u32,
333+
options(noreturn),
334+
);
335+
}
336+
}

0 commit comments

Comments
 (0)