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