cortex_m/peripheral/sau.rs
1//! Security Attribution Unit
2//!
3//! *NOTE* Available only on Armv8-M and Armv8.1-M, for the following Rust target triples:
4//! * `thumbv8m.base-none-eabi`
5//! * `thumbv8m.main-none-eabi`
6//! * `thumbv8m.main-none-eabihf`
7//!
8//! For reference please check the section B8.3 of the Armv8-M Architecture Reference Manual.
9
10use crate::interrupt;
11use crate::peripheral::SAU;
12use bitfield::bitfield;
13use volatile_register::{RO, RW};
14
15/// Register block
16#[repr(C)]
17pub struct RegisterBlock {
18 /// Control Register
19 pub ctrl: RW<Ctrl>,
20 /// Type Register
21 pub _type: RO<Type>,
22 /// Region Number Register
23 pub rnr: RW<Rnr>,
24 /// Region Base Address Register
25 pub rbar: RW<Rbar>,
26 /// Region Limit Address Register
27 pub rlar: RW<Rlar>,
28 /// Secure Fault Status Register
29 pub sfsr: RO<Sfsr>,
30 /// Secure Fault Address Register
31 pub sfar: RO<Sfar>,
32}
33
34bitfield! {
35 /// Control Register description
36 #[repr(C)]
37 #[derive(Copy, Clone)]
38 pub struct Ctrl(u32);
39 get_enable, set_enable: 0;
40 get_allns, set_allns: 1;
41}
42
43bitfield! {
44 /// Type Register description
45 #[repr(C)]
46 #[derive(Copy, Clone)]
47 pub struct Type(u32);
48 u8;
49 sregion, _: 7, 0;
50}
51
52bitfield! {
53 /// Region Number Register description
54 #[repr(C)]
55 #[derive(Copy, Clone)]
56 pub struct Rnr(u32);
57 u8;
58 get_region, set_region: 7, 0;
59}
60
61bitfield! {
62 /// Region Base Address Register description
63 #[repr(C)]
64 #[derive(Copy, Clone)]
65 pub struct Rbar(u32);
66 u32;
67 get_baddr, set_baddr: 31, 5;
68}
69
70bitfield! {
71 /// Region Limit Address Register description
72 #[repr(C)]
73 #[derive(Copy, Clone)]
74 pub struct Rlar(u32);
75 u32;
76 get_laddr, set_laddr: 31, 5;
77 get_nsc, set_nsc: 1;
78 get_enable, set_enable: 0;
79}
80
81bitfield! {
82 /// Secure Fault Status Register description
83 #[repr(C)]
84 #[derive(Copy, Clone)]
85 pub struct Sfsr(u32);
86 impl Debug;
87 /// Invalid Entry Point
88 pub invep, _: 0;
89 /// Invalid Integrity Signature
90 pub invis, _: 1;
91 /// Invalid Exception Return
92 pub inver, _: 2;
93 /// Attribution Unit Violation
94 pub auviol, _: 3;
95 /// Invalid Transition
96 pub invtran, _: 4;
97 /// Lazy state preservation error
98 pub lsperr, _: 5;
99 /// SFAR is valid
100 pub sfarvalid, _: 6;
101 /// Lazy state error
102 pub lserr, _: 7;
103}
104
105bitfield! {
106 /// Secure Fault Address Register description
107 #[repr(C)]
108 #[derive(Copy, Clone)]
109 pub struct Sfar(u32);
110 impl Debug;
111 u32;
112 /// Faulting memory address
113 ///
114 /// Only valid if SFSR.SFARVALID = 1
115 pub address, _: 31, 0;
116}
117
118/// Possible attribute of a SAU region.
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum SauRegionAttribute {
121 /// SAU region is Secure
122 Secure,
123 /// SAU region is Non-Secure Callable
124 NonSecureCallable,
125 /// SAU region is Non-Secure
126 NonSecure,
127}
128
129/// Description of a SAU region.
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub struct SauRegion {
132 /// First address of the region, its 5 least significant bits must be set to zero.
133 pub base_address: u32,
134 /// Last address of the region, its 5 least significant bits must be set to one.
135 pub limit_address: u32,
136 /// Attribute of the region.
137 pub attribute: SauRegionAttribute,
138}
139
140/// Possible error values returned by the SAU methods.
141#[derive(Debug)]
142pub enum SauError {
143 /// The region number parameter to set or get a region must be between 0 and
144 /// region_numbers() - 1.
145 RegionNumberTooBig,
146 /// Bits 0 to 4 of the base address of a SAU region must be set to zero.
147 WrongBaseAddress,
148 /// Bits 0 to 4 of the limit address of a SAU region must be set to one.
149 WrongLimitAddress,
150 /// The number of regions passed to [`SAU::init`] exceeds the number of regions implemented
151 /// in hardware (as reported by [`SAU::region_numbers`]).
152 TooManyRegions,
153}
154
155impl SAU {
156 /// Get the number of implemented SAU regions.
157 #[inline]
158 pub fn region_numbers(&self) -> u8 {
159 self._type.read().sregion()
160 }
161
162 /// Disable the SAU and mark all memory Non-Secure (ALLNS mode).
163 ///
164 /// Sets `CTRL.ALLNS = 1`, `CTRL.ENABLE = 0`. When the SAU is disabled with
165 /// ALLNS set, the entire address space is treated as Non-Secure (subject to
166 /// any IDAU overrides). Use this when running entirely in Non-Secure mode
167 /// with no security boundary enforcement.
168 ///
169 /// To re-enable security boundaries, call [`init`](Self::init) or
170 /// [`enable`](Self::enable) after programming regions.
171 #[inline]
172 pub fn disable_allns(&mut self) {
173 unsafe {
174 self.ctrl.write(Ctrl(0b10)); // ALLNS=1, ENABLE=0
175 }
176 }
177
178 /// Program SAU regions and enable the SAU.
179 ///
180 /// This is a convenience wrapper around [`set_region`](Self::set_region) +
181 /// [`enable`](Self::enable):
182 ///
183 /// 1. Disables the SAU temporarily.
184 /// 2. Programs all regions from `regions`.
185 /// 3. Re-enables the SAU.
186 ///
187 /// Memory not covered by any enabled region is treated as Secure once the
188 /// SAU is enabled.
189 ///
190 /// To also enable the `SecureFault` exception so TrustZone violations
191 /// surface as a dedicated fault rather than escalating to `HardFault`, call
192 /// `scb.enable(cortex_m::peripheral::scb::Exception::SecureFault)` after
193 /// this.
194 ///
195 /// # Errors
196 /// Returns [`SauError::TooManyRegions`] if `regions.len()` exceeds the
197 /// number of regions implemented in hardware (see
198 /// [`region_numbers`](Self::region_numbers). Returns other [`SauError`]
199 /// variants if any region descriptor has a misaligned base or limit
200 /// address.
201 ///
202 /// On error the SAU is left disabled (in the state set at step 1 above).
203 #[inline]
204 pub fn init(&mut self, regions: &[SauRegion]) -> Result<(), SauError> {
205 if regions.len() > self.region_numbers() as usize {
206 return Err(SauError::TooManyRegions);
207 }
208 // Disable while reprogramming to avoid partial-update windows.
209 unsafe {
210 self.ctrl.write(Ctrl(0));
211 }
212 for (i, ®ion) in regions.iter().enumerate() {
213 self.set_region(i as u8, region)?;
214 }
215 self.enable();
216 Ok(())
217 }
218
219 /// Enable the SAU.
220 #[inline]
221 pub fn enable(&mut self) {
222 unsafe {
223 self.ctrl.modify(|mut ctrl| {
224 ctrl.set_enable(true);
225 ctrl
226 });
227 }
228 }
229
230 /// Set a SAU region to a region number.
231 /// SAU regions must be 32 bytes aligned and their sizes must be a multiple of 32 bytes. It
232 /// means that the 5 least significant bits of the base address of a SAU region must be set to
233 /// zero and the 5 least significant bits of the limit address must be set to one.
234 /// The region number must be valid.
235 /// This function is executed under a critical section to prevent having inconsistent results.
236 #[inline]
237 pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
238 interrupt::free(|_| {
239 let base_address = region.base_address;
240 let limit_address = region.limit_address;
241 let attribute = region.attribute;
242
243 if region_number >= self.region_numbers() {
244 Err(SauError::RegionNumberTooBig)
245 } else if base_address & 0x1F != 0 {
246 Err(SauError::WrongBaseAddress)
247 } else if limit_address & 0x1F != 0x1F {
248 Err(SauError::WrongLimitAddress)
249 } else {
250 // All fields of these registers are going to be modified so we don't need to read them
251 // before.
252 let mut rnr = Rnr(0);
253 let mut rbar = Rbar(0);
254 let mut rlar = Rlar(0);
255
256 rnr.set_region(region_number);
257 rbar.set_baddr(base_address >> 5);
258 rlar.set_laddr(limit_address >> 5);
259
260 match attribute {
261 SauRegionAttribute::Secure => {
262 rlar.set_nsc(false);
263 rlar.set_enable(false);
264 }
265 SauRegionAttribute::NonSecureCallable => {
266 rlar.set_nsc(true);
267 rlar.set_enable(true);
268 }
269 SauRegionAttribute::NonSecure => {
270 rlar.set_nsc(false);
271 rlar.set_enable(true);
272 }
273 }
274
275 unsafe {
276 self.rnr.write(rnr);
277 self.rbar.write(rbar);
278 self.rlar.write(rlar);
279 }
280
281 Ok(())
282 }
283 })
284 }
285
286 /// Get a region from the SAU.
287 /// The region number must be valid.
288 /// This function is executed under a critical section to prevent having inconsistent results.
289 #[inline]
290 pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
291 interrupt::free(|_| {
292 if region_number >= self.region_numbers() {
293 Err(SauError::RegionNumberTooBig)
294 } else {
295 unsafe {
296 self.rnr.write(Rnr(region_number.into()));
297 }
298
299 let rbar = self.rbar.read();
300 let rlar = self.rlar.read();
301
302 let attribute = match (rlar.get_enable(), rlar.get_nsc()) {
303 (false, _) => SauRegionAttribute::Secure,
304 (true, false) => SauRegionAttribute::NonSecure,
305 (true, true) => SauRegionAttribute::NonSecureCallable,
306 };
307
308 Ok(SauRegion {
309 base_address: rbar.get_baddr() << 5,
310 limit_address: (rlar.get_laddr() << 5) | 0x1F,
311 attribute,
312 })
313 }
314 })
315 }
316}