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 % 32 != 0 {
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
397impl<const NUM_REGIONS: usize> mpu::MPU for MPU<NUM_REGIONS> {
398    type MpuConfig = CortexMConfig<NUM_REGIONS>;
399
400    fn enable_app_mpu(&self) {
401        // Enable the MPU, disable it during HardFault/NMI handlers, and allow
402        // privileged code access to all unprotected memory.
403        self.registers
404            .ctrl
405            .write(MPU_CTRL::ENABLE::SET + MPU_CTRL::HFNMIENA::CLEAR + MPU_CTRL::PRIVDEFENA::SET);
406    }
407
408    fn disable_app_mpu(&self) {
409        // The MPU is not enabled for privileged mode, so we don't have to do
410        // anything
411        self.registers.ctrl.write(MPU_CTRL::ENABLE::CLEAR);
412    }
413
414    fn number_total_regions(&self) -> usize {
415        self.registers.mpu_type.read(MPU_TYPE::DREGION) as usize
416    }
417
418    fn new_config(&self) -> Option<Self::MpuConfig> {
419        let id = self.config_count.get();
420        self.config_count.set(id.checked_add(1)?);
421
422        // Allocate the regions with index `0` first, then use `reset_config` to
423        // write the properly-indexed `CortexMRegion`s:
424        let mut ret = CortexMConfig {
425            id,
426            regions: [CortexMRegion::empty(0); NUM_REGIONS],
427            is_dirty: Cell::new(true),
428        };
429
430        self.reset_config(&mut ret);
431
432        Some(ret)
433    }
434
435    fn reset_config(&self, config: &mut Self::MpuConfig) {
436        for i in 0..NUM_REGIONS {
437            config.regions[i] = CortexMRegion::empty(i);
438        }
439
440        config.is_dirty.set(true);
441    }
442
443    fn allocate_region(
444        &self,
445        unallocated_memory_start: *const u8,
446        unallocated_memory_size: usize,
447        min_region_size: usize,
448        permissions: mpu::Permissions,
449        config: &mut Self::MpuConfig,
450    ) -> Option<mpu::Region> {
451        let mut region_calculation = || {
452            // Check that no previously allocated regions overlap the unallocated memory.
453            for region in config.regions.iter() {
454                if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
455                    return Err(());
456                }
457            }
458
459            let region_num = config.unused_region_number().ok_or(())?;
460
461            let region_start = align32(unallocated_memory_start)?;
462            let region_end = align32(region_start.wrapping_add(min_region_size))?;
463            let region_size = unsafe { region_end.offset_from(region_start) };
464
465            // Check for overflow
466            if region_size < 0 {
467                return Err(());
468            }
469
470            // Make sure the region fits in the unallocated memory.
471            if region_size as usize > unallocated_memory_size {
472                return Err(());
473            }
474
475            let region = CortexMRegion::new(
476                region_start,
477                region_size as usize,
478                region_start,
479                region_size as usize,
480                region_num,
481                permissions,
482            )
483            .ok_or(())?;
484
485            config.regions[region_num] = region;
486            config.is_dirty.set(true);
487
488            Ok(mpu::Region::new(region_start, region_size as usize))
489        };
490
491        region_calculation().ok()
492    }
493
494    fn remove_memory_region(
495        &self,
496        region: mpu::Region,
497        config: &mut Self::MpuConfig,
498    ) -> Result<(), ()> {
499        let (idx, _r) = config
500            .regions
501            .iter()
502            .enumerate()
503            .find(|(_idx, r)| **r == region)
504            .ok_or(())?;
505
506        if idx == APP_MEMORY_REGION_MAX_NUM {
507            return Err(());
508        }
509
510        config.regions[idx] = CortexMRegion::empty(idx);
511        config.is_dirty.set(true);
512
513        Ok(())
514    }
515
516    fn allocate_app_memory_region(
517        &self,
518        unallocated_memory_start: *const u8,
519        unallocated_memory_size: usize,
520        min_memory_size: usize,
521        initial_app_memory_size: usize,
522        initial_kernel_memory_size: usize,
523        permissions: mpu::Permissions,
524        config: &mut Self::MpuConfig,
525    ) -> Option<(*const u8, usize)> {
526        let mut region_calculation = || {
527            // Check that no previously allocated regions overlap the unallocated
528            // memory.
529            for region in config.regions.iter() {
530                if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
531                    return Err(());
532                }
533            }
534
535            // Make sure there is enough memory for app memory and kernel memory.
536            let memory_size = cmp::max(
537                min_memory_size,
538                initial_app_memory_size + initial_kernel_memory_size,
539            );
540
541            // The region should start as close as possible to the start of the
542            // unallocated memory.
543            let region_start = align32(unallocated_memory_start)?;
544            let region_end = align32(region_start.wrapping_add(memory_size))?;
545            let region_size = unsafe { region_end.offset_from(region_start) };
546
547            // Check for overflow
548            if region_size < 0 {
549                return Err(());
550            }
551
552            // Make sure the region fits in the unallocated memory.
553            if region_size as usize > unallocated_memory_size {
554                return Err(());
555            }
556
557            let logical_start = region_start;
558            let logical_end = align32(logical_start.wrapping_add(initial_app_memory_size))?;
559            let logical_size = unsafe { logical_end.offset_from(logical_start) };
560
561            // Check for overflow
562            if logical_size < 0 {
563                return Err(());
564            }
565
566            let region = CortexMRegion::new(
567                logical_start,
568                logical_size as usize,
569                region_start,
570                region_size as usize,
571                0,
572                permissions,
573            )
574            .ok_or(())?;
575
576            config.regions[0] = region;
577            config.is_dirty.set(true);
578
579            Ok((region_start, memory_size))
580        };
581
582        region_calculation().ok()
583    }
584
585    fn update_app_memory_region(
586        &self,
587        app_memory_break: *const u8,
588        kernel_memory_break: *const u8,
589        permissions: mpu::Permissions,
590        config: &mut Self::MpuConfig,
591    ) -> Result<(), ()> {
592        // Get first region, or error if the process tried to update app memory
593        // MPU region before it was created.
594        let (region_start, region_end) = config.regions[0].location.ok_or(())?;
595        // .map(|(region_start, region_end)| (region_start as usize, region_end as usize))?;
596
597        // Check if the memory breaks are out of the allocated region.
598        if (app_memory_break as usize) < (region_start as usize)
599            || (app_memory_break as usize) >= (region_end as usize)
600        {
601            return Err(());
602        }
603
604        if (kernel_memory_break as usize) < (region_start as usize)
605            || (kernel_memory_break as usize) >= (region_end as usize)
606        {
607            return Err(());
608        }
609
610        // Out of memory
611        if (app_memory_break as usize) > (kernel_memory_break as usize) {
612            return Err(());
613        }
614
615        let logical_start = region_start;
616        let logical_end = align32(app_memory_break)?;
617        let logical_size = unsafe { logical_end.offset_from(logical_start) };
618
619        // Check for overflow
620        if logical_size < 0 {
621            return Err(());
622        }
623
624        // Check if the aligned memory doesn't go over the grants.
625        if (logical_end as usize) > (kernel_memory_break as usize) {
626            return Err(());
627        }
628
629        let region_size = unsafe { region_end.offset_from(region_start) };
630
631        let region = CortexMRegion::new(
632            logical_start,
633            logical_size as usize,
634            region_start,
635            region_size as usize,
636            0,
637            permissions,
638        )
639        .ok_or(())?;
640
641        config.regions[0] = region;
642        config.is_dirty.set(true);
643
644        Ok(())
645    }
646
647    fn configure_mpu(&self, config: &Self::MpuConfig) {
648        // Set ATTR0 to Normal Memory, Outer and Inner Non-cacheable.
649        self.registers
650            .mair0
651            .modify(MPU_MAIR0::ATTR0.val(0b0100_0100));
652        // If the hardware is already configured for this app and the app's MPU
653        // configuration has not changed, then skip the hardware update.
654        if !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
655            // Set MPU regions
656            for region in config.regions.iter() {
657                self.registers
658                    .rnr
659                    .modify(MPU_RNR::REGION.val(region.region_num as u32));
660                self.registers.rbar.write(region.rbar_value);
661                self.registers.rlar.write(region.rlar_value);
662            }
663            self.hardware_is_configured_for.set(config.id);
664            config.is_dirty.set(false);
665        }
666    }
667}