@@ -103,7 +103,7 @@ bitfield! {
103103}
104104
105105/// Possible attribute of a SAU region.
106- #[ derive( Debug ) ]
106+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
107107pub 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 ) ]
118118pub 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