bern_arch/cortex_m/
mpu.rs

1//! ARM Cortex-M Memory Protection Unit.
2//!
3//! Based on <https://github.com/helium/cortex-mpu>.
4
5use core::mem;
6use cortex_m::peripheral::{self, mpu, MPU};
7use cortex_m::asm;
8use bern_units::memory_size::Byte;
9
10/// Valid sizes for the MPU.
11#[derive(Copy, Clone, Debug, Eq, PartialEq)]
12#[repr(u8)]
13pub enum Size {
14    S32 = 4,
15    S64 = 5,
16    S128 = 6,
17    S256 = 7,
18    S512 = 8,
19    S1K = 9,
20    S2K = 10,
21    S4K = 11,
22    S8K = 12,
23    S16K = 13,
24    S32K = 14,
25    S64K = 15,
26    S128K = 16,
27    S256K = 17,
28    S512K = 18,
29    S1M = 19,
30    S2M = 20,
31    S4M = 21,
32    S8M = 22,
33    S16M = 23,
34    S32M = 24,
35    S64M = 25,
36    S128M = 26,
37    S256M = 27,
38    S512M = 28,
39    S1G = 29,
40    S2G = 30,
41    S4G = 31,
42}
43impl Size {
44    /// Return register value from `Size`.
45    pub const fn bits(self) -> usize {
46        self as usize
47    }
48
49    /// Return `Size` in bytes
50    pub const fn size_bytes(self) -> usize {
51        2u32.pow((self as u32) + 1) as usize
52    }
53
54    /// Creates a Size from a raw number.
55    ///
56    /// # Safety
57    /// `size` must be a multiple of `2`.
58    pub unsafe fn from_bytes(mut size: usize) -> Self {
59        let mut log2: u8 = 0;
60        while size > 1 {
61            size = size >> 1;
62            log2 += 1;
63        }
64        mem::transmute(log2 - 1)
65    }
66}
67
68impl Into<Size> for Byte {
69    fn into(self) -> Size {
70        unsafe { Size::from_bytes(self.0 as usize) }
71    }
72}
73
74/* Control Register */
75pub const MPU_ENABLE: u32 = 1;
76pub const MPU_HARD_FAULT_ENABLED: u32 = 1 << 1;
77pub const MPU_PRIVILEGED_DEFAULT_ENABLE: u32 = 1 << 2;
78
79/* Region Number Register */
80pub enum RegionNumber {
81    Ignore,
82    Use(u8),
83}
84
85/* Region Base Address Register */
86pub const MPU_REGION_VALID: u32 = 1 << 4;
87
88/* Region Attribute and Status Register */
89pub const MPU_REGION_ENABLE: u32 = 1;
90
91pub enum Permission {
92    NoAccess,
93    ReadOnly,
94    ReadWrite,
95}
96
97impl From<crate::memory_protection::Permission> for Permission {
98    /// Match permission from interface to local permission enum
99    fn from(permission: crate::memory_protection::Permission) -> Self {
100        match permission {
101            crate::memory_protection::Permission::NoAccess => Permission::NoAccess,
102            crate::memory_protection::Permission::ReadOnly => Permission::ReadOnly,
103            crate::memory_protection::Permission::ReadWrite => Permission::ReadWrite,
104        }
105    }
106}
107
108/// Memory attributes on the hardware.
109pub enum Attributes {
110    /// No caching or buffering allowed
111    StronglyOrdered,
112    /// Memory mapped peripheral.
113    Device {
114        /// Can be shared between multiple cores.
115        shareable: bool,
116    },
117    /// Normal memory, e.g. Flash or SRAM.
118    Normal {
119        /// Can be shared between multiple cores.
120        shareable: bool,
121        /// (inner, outer)
122        cache_policy: (CachePolicy, CachePolicy),
123    },
124}
125
126/// Requested cache policy if implemented on MCU.
127pub enum CachePolicy {
128    /// No caching allowed.
129    NoCache,
130    /// Write without caching, allow read caching.
131    WriteThrough,
132    /// Allow read and write caching. Cache will write back to main memory on
133    /// its own.
134    WriteBack {
135        /// Write allocate: fetch data on a write miss to the cache.
136        wa: bool,
137    },
138}
139
140/// MPU subregions.
141///
142/// A memory region is divided into 8 subregion of equal size. The memory region
143/// rule can be disabled for any of the 8 subregions.
144/// A bit corresponds to one subregion: LSB - lowest address, MSB - highest
145/// address.
146#[derive(Debug, Copy, Clone, PartialEq, Eq)]
147pub struct Subregions(u8);
148impl Subregions {
149    /// Memory rule applies to all subregions.
150    pub const ALL: Subregions = Subregions(0xFF);
151    /// All subregions disabled.
152    ///
153    /// **Note:** Just disable the memory region altogether.
154    pub const NONE: Subregions = Subregions(0);
155
156    pub const fn bits(self) -> u32 {
157        !self.0 as u32
158    }
159}
160
161impl Default for Subregions {
162    fn default() -> Self {
163        Subregions::ALL
164    }
165}
166
167/// Raw register values, for fast MPU updates.
168#[derive(Copy, Clone)]
169#[repr(C)]
170pub struct MemoryRegion {
171    pub region_base_address_reg: u32,
172    pub region_attribute_size_reg: u32,
173}
174
175pub struct Mpu<'a>(&'a mut mpu::RegisterBlock);
176
177impl Mpu<'_> {
178    /// Get the memory protection peripheral.
179    #[inline]
180    pub unsafe fn take() -> Self {
181        Self(&mut *(peripheral::MPU::PTR as *mut _))
182    }
183
184    /// Global MPU enable.
185    #[inline]
186    pub fn enable(&mut self) {
187        unsafe {
188            self.0.ctrl.write(MPU_ENABLE | MPU_PRIVILEGED_DEFAULT_ENABLE);
189        }
190        asm::dsb();
191        asm::isb();
192    }
193
194    /// Global MPU disable.
195    #[inline]
196    pub fn disable(&mut self) {
197        asm::dmb();
198        unsafe {
199            self.0.ctrl.write(0);
200        }
201    }
202
203    /// Compile RBAR register values from configuration.
204    pub const fn prepare_region_base_address(addr: u32, region: RegionNumber) -> u32 {
205        let base_addr = addr & !0x1F;
206        let (valid, region) = match region {
207            RegionNumber::Ignore => (0, 0),
208            RegionNumber::Use(region) => (MPU_REGION_VALID, region),
209        };
210
211        base_addr | valid | region as u32
212    }
213
214    /// Apply memory base address and region.
215    #[inline]
216    pub fn set_region_base_address(&mut self, addr: u32, region: RegionNumber) {
217        let register = Self::prepare_region_base_address(
218            addr,
219            region
220        );
221
222        unsafe {
223            self.0.rbar.write(register);
224        }
225    }
226
227    /// Compile RASR register values from configuration.
228    pub const fn prepare_region_attributes(executable: bool,
229                                     access: (Permission, Permission),
230                                     attributes: Attributes,
231                                     subregions: Subregions,
232                                     region_size: Size) -> u32 {
233
234        // (privileged, unprivileged)
235        let ap = match access {
236            (Permission::NoAccess, Permission::NoAccess) => 0b000,
237            (Permission::ReadWrite, Permission::NoAccess) => 0b001,
238            (Permission::ReadWrite, Permission::ReadOnly) => 0b010,
239            (Permission::ReadWrite, Permission::ReadWrite) => 0b011,
240            (Permission::ReadOnly, Permission::NoAccess) => 0b101,
241            (Permission::ReadOnly, Permission::ReadOnly) => 0b111,
242            (_, _) => 0b000, // no access
243        };
244
245        let (tex, c, b, s) = match attributes {
246            Attributes::StronglyOrdered => (0b000, 0, 0, 0),
247            Attributes::Device { shareable: true } => (0b000, 0, 1, 0),
248            Attributes::Device { shareable: false } => (0b010, 0, 0, 0),
249            Attributes::Normal { shareable, cache_policy } => match cache_policy {
250                (CachePolicy::WriteThrough, CachePolicy::WriteThrough) => (0b000, 1, 0, shareable as u32),
251                (CachePolicy::WriteBack { wa: false }, CachePolicy::WriteBack { wa: false }) => (0b000, 1, 1, shareable as u32),
252                (CachePolicy::NoCache, CachePolicy::NoCache) => (0b001, 0, 0, shareable as u32),
253                (CachePolicy::WriteBack { wa: true }, CachePolicy::WriteBack { wa: true }) => (0b001, 1, 1, shareable as u32),
254
255                (CachePolicy::NoCache, CachePolicy::WriteBack { wa: true }) => (0b100, 0, 1, shareable as u32),
256                (CachePolicy::NoCache, CachePolicy::WriteThrough) => (0b100, 1, 0, shareable as u32),
257                (CachePolicy::NoCache, CachePolicy::WriteBack { wa: false }) => (0b100, 1, 1, shareable as u32),
258                (CachePolicy::WriteBack { wa: true }, CachePolicy::NoCache) => (0b101, 0, 0, shareable as u32),
259                (CachePolicy::WriteBack { wa: true }, CachePolicy::WriteThrough) => (0b101, 1, 0, shareable as u32),
260                (CachePolicy::WriteBack { wa: true }, CachePolicy::WriteBack { wa: false }) => (0b101, 1, 1, shareable as u32),
261                (CachePolicy::WriteThrough, CachePolicy::NoCache) => (0b110, 0, 0, shareable as u32),
262                (CachePolicy::WriteThrough, CachePolicy::WriteBack { wa: true}) => (0b110, 0, 1, shareable as u32),
263                (CachePolicy::WriteThrough, CachePolicy::WriteBack { wa: false}) => (0b110, 1, 1, shareable as u32),
264                (CachePolicy::WriteBack { wa: false }, CachePolicy::NoCache) => (0b111, 0, 0, shareable as u32),
265                (CachePolicy::WriteBack { wa: false }, CachePolicy::WriteBack { wa: true}) => (0b111, 0, 1, shareable as u32),
266                (CachePolicy::WriteBack { wa: false }, CachePolicy::WriteThrough) => (0b111, 1, 0, shareable as u32),
267            },
268        };
269
270        let register = (!executable as u32) << 28 |
271            ap << 24 |
272            tex << 19 | s << 18 | c << 17 | b << 16 |
273            (subregions.bits() as u32) << 8 |
274            (region_size.bits() as u32) << 1 |
275            MPU_REGION_ENABLE;
276
277        register
278    }
279
280    /// Apply memory region attributes and size.
281    #[inline]
282    pub fn set_region_attributes(&mut self,
283                                 executable: bool,
284                                 access: (Permission, Permission),
285                                 attributes: Attributes,
286                                 subregions: Subregions,
287                                 region_size: Size) {
288
289        let register = Self::prepare_region_attributes(
290            executable,
291            access,
292            attributes,
293            subregions,
294            region_size
295        );
296
297        unsafe {
298            self.0.rasr.write(register);
299        }
300    }
301
302    /// Apply one precompiled region.
303    pub fn set_region(&mut self, memory_region: &MemoryRegion) {
304        unsafe {
305            self.0.rbar.write(memory_region.region_base_address_reg);
306            self.0.rasr.write(memory_region.region_attribute_size_reg);
307        }
308    }
309
310    /// Apply 3 precompiled regions.
311    pub fn set_regions(&mut self, memory_region: &[MemoryRegion; 3]) {
312        unsafe {
313            self.0.rbar_a1.write(memory_region[0].region_base_address_reg);
314            self.0.rasr_a1.write(memory_region[0].region_attribute_size_reg);
315            self.0.rbar_a2.write(memory_region[1].region_base_address_reg);
316            self.0.rasr_a2.write(memory_region[1].region_attribute_size_reg);
317            self.0.rbar_a3.write(memory_region[2].region_base_address_reg);
318            self.0.rasr_a3.write(memory_region[2].region_attribute_size_reg);
319        }
320    }
321
322    /// Disable one memory region.
323    #[inline]
324    pub fn disable_region(&mut self, region: u8) {
325        unsafe {
326            self.0.rnr.write(region as u32);
327            self.0.rasr.write(0);
328        }
329    }
330}