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    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
96bitfield! {
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)]
107pub 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)]
118pub 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)]
129pub 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
139impl 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}