Skip to content

Commit 504e127

Browse files
committed
peripheral: add TrustZone support for SCB NSACR and NVIC ITNS (ARMv8-M)
Add the missing ARMv8-M TrustZone registers and typed helper methods: - SCB: add `nsacr` field (Non-Secure Access Control Register, 0xE000_ED8C) gated behind `#[cfg(armv8m)]`. Add `enable_nonsecure_fpu()`, `disable_nonsecure_fpu()`, and `is_nonsecure_fpu_enabled()` helpers on SCB for controlling CP10/CP11 (FPU) access from Non-Secure code. - NVIC: add `route_to_nonsecure<I>()`, `route_to_secure<I>()`, and `is_routed_to_nonsecure<I>()` typed methods on NVIC, using the existing `itns` register field (already present since #580). Gated behind `#[cfg(armv8m)]`. - test: add address assertions for `nvic.itns` (0xE000_E380) and `scb.nsacr` (0xE000_ED8C), both gated `#[cfg(armv8m)]`.
1 parent 6baaf6a commit 504e127

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

cortex-m/src/peripheral/nvic.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,55 @@ impl NVIC {
257257
unsafe { (*Self::PTR).icpr[usize::from(nr / 32)].write(1 << (nr % 32)) }
258258
}
259259

260+
/// Route `interrupt` to the Non-Secure world (ARMv8-M only).
261+
///
262+
/// Sets the corresponding ITNS bit so the interrupt is taken as Non-Secure
263+
/// and will not preempt Secure execution. Call this for every peripheral
264+
/// interrupt handled by the Non-Secure application before jumping to it.
265+
///
266+
/// # Safety
267+
/// Must be called from the Secure world. Routing an interrupt to Non-Secure
268+
/// while Secure handlers depend on it can violate security invariants.
269+
#[cfg(armv8m)]
270+
#[inline]
271+
pub unsafe fn route_to_nonsecure<I>(interrupt: I)
272+
where
273+
I: InterruptNumber,
274+
{
275+
let nr = interrupt.number();
276+
unsafe { (*Self::PTR).itns[usize::from(nr / 32)].modify(|v| v | (1 << (nr % 32))) }
277+
}
278+
279+
/// Route `interrupt` back to the Secure world (ARMv8-M only).
280+
///
281+
/// Clears the corresponding ITNS bit. After this call the interrupt
282+
/// targets Secure state (the default after reset).
283+
///
284+
/// # Safety
285+
/// Must be called from the Secure world.
286+
#[cfg(armv8m)]
287+
#[inline]
288+
pub unsafe fn route_to_secure<I>(interrupt: I)
289+
where
290+
I: InterruptNumber,
291+
{
292+
let nr = interrupt.number();
293+
unsafe { (*Self::PTR).itns[usize::from(nr / 32)].modify(|v| v & !(1 << (nr % 32))) }
294+
}
295+
296+
/// Returns `true` if `interrupt` is routed to the Non-Secure world (ARMv8-M only).
297+
#[cfg(armv8m)]
298+
#[inline]
299+
pub fn is_routed_to_nonsecure<I>(interrupt: I) -> bool
300+
where
301+
I: InterruptNumber,
302+
{
303+
let nr = interrupt.number();
304+
let mask = 1 << (nr % 32);
305+
// NOTE(unsafe) atomic read with no side effects
306+
unsafe { ((*Self::PTR).itns[usize::from(nr / 32)].read() & mask) == mask }
307+
}
308+
260309
#[cfg(armv6m)]
261310
#[inline]
262311
fn ipr_index<I>(interrupt: I) -> usize

cortex-m/src/peripheral/scb.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ pub struct RegisterBlock {
100100
pub cpacr: RW<u32>,
101101
#[cfg(armv6m)]
102102
_reserved9: u32,
103+
104+
/// Non-Secure Access Control (only present on ARMv8-M)
105+
///
106+
/// Controls whether Non-Secure code can access coprocessors. Bits 10–11
107+
/// correspond to CP10 and CP11 (the FPU): setting them allows Non-Secure
108+
/// code to use floating-point instructions.
109+
#[cfg(armv8m)]
110+
pub nsacr: RW<u32>,
103111
}
104112

105113
/// FPU access mode
@@ -171,6 +179,40 @@ impl SCB {
171179
}
172180
}
173181

182+
/// ARMv8-M TrustZone coprocessor access control.
183+
#[cfg(armv8m)]
184+
impl SCB {
185+
const SCB_NSACR_CP10_CP11: u32 = 0b11 << 10;
186+
187+
/// Allow Non-Secure code to use the FPU (CP10 and CP11).
188+
///
189+
/// Sets NSACR bits 10–11 so that Non-Secure threads can execute
190+
/// floating-point instructions. Without this, any NS FPU instruction
191+
/// raises a UsageFault.
192+
///
193+
/// Call this before jumping to Non-Secure code if the NS application
194+
/// uses floating-point.
195+
#[inline]
196+
pub fn enable_nonsecure_fpu(&mut self) {
197+
unsafe { self.nsacr.modify(|v| v | Self::SCB_NSACR_CP10_CP11) }
198+
}
199+
200+
/// Deny Non-Secure code from using the FPU.
201+
///
202+
/// Clears NSACR bits 10–11.
203+
#[inline]
204+
pub fn disable_nonsecure_fpu(&mut self) {
205+
unsafe { self.nsacr.modify(|v| v & !Self::SCB_NSACR_CP10_CP11) }
206+
}
207+
208+
/// Returns `true` if Non-Secure code is allowed to use the FPU.
209+
#[inline]
210+
pub fn is_nonsecure_fpu_enabled() -> bool {
211+
// NOTE(unsafe) atomic read with no side effects
212+
unsafe { ((*Self::PTR).nsacr.read() & Self::SCB_NSACR_CP10_CP11) != 0 }
213+
}
214+
}
215+
174216
impl SCB {
175217
/// Returns the active exception number
176218
#[inline]

cortex-m/src/peripheral/test.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ fn nvic() {
116116
assert_eq!(address(&nvic.ispr), 0xE000E200);
117117
assert_eq!(address(&nvic.icpr), 0xE000E280);
118118
assert_eq!(address(&nvic.iabr), 0xE000E300);
119+
#[cfg(armv8m)]
120+
assert_eq!(address(&nvic.itns), 0xE000_E380);
121+
#[cfg(armv8m)]
122+
assert_eq!(address(&nvic.itns[1]), 0xE000_E384);
119123
assert_eq!(address(&nvic.ipr), 0xE000E400);
120124
#[cfg(not(armv6m))]
121125
assert_eq!(address(&nvic.stir), 0xE000EF00);
@@ -139,6 +143,8 @@ fn scb() {
139143
assert_eq!(address(&scb.bfar), 0xE000_ED38);
140144
assert_eq!(address(&scb.afsr), 0xE000_ED3C);
141145
assert_eq!(address(&scb.cpacr), 0xE000_ED88);
146+
#[cfg(armv8m)]
147+
assert_eq!(address(&scb.nsacr), 0xE000_ED8C);
142148
}
143149

144150
#[test]

0 commit comments

Comments
 (0)