Skip to main content

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, &region) 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}