diff --git a/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs b/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs index 70eee1a1..bd5fd959 100644 --- a/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs +++ b/cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs @@ -11,5 +11,5 @@ fn foo() -> ! { loop {} } -#[interrupt] //~ ERROR failed to resolve: use of unresolved module or unlinked crate `interrupt` +#[interrupt] //~ ERROR cannot find module or crate `interrupt` fn USART1() {} diff --git a/cortex-m/src/peripheral/nvic.rs b/cortex-m/src/peripheral/nvic.rs index 60411f0e..7e444ef2 100644 --- a/cortex-m/src/peripheral/nvic.rs +++ b/cortex-m/src/peripheral/nvic.rs @@ -257,6 +257,60 @@ impl NVIC { unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) } } + /// Route `interrupt` to the Non-Secure world (ARMv8-M only). + /// + /// Sets the corresponding ITNS bit so the interrupt is taken as Non-Secure + /// and will not preempt Secure execution. Call this for every peripheral + /// interrupt handled by the Non-Secure application before jumping to it. + /// + /// # Safety + /// Must be called from the Secure world. Routing an interrupt to Non-Secure + /// while Secure handlers depend on it can violate security invariants. + #[cfg(armv8m)] + #[inline] + pub unsafe fn route_to_nonsecure(interrupt: I) + where + I: InterruptNumber, + { + let nr = interrupt.number(); + let group_idx = usize::from(nr / 32); + let bit_mask = 1 << (nr % 32); + unsafe { (*Self::PTR).itns[group_idx].modify(|v| v | bit_mask) } + } + + /// Route `interrupt` back to the Secure world (ARMv8-M only). + /// + /// Clears the corresponding ITNS bit. After this call the interrupt + /// targets Secure state (the default after reset). + /// + /// # Safety + /// Must be called from the Secure world. + #[cfg(armv8m)] + #[inline] + pub unsafe fn route_to_secure(interrupt: I) + where + I: InterruptNumber, + { + let nr = interrupt.number(); + let group_idx = usize::from(nr / 32); + let bit_mask = 1 << (nr % 32); + unsafe { (*Self::PTR).itns[group_idx].modify(|v| v & !bit_mask) } + } + + /// Returns `true` if `interrupt` is routed to the Non-Secure world (ARMv8-M only). + #[cfg(armv8m)] + #[inline] + pub fn is_routed_to_nonsecure(interrupt: I) -> bool + where + I: InterruptNumber, + { + let nr = interrupt.number(); + let group_idx = usize::from(nr / 32); + let bit_mask = 1 << (nr % 32); + // NOTE(unsafe) atomic read with no side effects + unsafe { ((*Self::PTR).itns[group_idx].read() & bit_mask) == bit_mask } + } + #[cfg(armv6m)] #[inline] fn ipr_index(interrupt: I) -> usize diff --git a/cortex-m/src/peripheral/scb.rs b/cortex-m/src/peripheral/scb.rs index 6d9d4700..bf773d3d 100644 --- a/cortex-m/src/peripheral/scb.rs +++ b/cortex-m/src/peripheral/scb.rs @@ -100,6 +100,14 @@ pub struct RegisterBlock { pub cpacr: RW, #[cfg(armv6m)] _reserved9: u32, + + /// Non-Secure Access Control (only present on ARMv8-M) + /// + /// Controls whether Non-Secure code can access coprocessors. Bits 10–11 + /// correspond to CP10 and CP11 (the FPU): setting them allows Non-Secure + /// code to use floating-point instructions. + #[cfg(armv8m)] + pub nsacr: RW, } /// FPU access mode @@ -171,6 +179,40 @@ impl SCB { } } +/// ARMv8-M TrustZone coprocessor access control. +#[cfg(armv8m)] +impl SCB { + const SCB_NSACR_CP10_CP11: u32 = 0b11 << 10; + + /// Allow Non-Secure code to use the FPU (CP10 and CP11). + /// + /// Sets NSACR bits 10–11 so that Non-Secure threads can execute + /// floating-point instructions. Without this, any NS FPU instruction + /// raises a UsageFault. + /// + /// Call this before jumping to Non-Secure code if the NS application + /// uses floating-point. + #[inline] + pub fn enable_nonsecure_fpu(&mut self) { + unsafe { self.nsacr.modify(|v| v | Self::SCB_NSACR_CP10_CP11) } + } + + /// Deny Non-Secure code from using the FPU. + /// + /// Clears NSACR bits 10–11. + #[inline] + pub fn disable_nonsecure_fpu(&mut self) { + unsafe { self.nsacr.modify(|v| v & !Self::SCB_NSACR_CP10_CP11) } + } + + /// Returns `true` if Non-Secure code is allowed to use the FPU. + #[inline] + pub fn is_nonsecure_fpu_enabled() -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { ((*Self::PTR).nsacr.read() & Self::SCB_NSACR_CP10_CP11) != 0 } + } +} + impl SCB { /// Returns the active exception number #[inline] diff --git a/cortex-m/src/peripheral/test.rs b/cortex-m/src/peripheral/test.rs index cab064aa..f22ee4d5 100644 --- a/cortex-m/src/peripheral/test.rs +++ b/cortex-m/src/peripheral/test.rs @@ -116,6 +116,10 @@ fn nvic() { assert_eq!(address(&nvic.ispr), 0xE000E200); assert_eq!(address(&nvic.icpr), 0xE000E280); assert_eq!(address(&nvic.iabr), 0xE000E300); + #[cfg(armv8m)] + assert_eq!(address(&nvic.itns), 0xE000_E380); + #[cfg(armv8m)] + assert_eq!(address(&nvic.itns[1]), 0xE000_E384); assert_eq!(address(&nvic.ipr), 0xE000E400); #[cfg(not(armv6m))] assert_eq!(address(&nvic.stir), 0xE000EF00); @@ -139,6 +143,8 @@ fn scb() { assert_eq!(address(&scb.bfar), 0xE000_ED38); assert_eq!(address(&scb.afsr), 0xE000_ED3C); assert_eq!(address(&scb.cpacr), 0xE000_ED88); + #[cfg(armv8m)] + assert_eq!(address(&scb.nsacr), 0xE000_ED8C); } #[test]