cortexm33/
mpu_v8m.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright OxidOS Automotive 2025.
4
5//! Cortex-M Memory Protection Unit (MPU)
6//!
7//! Implementation of the memory protection unit for the Cortex-M33.
8
9use core::cell::Cell;
10use core::cmp;
11use core::fmt;
12use core::num::NonZeroUsize;
13
14use kernel::platform::mpu;
15use kernel::utilities::cells::OptionalCell;
16use kernel::utilities::registers::interfaces::ReadWriteable;
17use kernel::utilities::registers::interfaces::{Readable, Writeable};
18use kernel::utilities::registers::{
19    register_bitfields, register_structs, FieldValue, ReadOnly, ReadWrite,
20};
21use kernel::utilities::StaticRef;
22
23/// Smallest allowable MPU region across all CortexM cores
24/// Individual cores may have bigger min sizes, but never lower than 32
25const CORTEXM_MIN_REGION_SIZE: usize = 32;
26
27register_structs! {
28    /// MPU Registers for the Armv8-M architecture
29    pub MpuRegisters {
30        /// MPU Type Register
31        (0x0000 => mpu_type: ReadOnly<u32, MPU_TYPE::Register>),
32        /// MPU Control Register
33        (0x0004 => ctrl: ReadWrite<u32, MPU_CTRL::Register>),
34        /// MPU Region Number Register
35        (0x0008 => rnr: ReadWrite<u32, MPU_RNR::Register>),
36        /// MPU Region Base Address Register
37        (0x000C => rbar: ReadWrite<u32, MPU_RBAR::Register>),
38        /// MPU Region Limit Address Register
39        (0x0010 => rlar: ReadWrite<u32, MPU_RLAR::Register>),
40        /// MPU Region Base Address Register Alias 1
41        (0x0014 => rbar_a1: ReadWrite<u32, MPU_RBAR_A1::Register>),
42        /// MPU Region Limit Address Register Alias 1
43        (0x0018 => rlar_a1: ReadWrite<u32, MPU_RLAR_A1::Register>),
44        /// MPU Region Base Address Register Alias 2
45        (0x001C => rbar_a2: ReadWrite<u32, MPU_RBAR_A2::Register>),
46        /// MPU Region Limit Address Register Alias 2
47        (0x0020 => rlar_a2: ReadWrite<u32, MPU_RLAR_A2::Register>),
48        /// MPU Region Base Address Register Alias 3
49        (0x0024 => rbar_a3: ReadWrite<u32, MPU_RBAR_A3::Register>),
50        /// MPU Region Limit Address Register Alias 3
51        (0x0028 => rlar_a3: ReadWrite<u32, MPU_RLAR_A3::Register>),
52        (0x002c => _reserved0),
53        /// MPU Memory Attribute Indirection Register 0
54        (0x0030 => mair0: ReadWrite<u32, MPU_MAIR0::Register>),
55        /// MPU Memory Attribute Indirection Register 1
56        (0x0034 => mair1: ReadWrite<u32, MPU_MAIR1::Register>),
57        (0x0038 => @END),
58    }
59}
60
61register_bitfields![u32,
62    MPU_TYPE [
63        /// Number of regions supported by the MPU
64        DREGION OFFSET(8) NUMBITS(8) [],
65        /// Indicates support for separate instructions and data address regions
66        SEPARATE OFFSET(0) NUMBITS(1) []
67    ],
68    MPU_CTRL [
69        /// Controls whether the default memory map is enabled for privileged software
70        PRIVDEFENA OFFSET(2) NUMBITS(1) [],
71        /// Controls whether handlers executing with priority less than 0 access memory with
72        HFNMIENA OFFSET(1) NUMBITS(1) [],
73        /// Enables the MPU
74        ENABLE OFFSET(0) NUMBITS(1) []
75    ],
76    MPU_RNR [
77        /// Indicates the memory region accessed by MPU_RBAR and MPU_RLAR
78        REGION OFFSET(0) NUMBITS(8) []
79    ],
80    MPU_RBAR [
81        /// Contains bits [31:5] of the lower inclusive limit of the selected MPU memory reg
82        BASE OFFSET(5) NUMBITS(27) [],
83        /// Defines the Shareability domain of this region for Normal memory
84        SH OFFSET(3) NUMBITS(2) [],
85        /// Defines the access permissions for this region
86        AP OFFSET(1) NUMBITS(2) [
87            ReadWritePrivilegedOnly = 0b00,
88            ReadWrite = 0b01,
89            ReadOnlyPrivilegedOnly = 0b10,
90            ReadOnly = 0b11
91        ],
92        /// Defines whether code can be executed from this region
93        XN OFFSET(0) NUMBITS(1) [
94            Enable = 0,
95            Disable = 1
96        ]
97    ],
98    MPU_RLAR [
99        /// Contains bits [31:5] of the upper inclusive limit of the selected MPU memory reg
100        LIMIT OFFSET(5) NUMBITS(27) [],
101        /// Privileged execute-never. Defines whether code can be executed from this privileged region.
102        PXN OFFSET(4) NUMBITS(1) [
103            Enable = 0,
104            Disable = 1,
105        ],
106        /// Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields
107        ATTRINDX OFFSET(1) NUMBITS(3) [],
108        /// Region enable
109        ENABLE OFFSET(0) NUMBITS(1) []
110    ],
111    MPU_RBAR_A1 [
112        /// Contains bits [31:5] of the lower inclusive limit of the selected MPU memory reg
113        BASE OFFSET(5) NUMBITS(27) [],
114        /// Defines the Shareability domain of this region for Normal memory
115        SH OFFSET(3) NUMBITS(2) [],
116        /// Defines the access permissions for this region
117        AP OFFSET(1) NUMBITS(2) [],
118        /// Defines whether code can be executed from this region
119        XN OFFSET(0) NUMBITS(1) []
120    ],
121    MPU_RLAR_A1 [
122        /// Contains bits [31:5] of the upper inclusive limit of the selected MPU memory reg
123        LIMIT OFFSET(5) NUMBITS(27) [],
124        /// Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields
125        ATTRINDX OFFSET(1) NUMBITS(3) [],
126        /// Region enable
127        EN OFFSET(0) NUMBITS(1) []
128    ],
129    MPU_RBAR_A2 [
130        /// Contains bits [31:5] of the lower inclusive limit of the selected MPU memory reg
131        BASE OFFSET(5) NUMBITS(27) [],
132        /// Defines the Shareability domain of this region for Normal memory
133        SH OFFSET(3) NUMBITS(2) [],
134        /// Defines the access permissions for this region
135        AP OFFSET(1) NUMBITS(2) [],
136        /// Defines whether code can be executed from this region
137        XN OFFSET(0) NUMBITS(1) []
138    ],
139    MPU_RLAR_A2 [
140        /// Contains bits [31:5] of the upper inclusive limit of the selected MPU memory reg
141        LIMIT OFFSET(5) NUMBITS(27) [],
142        /// Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields
143        ATTRINDX OFFSET(1) NUMBITS(3) [],
144        /// Region enable
145        EN OFFSET(0) NUMBITS(1) []
146    ],
147    MPU_RBAR_A3 [
148        /// Contains bits [31:5] of the lower inclusive limit of the selected MPU memory reg
149        BASE OFFSET(5) NUMBITS(27) [],
150        /// Defines the Shareability domain of this region for Normal memory
151        SH OFFSET(3) NUMBITS(2) [],
152        /// Defines the access permissions for this region
153        AP OFFSET(1) NUMBITS(2) [],
154        /// Defines whether code can be executed from this region
155        XN OFFSET(0) NUMBITS(1) []
156    ],
157    MPU_RLAR_A3 [
158        /// Contains bits [31:5] of the upper inclusive limit of the selected MPU memory reg
159        LIMIT OFFSET(5) NUMBITS(27) [],
160        /// Associates a set of attributes in the MPU_MAIR0 and MPU_MAIR1 fields
161        ATTRINDX OFFSET(1) NUMBITS(3) [],
162        /// Region enable
163        EN OFFSET(0) NUMBITS(1) []
164    ],
165    MPU_MAIR0 [
166        /// Memory attribute encoding for MPU regions with an AttrIndex of 3
167        ATTR3 OFFSET(24) NUMBITS(8) [],
168        /// Memory attribute encoding for MPU regions with an AttrIndex of 2
169        ATTR2 OFFSET(16) NUMBITS(8) [],
170        /// Memory attribute encoding for MPU regions with an AttrIndex of 1
171        ATTR1 OFFSET(8) NUMBITS(8) [],
172        /// Memory attribute encoding for MPU regions with an AttrIndex of 0
173        ATTR0 OFFSET(0) NUMBITS(8) []
174    ],
175    MPU_MAIR1 [
176        /// Memory attribute encoding for MPU regions with an AttrIndex of 7
177        ATTR7 OFFSET(24) NUMBITS(8) [],
178        /// Memory attribute encoding for MPU regions with an AttrIndex of 6
179        ATTR6 OFFSET(16) NUMBITS(8) [],
180        /// Memory attribute encoding for MPU regions with an AttrIndex of 5
181        ATTR5 OFFSET(8) NUMBITS(8) [],
182        /// Memory attribute encoding for MPU regions with an AttrIndex of 4
183        ATTR4 OFFSET(0) NUMBITS(8) []
184    ],
185];
186
187/// Function to align a pointer to 32 bytes.
188fn align32(initial_ptr: *const u8) -> Result<*const u8, ()> {
189    let memory_offset = initial_ptr.align_offset(32);
190    if memory_offset == usize::MAX {
191        return Err(());
192    }
193
194    let aligned_ptr = initial_ptr.wrapping_add(memory_offset);
195    Ok(aligned_ptr)
196}
197
198/// State related to the real physical MPU.
199///
200/// There should only be one instantiation of this object as it represents
201/// real hardware.
202pub struct MPU<const NUM_REGIONS: usize> {
203    /// MMIO reference to MPU registers.
204    registers: StaticRef<MpuRegisters>,
205    /// Monotonically increasing counter for allocated regions, used
206    /// to assign unique IDs to `CortexMConfig` instances.
207    config_count: Cell<NonZeroUsize>,
208    /// Optimization logic. This is used to indicate which application the MPU
209    /// is currently configured for so that the MPU can skip updating when the
210    /// kernel returns to the same app.
211    hardware_is_configured_for: OptionalCell<NonZeroUsize>,
212}
213
214impl<const NUM_REGIONS: usize> MPU<NUM_REGIONS> {
215    pub const unsafe fn new(registers: StaticRef<MpuRegisters>) -> Self {
216        Self {
217            registers,
218            config_count: Cell::new(NonZeroUsize::MIN),
219            hardware_is_configured_for: OptionalCell::empty(),
220        }
221    }
222
223    // Function useful for boards where the bootloader sets up some
224    // MPU configuration that conflicts with Tock's configuration:
225    pub unsafe fn clear_mpu(&self) {
226        self.registers.ctrl.write(MPU_CTRL::ENABLE::CLEAR);
227    }
228}
229
230/// Per-process struct storing MPU configuration for cortex-m MPUs.
231///
232/// The cortex-m MPU has eight regions, all of which must be configured (though
233/// unused regions may be configured as disabled). This struct caches the result
234/// of region configuration calculation.
235pub struct CortexMConfig<const NUM_REGIONS: usize> {
236    /// Unique ID for this configuration, assigned from a
237    /// monotonically increasing counter in the MPU struct.
238    id: NonZeroUsize,
239    /// The computed region configuration for this process.
240    regions: [CortexMRegion; NUM_REGIONS],
241    /// Has the configuration changed since the last time the this process
242    /// configuration was written to hardware?
243    is_dirty: Cell<bool>,
244}
245
246/// Records the index of the last region used for application RAM memory.
247/// Regions 0-APP_MEMORY_REGION_MAX_NUM are used for application RAM. Regions
248/// with indices above APP_MEMORY_REGION_MAX_NUM can be used for other MPU
249/// needs.
250const APP_MEMORY_REGION_MAX_NUM: usize = 0;
251
252impl<const NUM_REGIONS: usize> fmt::Display for CortexMConfig<NUM_REGIONS> {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        write!(f, "\r\n Cortex-M MPU")?;
255        for (i, region) in self.regions.iter().enumerate() {
256            if let Some(location) = region.location {
257                let access_bits = region.rbar_value.read(MPU_RBAR::AP);
258                let access_str = match access_bits {
259                    0b00 => "ReadWritePrivilegedOnly",
260                    0b01 => "ReadWrite",
261                    0b10 => "ReadOnlyPrivilegedOnly",
262                    0b11 => "ReadOnly",
263                    _ => "ERR",
264                };
265                let start = location.0 as usize;
266                let end = location.1 as usize;
267                write!(
268                    f,
269                    "\
270                     \r\n  Region {}: [{:#010X}:{:#010X}], length: {} bytes; {} ({:#x})",
271                    i,
272                    start,
273                    end,
274                    end - start,
275                    access_str,
276                    access_bits,
277                )?;
278            } else {
279                write!(f, "\r\n  Region {}: Unused", i)?;
280            }
281        }
282        write!(f, "\r\n")
283    }
284}
285
286impl<const NUM_REGIONS: usize> CortexMConfig<NUM_REGIONS> {
287    fn unused_region_number(&self) -> Option<usize> {
288        for (number, region) in self.regions.iter().enumerate() {
289            if number == APP_MEMORY_REGION_MAX_NUM {
290                continue;
291            }
292            if region.location.is_none() {
293                return Some(number);
294            }
295        }
296        None
297    }
298}
299
300/// Struct storing configuration for a Cortex-M MPU region.
301#[derive(Copy, Clone)]
302pub struct CortexMRegion {
303    location: Option<(*const u8, *const u8)>,
304    rbar_value: FieldValue<u32, MPU_RBAR::Register>,
305    rlar_value: FieldValue<u32, MPU_RLAR::Register>,
306    region_num: usize,
307}
308
309impl PartialEq<mpu::Region> for CortexMRegion {
310    fn eq(&self, other: &mpu::Region) -> bool {
311        self.location.is_some_and(|(start, end)| {
312            core::ptr::eq(start, other.start_address())
313                && (end as usize - start as usize) == other.size()
314        })
315    }
316}
317
318impl CortexMRegion {
319    fn new(
320        logical_start: *const u8,
321        logical_size: usize,
322        region_start: *const u8,
323        region_size: usize,
324        region_num: usize,
325        permissions: mpu::Permissions,
326    ) -> Option<CortexMRegion> {
327        // Logical size must be above minimum size for cortexM MPU regions and
328        // and less than the size of the underlying physical region
329        if logical_size < CORTEXM_MIN_REGION_SIZE || region_size < logical_size {
330            return None;
331        }
332
333        // Determine access and execute permissions
334        let (access, execute) = match permissions {
335            mpu::Permissions::ReadWriteExecute => (MPU_RBAR::AP::ReadWrite, MPU_RBAR::XN::Enable),
336            mpu::Permissions::ReadWriteOnly => (MPU_RBAR::AP::ReadWrite, MPU_RBAR::XN::Disable),
337            mpu::Permissions::ReadExecuteOnly => (MPU_RBAR::AP::ReadOnly, MPU_RBAR::XN::Enable),
338            mpu::Permissions::ReadOnly => (MPU_RBAR::AP::ReadOnly, MPU_RBAR::XN::Disable),
339            mpu::Permissions::ExecuteOnly => {
340                (MPU_RBAR::AP::ReadOnlyPrivilegedOnly, MPU_RBAR::XN::Enable)
341            }
342        };
343
344        // Base Address register
345        let rbar_value = MPU_RBAR::BASE.val((logical_start as u32) >> 5)
346            + MPU_RBAR::SH.val(0)
347            + access
348            + execute;
349
350        let logical_end = logical_start as usize + logical_size;
351
352        // The end address must be aligned to 32 bytes.
353        if !logical_end.is_multiple_of(32) {
354            return None;
355        }
356
357        // Limit Address register
358        let rlar_value = MPU_RLAR::ENABLE::SET
359            + MPU_RLAR::LIMIT.val((logical_end as u32) >> 5)
360            + MPU_RLAR::PXN::Disable
361            + MPU_RLAR::ATTRINDX.val(0);
362
363        Some(CortexMRegion {
364            location: Some((region_start, region_start.wrapping_add(region_size))),
365            rbar_value,
366            rlar_value,
367            region_num,
368        })
369    }
370
371    fn empty(region_num: usize) -> CortexMRegion {
372        CortexMRegion {
373            location: None,
374            rbar_value: MPU_RBAR::BASE.val(0),
375            rlar_value: MPU_RLAR::ENABLE::CLEAR,
376            region_num,
377        }
378    }
379
380    fn overlaps(&self, other_start: *const u8, other_size: usize) -> bool {
381        let other_start = other_start as usize;
382        let other_end = other_start + other_size;
383
384        let (region_start, region_end) = match self.location {
385            Some((region_start, region_end)) => {
386                let region_start = region_start as usize;
387                let region_end = region_end as usize;
388                (region_start, region_end)
389            }
390            None => return false,
391        };
392
393        region_start < other_end && other_start < region_end
394    }
395}
396
397// `MPU` is an unsafe trait, and with this implementation we guarantee
398// that we adhere to the semantics documented on that trait and its
399// associated types and methods.
400unsafe impl<const NUM_REGIONS: usize> mpu::MPU for MPU<NUM_REGIONS> {
401    type MpuConfig = CortexMConfig<NUM_REGIONS>;
402
403    fn enable_app_mpu(&self) {
404        // Enable the MPU, disable it during HardFault/NMI handlers, and allow
405        // privileged code access to all unprotected memory.
406        self.registers
407            .ctrl
408            .write(MPU_CTRL::ENABLE::SET + MPU_CTRL::HFNMIENA::CLEAR + MPU_CTRL::PRIVDEFENA::SET);
409    }
410
411    unsafe fn disable_app_mpu(&self) {
412        // The MPU is not enabled for privileged mode, so we don't have to do
413        // anything
414        self.registers.ctrl.write(MPU_CTRL::ENABLE::CLEAR);
415    }
416
417    fn number_total_regions(&self) -> usize {
418        self.registers.mpu_type.read(MPU_TYPE::DREGION) as usize
419    }
420
421    fn new_config(&self) -> Option<Self::MpuConfig> {
422        let id = self.config_count.get();
423        self.config_count.set(id.checked_add(1)?);
424
425        // Allocate the regions with index `0` first, then use `reset_config` to
426        // write the properly-indexed `CortexMRegion`s:
427        let mut ret = CortexMConfig {
428            id,
429            regions: [CortexMRegion::empty(0); NUM_REGIONS],
430            is_dirty: Cell::new(true),
431        };
432
433        self.reset_config(&mut ret);
434
435        Some(ret)
436    }
437
438    fn reset_config(&self, config: &mut Self::MpuConfig) {
439        for i in 0..NUM_REGIONS {
440            config.regions[i] = CortexMRegion::empty(i);
441        }
442
443        config.is_dirty.set(true);
444    }
445
446    fn allocate_region(
447        &self,
448        unallocated_memory_start: *const u8,
449        unallocated_memory_size: usize,
450        min_region_size: usize,
451        permissions: mpu::Permissions,
452        config: &mut Self::MpuConfig,
453    ) -> Option<mpu::Region> {
454        let mut region_calculation = || {
455            // Check that no previously allocated regions overlap the unallocated memory.
456            for region in config.regions.iter() {
457                if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
458                    return Err(());
459                }
460            }
461
462            let region_num = config.unused_region_number().ok_or(())?;
463
464            let region_start = align32(unallocated_memory_start)?;
465            let region_end = align32(region_start.wrapping_add(min_region_size))?;
466            let region_size = unsafe { region_end.offset_from(region_start) };
467
468            // Check for overflow
469            if region_size < 0 {
470                return Err(());
471            }
472
473            // Make sure the region fits in the unallocated memory.
474            if region_size as usize > unallocated_memory_size {
475                return Err(());
476            }
477
478            let region = CortexMRegion::new(
479                region_start,
480                region_size as usize,
481                region_start,
482                region_size as usize,
483                region_num,
484                permissions,
485            )
486            .ok_or(())?;
487
488            config.regions[region_num] = region;
489            config.is_dirty.set(true);
490
491            Ok(mpu::Region::new(region_start, region_size as usize))
492        };
493
494        region_calculation().ok()
495    }
496
497    fn remove_memory_region(
498        &self,
499        region: mpu::Region,
500        config: &mut Self::MpuConfig,
501    ) -> Result<(), ()> {
502        let (idx, _r) = config
503            .regions
504            .iter()
505            .enumerate()
506            .find(|(_idx, r)| **r == region)
507            .ok_or(())?;
508
509        if idx == APP_MEMORY_REGION_MAX_NUM {
510            return Err(());
511        }
512
513        config.regions[idx] = CortexMRegion::empty(idx);
514        config.is_dirty.set(true);
515
516        Ok(())
517    }
518
519    fn allocate_app_memory_region(
520        &self,
521        unallocated_memory_start: *const u8,
522        unallocated_memory_size: usize,
523        min_memory_size: usize,
524        initial_app_memory_size: usize,
525        initial_kernel_memory_size: usize,
526        permissions: mpu::Permissions,
527        config: &mut Self::MpuConfig,
528    ) -> Option<(*const u8, usize)> {
529        let mut region_calculation = || {
530            // Check that no previously allocated regions overlap the unallocated
531            // memory.
532            for region in config.regions.iter() {
533                if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
534                    return Err(());
535                }
536            }
537
538            // Make sure there is enough memory for app memory and kernel memory.
539            let memory_size = cmp::max(
540                min_memory_size,
541                initial_app_memory_size + initial_kernel_memory_size,
542            );
543
544            // The region should start as close as possible to the start of the
545            // unallocated memory.
546            let region_start = align32(unallocated_memory_start)?;
547            let region_end = align32(region_start.wrapping_add(memory_size))?;
548            let region_size = unsafe { region_end.offset_from(region_start) };
549
550            // Check for overflow
551            if region_size < 0 {
552                return Err(());
553            }
554
555            // Make sure the region fits in the unallocated memory.
556            if region_size as usize > unallocated_memory_size {
557                return Err(());
558            }
559
560            let logical_start = region_start;
561            let logical_end = align32(logical_start.wrapping_add(initial_app_memory_size))?;
562            let logical_size = unsafe { logical_end.offset_from(logical_start) };
563
564            // Check for overflow
565            if logical_size < 0 {
566                return Err(());
567            }
568
569            let region = CortexMRegion::new(
570                logical_start,
571                logical_size as usize,
572                region_start,
573                region_size as usize,
574                0,
575                permissions,
576            )
577            .ok_or(())?;
578
579            config.regions[0] = region;
580            config.is_dirty.set(true);
581
582            Ok((region_start, memory_size))
583        };
584
585        region_calculation().ok()
586    }
587
588    fn update_app_memory_region(
589        &self,
590        app_memory_break: *const u8,
591        kernel_memory_break: *const u8,
592        permissions: mpu::Permissions,
593        config: &mut Self::MpuConfig,
594    ) -> Result<(), ()> {
595        // Get first region, or error if the process tried to update app memory
596        // MPU region before it was created.
597        let (region_start, region_end) = config.regions[0].location.ok_or(())?;
598        // .map(|(region_start, region_end)| (region_start as usize, region_end as usize))?;
599
600        // Check if the memory breaks are out of the allocated region.
601        if (app_memory_break as usize) < (region_start as usize)
602            || (app_memory_break as usize) >= (region_end as usize)
603        {
604            return Err(());
605        }
606
607        if (kernel_memory_break as usize) < (region_start as usize)
608            || (kernel_memory_break as usize) >= (region_end as usize)
609        {
610            return Err(());
611        }
612
613        // Out of memory
614        if (app_memory_break as usize) > (kernel_memory_break as usize) {
615            return Err(());
616        }
617
618        let logical_start = region_start;
619        let logical_end = align32(app_memory_break)?;
620        let logical_size = unsafe { logical_end.offset_from(logical_start) };
621
622        // Check for overflow
623        if logical_size < 0 {
624            return Err(());
625        }
626
627        // Check if the aligned memory doesn't go over the grants.
628        if (logical_end as usize) > (kernel_memory_break as usize) {
629            return Err(());
630        }
631
632        let region_size = unsafe { region_end.offset_from(region_start) };
633
634        let region = CortexMRegion::new(
635            logical_start,
636            logical_size as usize,
637            region_start,
638            region_size as usize,
639            0,
640            permissions,
641        )
642        .ok_or(())?;
643
644        config.regions[0] = region;
645        config.is_dirty.set(true);
646
647        Ok(())
648    }
649
650    unsafe fn configure_mpu(&self, config: &Self::MpuConfig) {
651        // Set ATTR0 to Normal Memory, Outer and Inner Non-cacheable.
652        self.registers
653            .mair0
654            .modify(MPU_MAIR0::ATTR0.val(0b0100_0100));
655        // If the hardware is already configured for this app and the app's MPU
656        // configuration has not changed, then skip the hardware update.
657        if !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
658            // Set MPU regions
659            for region in config.regions.iter() {
660                self.registers
661                    .rnr
662                    .modify(MPU_RNR::REGION.val(region.region_num as u32));
663                self.registers.rbar.write(region.rbar_value);
664                self.registers.rlar.write(region.rlar_value);
665            }
666            self.hardware_is_configured_for.set(config.id);
667            config.is_dirty.set(false);
668        }
669    }
670}