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 bern_units::memory_size::Byte;
6use core::mem;
7use cortex_m::asm;
8use cortex_m::peripheral::{self, mpu, MPU};
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
189                .ctrl
190                .write(MPU_ENABLE | MPU_PRIVILEGED_DEFAULT_ENABLE);
191        }
192        asm::dsb();
193        asm::isb();
194    }
195
196    /// Global MPU disable.
197    #[inline]
198    pub fn disable(&mut self) {
199        asm::dmb();
200        unsafe {
201            self.0.ctrl.write(0);
202        }
203    }
204
205    /// Compile RBAR register values from configuration.
206    pub const fn prepare_region_base_address(addr: u32, region: RegionNumber) -> u32 {
207        let base_addr = addr & !0x1F;
208        let (valid, region) = match region {
209            RegionNumber::Ignore => (0, 0),
210            RegionNumber::Use(region) => (MPU_REGION_VALID, region),
211        };
212
213        base_addr | valid | region as u32
214    }
215
216    /// Apply memory base address and region.
217    #[inline]
218    pub fn set_region_base_address(&mut self, addr: u32, region: RegionNumber) {
219        let register = Self::prepare_region_base_address(addr, region);
220
221        unsafe {
222            self.0.rbar.write(register);
223        }
224    }
225
226    /// Compile RASR register values from configuration.
227    pub const fn prepare_region_attributes(
228        executable: bool,
229        access: (Permission, Permission),
230        attributes: Attributes,
231        subregions: Subregions,
232        region_size: Size,
233    ) -> u32 {
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 {
250                shareable,
251                cache_policy,
252            } => match cache_policy {
253                (CachePolicy::WriteThrough, CachePolicy::WriteThrough) => {
254                    (0b000, 1, 0, shareable as u32)
255                }
256                (CachePolicy::WriteBack { wa: false }, CachePolicy::WriteBack { wa: false }) => {
257                    (0b000, 1, 1, shareable as u32)
258                }
259                (CachePolicy::NoCache, CachePolicy::NoCache) => (0b001, 0, 0, shareable as u32),
260                (CachePolicy::WriteBack { wa: true }, CachePolicy::WriteBack { wa: true }) => {
261                    (0b001, 1, 1, shareable as u32)
262                }
263
264                (CachePolicy::NoCache, CachePolicy::WriteBack { wa: true }) => {
265                    (0b100, 0, 1, shareable as u32)
266                }
267                (CachePolicy::NoCache, CachePolicy::WriteThrough) => {
268                    (0b100, 1, 0, shareable as u32)
269                }
270                (CachePolicy::NoCache, CachePolicy::WriteBack { wa: false }) => {
271                    (0b100, 1, 1, shareable as u32)
272                }
273                (CachePolicy::WriteBack { wa: true }, CachePolicy::NoCache) => {
274                    (0b101, 0, 0, shareable as u32)
275                }
276                (CachePolicy::WriteBack { wa: true }, CachePolicy::WriteThrough) => {
277                    (0b101, 1, 0, shareable as u32)
278                }
279                (CachePolicy::WriteBack { wa: true }, CachePolicy::WriteBack { wa: false }) => {
280                    (0b101, 1, 1, shareable as u32)
281                }
282                (CachePolicy::WriteThrough, CachePolicy::NoCache) => {
283                    (0b110, 0, 0, shareable as u32)
284                }
285                (CachePolicy::WriteThrough, CachePolicy::WriteBack { wa: true }) => {
286                    (0b110, 0, 1, shareable as u32)
287                }
288                (CachePolicy::WriteThrough, CachePolicy::WriteBack { wa: false }) => {
289                    (0b110, 1, 1, shareable as u32)
290                }
291                (CachePolicy::WriteBack { wa: false }, CachePolicy::NoCache) => {
292                    (0b111, 0, 0, shareable as u32)
293                }
294                (CachePolicy::WriteBack { wa: false }, CachePolicy::WriteBack { wa: true }) => {
295                    (0b111, 0, 1, shareable as u32)
296                }
297                (CachePolicy::WriteBack { wa: false }, CachePolicy::WriteThrough) => {
298                    (0b111, 1, 0, shareable as u32)
299                }
300            },
301        };
302
303        let register = (!executable as u32) << 28
304            | ap << 24
305            | tex << 19
306            | s << 18
307            | c << 17
308            | b << 16
309            | (subregions.bits() as u32) << 8
310            | (region_size.bits() as u32) << 1
311            | MPU_REGION_ENABLE;
312
313        register
314    }
315
316    /// Apply memory region attributes and size.
317    #[inline]
318    pub fn set_region_attributes(
319        &mut self,
320        executable: bool,
321        access: (Permission, Permission),
322        attributes: Attributes,
323        subregions: Subregions,
324        region_size: Size,
325    ) {
326        let register = Self::prepare_region_attributes(
327            executable,
328            access,
329            attributes,
330            subregions,
331            region_size,
332        );
333
334        unsafe {
335            self.0.rasr.write(register);
336        }
337    }
338
339    /// Apply one precompiled region.
340    pub fn set_region(&mut self, memory_region: &MemoryRegion) {
341        unsafe {
342            self.0.rbar.write(memory_region.region_base_address_reg);
343            self.0.rasr.write(memory_region.region_attribute_size_reg);
344        }
345    }
346
347    /// Apply 3 precompiled regions.
348    pub fn set_regions(&mut self, memory_region: &[MemoryRegion; 3]) {
349        unsafe {
350            self.0
351                .rbar_a1
352                .write(memory_region[0].region_base_address_reg);
353            self.0
354                .rasr_a1
355                .write(memory_region[0].region_attribute_size_reg);
356            self.0
357                .rbar_a2
358                .write(memory_region[1].region_base_address_reg);
359            self.0
360                .rasr_a2
361                .write(memory_region[1].region_attribute_size_reg);
362            self.0
363                .rbar_a3
364                .write(memory_region[2].region_base_address_reg);
365            self.0
366                .rasr_a3
367                .write(memory_region[2].region_attribute_size_reg);
368        }
369    }
370
371    /// Disable one memory region.
372    #[inline]
373    pub fn disable_region(&mut self, region: u8) {
374        unsafe {
375            self.0.rnr.write(region as u32);
376            self.0.rasr.write(0);
377        }
378    }
379}