Skip to content

Commit 41d8843

Browse files
committed
Add Security Attribution Unit support
The SAU is a Armv8-M core peripheral that, alongside the Implementation Defined Attribution Unit, manages the security attribution of the memory zones. This driver provides abstraction to help setting the SAU up. Signed-off-by: Hugues de Valon <[email protected]>
1 parent ce538b2 commit 41d8843

File tree

3 files changed

+278
-1
lines changed

3 files changed

+278
-1
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ links = "cortex-m" # prevent multiple versions of this crate to be linked toget
1919
aligned = "0.3.1"
2020
bare-metal = { version = "0.2.0", features = ["const-fn"] }
2121
volatile-register = "0.2.0"
22+
bitfield = "0.13.2"
2223

2324
[features]
2425
const-fn = []

src/peripheral/mod.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
7171
// TODO stand-alone registers: ICTR, ACTLR and STIR
7272

73-
7473
use core::marker::PhantomData;
7574
use core::ops;
7675

@@ -90,6 +89,8 @@ pub mod fpu;
9089
pub mod itm;
9190
pub mod mpu;
9291
pub mod nvic;
92+
#[cfg(armv8m)]
93+
pub mod sau;
9394
pub mod scb;
9495
pub mod syst;
9596
#[cfg(not(armv6m))]
@@ -130,6 +131,9 @@ pub struct Peripherals {
130131
/// Nested Vector Interrupt Controller
131132
pub NVIC: NVIC,
132133

134+
/// Security Attribution Unit
135+
pub SAU: SAU,
136+
133137
/// System Control Block
134138
pub SCB: SCB,
135139

@@ -191,6 +195,9 @@ impl Peripherals {
191195
NVIC: NVIC {
192196
_marker: PhantomData,
193197
},
198+
SAU: SAU {
199+
_marker: PhantomData,
200+
},
194201
SCB: SCB {
195202
_marker: PhantomData,
196203
},
@@ -443,6 +450,32 @@ impl ops::Deref for NVIC {
443450
}
444451
}
445452

453+
/// Security Attribution Unit
454+
pub struct SAU {
455+
_marker: PhantomData<*const ()>,
456+
}
457+
458+
unsafe impl Send for SAU {}
459+
460+
#[cfg(armv8m)]
461+
impl SAU {
462+
/// Returns a pointer to the register block
463+
#[inline(always)]
464+
pub fn ptr() -> *const sau::RegisterBlock {
465+
0xE000_EDD0 as *const _
466+
}
467+
}
468+
469+
#[cfg(armv8m)]
470+
impl ops::Deref for SAU {
471+
type Target = self::sau::RegisterBlock;
472+
473+
#[inline(always)]
474+
fn deref(&self) -> &Self::Target {
475+
unsafe { &*Self::ptr() }
476+
}
477+
}
478+
446479
/// System Control Block
447480
pub struct SCB {
448481
_marker: PhantomData<*const ()>,

src/peripheral/sau.rs

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
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+
10+
use crate::interrupt;
11+
use crate::peripheral::SAU;
12+
use bitfield::bitfield;
13+
use volatile_register::{RO, RW};
14+
15+
/// Register block
16+
#[repr(C)]
17+
pub 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+
34+
bitfield! {
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+
43+
bitfield! {
44+
/// Type Register description
45+
#[repr(C)]
46+
#[derive(Copy, Clone)]
47+
pub struct Type(u32);
48+
u8;
49+
sregion, _: 7, 0;
50+
}
51+
52+
bitfield! {
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+
61+
bitfield! {
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+
70+
bitfield! {
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+
81+
bitfield! {
82+
/// Secure Fault Status Register description
83+
#[repr(C)]
84+
#[derive(Copy, Clone)]
85+
pub struct Sfsr(u32);
86+
invep, _: 0;
87+
invis, _: 1;
88+
inver, _: 2;
89+
auviol, _: 3;
90+
invtran, _: 4;
91+
lsperr, _: 5;
92+
sfarvalid, _: 6;
93+
lserr, _: 7;
94+
}
95+
96+
bitfield! {
97+
/// Secure Fault Address Register description
98+
#[repr(C)]
99+
#[derive(Copy, Clone)]
100+
pub struct Sfar(u32);
101+
u32;
102+
address, _: 31, 0;
103+
}
104+
105+
/// Possible attribute of a SAU region.
106+
#[derive(Debug)]
107+
pub enum SauRegionAttribute {
108+
/// SAU region is Secure
109+
Secure,
110+
/// SAU region is Non-Secure Callable
111+
NonSecureCallable,
112+
/// SAU region is Non-Secure
113+
NonSecure,
114+
}
115+
116+
/// Description of a SAU region.
117+
#[derive(Debug)]
118+
pub struct SauRegion {
119+
/// First address of the region, its 5 least significant bits must be set to zero.
120+
pub base_address: u32,
121+
/// Last address of the region, its 5 least significant bits must be set to one.
122+
pub limit_address: u32,
123+
/// Attribute of the region.
124+
pub attribute: SauRegionAttribute,
125+
}
126+
127+
/// Possible error values returned by the SAU methods.
128+
#[derive(Debug)]
129+
pub enum SauError {
130+
/// The region number parameter to set or get a region must be between 0 and
131+
/// region_numbers() - 1.
132+
RegionNumberTooBig,
133+
/// Bits 0 to 4 of the base address of a SAU region must be set to zero.
134+
WrongBaseAddress,
135+
/// Bits 0 to 4 of the limit address of a SAU region must be set to one.
136+
WrongLimitAddress,
137+
}
138+
139+
impl SAU {
140+
/// Get the number of implemented SAU regions.
141+
#[inline]
142+
pub fn region_numbers(&self) -> u8 {
143+
self._type.read().sregion()
144+
}
145+
146+
/// Enable the SAU.
147+
#[inline]
148+
pub fn enable(&mut self) {
149+
unsafe {
150+
self.ctrl.modify(|mut ctrl| {
151+
ctrl.set_enable(true);
152+
ctrl
153+
});
154+
}
155+
}
156+
157+
/// Set a SAU region to a region number.
158+
/// SAU regions must be 32 bytes aligned and their sizes must be a multiple of 32 bytes. It
159+
/// means that the 5 least significant bits of the base address of a SAU region must be set to
160+
/// zero and the 5 least significant bits of the limit address must be set to one.
161+
/// The region number must be valid.
162+
/// This function is executed under a critical section to prevent having inconsistent results.
163+
#[inline]
164+
pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
165+
interrupt::free(|_| {
166+
let base_address = region.base_address;
167+
let limit_address = region.limit_address;
168+
let attribute = region.attribute;
169+
170+
if region_number >= self.region_numbers() {
171+
Err(SauError::RegionNumberTooBig)
172+
} else if base_address & 0x1F != 0 {
173+
Err(SauError::WrongBaseAddress)
174+
} else if limit_address & 0x1F != 0x1F {
175+
Err(SauError::WrongLimitAddress)
176+
} else {
177+
// All fields of these registers are going to be modified so we don't need to read them
178+
// before.
179+
let mut rnr = Rnr(0);
180+
let mut rbar = Rbar(0);
181+
let mut rlar = Rlar(0);
182+
183+
rnr.set_region(region_number);
184+
rbar.set_baddr(base_address >> 5);
185+
rlar.set_laddr(limit_address >> 5);
186+
187+
match attribute {
188+
SauRegionAttribute::Secure => {
189+
rlar.set_nsc(false);
190+
rlar.set_enable(false);
191+
}
192+
SauRegionAttribute::NonSecureCallable => {
193+
rlar.set_nsc(true);
194+
rlar.set_enable(true);
195+
}
196+
SauRegionAttribute::NonSecure => {
197+
rlar.set_nsc(false);
198+
rlar.set_enable(true);
199+
}
200+
}
201+
202+
unsafe {
203+
self.rnr.write(rnr);
204+
self.rbar.write(rbar);
205+
self.rlar.write(rlar);
206+
}
207+
208+
Ok(())
209+
}
210+
})
211+
}
212+
213+
/// Get a region from the SAU.
214+
/// The region number must be valid.
215+
/// This function is executed under a critical section to prevent having inconsistent results.
216+
#[inline]
217+
pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
218+
interrupt::free(|_| {
219+
if region_number >= self.region_numbers() {
220+
Err(SauError::RegionNumberTooBig)
221+
} else {
222+
unsafe {
223+
self.rnr.write(Rnr(region_number.into()));
224+
}
225+
226+
let rbar = self.rbar.read();
227+
let rlar = self.rlar.read();
228+
229+
let attribute = match (rlar.get_enable(), rlar.get_nsc()) {
230+
(false, _) => SauRegionAttribute::Secure,
231+
(true, false) => SauRegionAttribute::NonSecure,
232+
(true, true) => SauRegionAttribute::NonSecureCallable,
233+
};
234+
235+
Ok(SauRegion {
236+
base_address: rbar.get_baddr() << 5,
237+
limit_address: (rlar.get_laddr() << 5) | 0x1F,
238+
attribute,
239+
})
240+
}
241+
})
242+
}
243+
}

0 commit comments

Comments
 (0)