cortexm/
mpu.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 Tock Contributors 2022.
4
5//! Cortex-M Memory Protection Unit (MPU)
6//!
7//! Implementation of the memory protection unit for the Cortex-M0+, Cortex-M3,
8//! Cortex-M4, and Cortex-M7.
9
10use core::cell::Cell;
11use core::cmp;
12use core::fmt;
13use core::num::NonZeroUsize;
14
15use kernel::platform::mpu;
16use kernel::utilities::cells::OptionalCell;
17use kernel::utilities::math;
18use kernel::utilities::registers::interfaces::{Readable, Writeable};
19use kernel::utilities::registers::{register_bitfields, FieldValue, ReadOnly, ReadWrite};
20use kernel::utilities::StaticRef;
21
22/// Smallest allowable MPU region across all CortexM cores
23/// Individual cores may have bigger min sizes, but never lower than 32
24const CORTEXM_MIN_REGION_SIZE: usize = 32;
25
26/// MPU Registers for the Cortex-M3, Cortex-M4 and Cortex-M7 families
27/// Described in section 4.5 of
28/// <http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/DUI0553A_cortex_m4_dgug.pdf>
29#[repr(C)]
30pub struct MpuRegisters {
31    /// Indicates whether the MPU is present and, if so, how many regions it
32    /// supports.
33    pub mpu_type: ReadOnly<u32, Type::Register>,
34
35    /// The control register:
36    ///   * Enables the MPU (bit 0).
37    ///   * Enables MPU in hard-fault, non-maskable interrupt (NMI).
38    ///   * Enables the default memory map background region in privileged mode.
39    pub ctrl: ReadWrite<u32, Control::Register>,
40
41    /// Selects the region number (zero-indexed) referenced by the region base
42    /// address and region attribute and size registers.
43    pub rnr: ReadWrite<u32, RegionNumber::Register>,
44
45    /// Defines the base address of the currently selected MPU region.
46    pub rbar: ReadWrite<u32, RegionBaseAddress::Register>,
47
48    /// Defines the region size and memory attributes of the selected MPU
49    /// region. The bits are defined as in 4.5.5 of the Cortex-M4 user guide.
50    pub rasr: ReadWrite<u32, RegionAttributes::Register>,
51}
52
53register_bitfields![u32,
54    Type [
55        /// The number of MPU instructions regions supported. Always reads 0.
56        IREGION OFFSET(16) NUMBITS(8) [],
57        /// The number of data regions supported. If this field reads-as-zero the
58        /// processor does not implement an MPU
59        DREGION OFFSET(8) NUMBITS(8) [],
60        /// Indicates whether the processor support unified (0) or separate
61        /// (1) instruction and data regions. Always reads 0 on the
62        /// Cortex-M4.
63        SEPARATE OFFSET(0) NUMBITS(1) []
64    ],
65
66    Control [
67        /// Enables privileged software access to the default
68        /// memory map
69        PRIVDEFENA OFFSET(2) NUMBITS(1) [
70            Enable = 0,
71            Disable = 1
72        ],
73        /// Enables the operation of MPU during hard fault, NMI,
74        /// and FAULTMASK handlers
75        HFNMIENA OFFSET(1) NUMBITS(1) [
76            Enable = 0,
77            Disable = 1
78        ],
79        /// Enables the MPU
80        ENABLE OFFSET(0) NUMBITS(1) [
81            Disable = 0,
82            Enable = 1
83        ]
84    ],
85
86    RegionNumber [
87        /// Region indicating the MPU region referenced by the MPU_RBAR and
88        /// MPU_RASR registers. Range 0-7 corresponding to the MPU regions.
89        REGION OFFSET(0) NUMBITS(8) []
90    ],
91
92    RegionBaseAddress [
93        /// Base address of the currently selected MPU region.
94        ADDR OFFSET(5) NUMBITS(27) [],
95        /// MPU Region Number valid bit.
96        VALID OFFSET(4) NUMBITS(1) [
97            /// Use the base address specified in Region Number Register (RNR)
98            UseRNR = 0,
99            /// Use the value of the REGION field in this register (RBAR)
100            UseRBAR = 1
101        ],
102        /// Specifies which MPU region to set if VALID is set to 1.
103        REGION OFFSET(0) NUMBITS(4) []
104    ],
105
106    RegionAttributes [
107        /// Enables instruction fetches/execute permission
108        XN OFFSET(28) NUMBITS(1) [
109            Enable = 0,
110            Disable = 1
111        ],
112        /// Defines access permissions
113        AP OFFSET(24) NUMBITS(3) [
114            //                                 Privileged  Unprivileged
115            //                                 Access      Access
116            NoAccess = 0b000,               // --          --
117            PrivilegedOnly = 0b001,         // RW          --
118            UnprivilegedReadOnly = 0b010,   // RW          R-
119            ReadWrite = 0b011,              // RW          RW
120            Reserved = 0b100,               // undef       undef
121            PrivilegedOnlyReadOnly = 0b101, // R-          --
122            ReadOnly = 0b110,               // R-          R-
123            ReadOnlyAlias = 0b111           // R-          R-
124        ],
125        /// Subregion disable bits
126        SRD OFFSET(8) NUMBITS(8) [],
127        /// Specifies the region size, being 2^(SIZE+1) (minimum 3)
128        SIZE OFFSET(1) NUMBITS(5) [],
129        /// Enables the region
130        ENABLE OFFSET(0) NUMBITS(1) []
131    ]
132];
133
134/// State related to the real physical MPU.
135///
136/// There should only be one instantiation of this object as it represents
137/// real hardware.
138pub struct MPU<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> {
139    /// MMIO reference to MPU registers.
140    registers: StaticRef<MpuRegisters>,
141    /// Monotonically increasing counter for allocated regions, used
142    /// to assign unique IDs to `CortexMConfig` instances.
143    config_count: Cell<NonZeroUsize>,
144    /// Optimization logic. This is used to indicate which application the MPU
145    /// is currently configured for so that the MPU can skip updating when the
146    /// kernel returns to the same app.
147    hardware_is_configured_for: OptionalCell<NonZeroUsize>,
148}
149
150impl<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> MPU<NUM_REGIONS, MIN_REGION_SIZE> {
151    pub const unsafe fn new(registers: StaticRef<MpuRegisters>) -> Self {
152        Self {
153            registers,
154            config_count: Cell::new(NonZeroUsize::MIN),
155            hardware_is_configured_for: OptionalCell::empty(),
156        }
157    }
158
159    // Function useful for boards where the bootloader sets up some
160    // MPU configuration that conflicts with Tock's configuration:
161    pub unsafe fn clear_mpu(&self) {
162        self.registers.ctrl.write(Control::ENABLE::CLEAR);
163    }
164}
165
166/// Per-process struct storing MPU configuration for cortex-m MPUs.
167///
168/// The cortex-m MPU has eight regions, all of which must be configured (though
169/// unused regions may be configured as disabled). This struct caches the result
170/// of region configuration calculation.
171pub struct CortexMConfig<const NUM_REGIONS: usize> {
172    /// Unique ID for this configuration, assigned from a
173    /// monotonically increasing counter in the MPU struct.
174    id: NonZeroUsize,
175    /// The computed region configuration for this process.
176    regions: [CortexMRegion; NUM_REGIONS],
177    /// Has the configuration changed since the last time the this process
178    /// configuration was written to hardware?
179    is_dirty: Cell<bool>,
180}
181
182/// Records the index of the last region used for application RAM memory.
183/// Regions 0-APP_MEMORY_REGION_MAX_NUM are used for application RAM. Regions
184/// with indices above APP_MEMORY_REGION_MAX_NUM can be used for other MPU
185/// needs.
186const APP_MEMORY_REGION_MAX_NUM: usize = 1;
187
188impl<const NUM_REGIONS: usize> fmt::Display for CortexMConfig<NUM_REGIONS> {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        write!(f, "\r\n Cortex-M MPU")?;
191        for (i, region) in self.regions.iter().enumerate() {
192            if let Some(location) = region.location() {
193                let access_bits = region.attributes().read(RegionAttributes::AP);
194                let access_str = match access_bits {
195                    0b000 => "NoAccess",
196                    0b001 => "PrivilegedOnly",
197                    0b010 => "UnprivilegedReadOnly",
198                    0b011 => "ReadWrite",
199                    0b100 => "Reserved",
200                    0b101 => "PrivilegedOnlyReadOnly",
201                    0b110 => "ReadOnly",
202                    0b111 => "ReadOnlyAlias",
203                    _ => "ERR",
204                };
205                let start = location.0 as usize;
206                write!(
207                    f,
208                    "\
209                     \r\n  Region {}: [{:#010X}:{:#010X}], length: {} bytes; {} ({:#x})",
210                    i,
211                    start,
212                    start + location.1,
213                    location.1,
214                    access_str,
215                    access_bits,
216                )?;
217                let subregion_bits = region.attributes().read(RegionAttributes::SRD);
218                let subregion_size = location.1 / 8;
219                for j in 0..8 {
220                    write!(
221                        f,
222                        "\
223                         \r\n    Sub-region {}: [{:#010X}:{:#010X}], {}",
224                        j,
225                        start + j * subregion_size,
226                        start + (j + 1) * subregion_size,
227                        if (subregion_bits >> j) & 1 == 0 {
228                            "Enabled"
229                        } else {
230                            "Disabled"
231                        },
232                    )?;
233                }
234            } else {
235                write!(f, "\r\n  Region {}: Unused", i)?;
236            }
237        }
238        write!(f, "\r\n")
239    }
240}
241
242impl<const NUM_REGIONS: usize> CortexMConfig<NUM_REGIONS> {
243    fn unused_region_number(&self) -> Option<usize> {
244        for (number, region) in self.regions.iter().enumerate() {
245            if number <= APP_MEMORY_REGION_MAX_NUM {
246                continue;
247            }
248            if region.location().is_none() {
249                return Some(number);
250            }
251        }
252        None
253    }
254}
255
256/// Struct storing configuration for a Cortex-M MPU region.
257#[derive(Copy, Clone)]
258pub struct CortexMRegion {
259    location: Option<(*const u8, usize)>,
260    base_address: FieldValue<u32, RegionBaseAddress::Register>,
261    attributes: FieldValue<u32, RegionAttributes::Register>,
262}
263
264impl PartialEq<mpu::Region> for CortexMRegion {
265    fn eq(&self, other: &mpu::Region) -> bool {
266        self.location.is_some_and(|(addr, size)| {
267            core::ptr::eq(addr, other.start_address()) && size == other.size()
268        })
269    }
270}
271
272impl CortexMRegion {
273    fn new(
274        logical_start: *const u8,
275        logical_size: usize,
276        region_start: *const u8,
277        region_size: usize,
278        region_num: usize,
279        subregions: Option<(usize, usize)>,
280        permissions: mpu::Permissions,
281    ) -> Option<CortexMRegion> {
282        // Logical size must be above minimum size for cortexM MPU regions and
283        // and less than the size of the underlying physical region
284        if logical_size < CORTEXM_MIN_REGION_SIZE || region_size < logical_size {
285            return None;
286        }
287
288        // Determine access and execute permissions
289        let (access, execute) = match permissions {
290            mpu::Permissions::ReadWriteExecute => (
291                RegionAttributes::AP::ReadWrite,
292                RegionAttributes::XN::Enable,
293            ),
294            mpu::Permissions::ReadWriteOnly => (
295                RegionAttributes::AP::ReadWrite,
296                RegionAttributes::XN::Disable,
297            ),
298            mpu::Permissions::ReadExecuteOnly => (
299                RegionAttributes::AP::UnprivilegedReadOnly,
300                RegionAttributes::XN::Enable,
301            ),
302            mpu::Permissions::ReadOnly => (
303                RegionAttributes::AP::UnprivilegedReadOnly,
304                RegionAttributes::XN::Disable,
305            ),
306            mpu::Permissions::ExecuteOnly => (
307                RegionAttributes::AP::PrivilegedOnly,
308                RegionAttributes::XN::Enable,
309            ),
310        };
311
312        // Base address register
313        let base_address = RegionBaseAddress::ADDR.val((region_start as u32) >> 5)
314            + RegionBaseAddress::VALID::UseRBAR
315            + RegionBaseAddress::REGION.val(region_num as u32);
316
317        let size_value = math::log_base_two(region_size as u32) - 1;
318
319        // Attributes register
320        let mut attributes = RegionAttributes::ENABLE::SET
321            + RegionAttributes::SIZE.val(size_value)
322            + access
323            + execute;
324
325        // If using subregions, add a subregion mask. The mask is a 8-bit
326        // bitfield where `0` indicates that the corresponding subregion is enabled.
327        // To compute the mask, we start with all subregions disabled and enable
328        // the ones in the inclusive range [min_subregion, max_subregion].
329        if let Some((min_subregion, max_subregion)) = subregions {
330            let mask = (min_subregion..=max_subregion).fold(u8::MAX, |res, i| {
331                // Enable subregions bit by bit (1 ^ 1 == 0)
332                res ^ (1 << i)
333            });
334            attributes += RegionAttributes::SRD.val(mask as u32);
335        }
336
337        Some(CortexMRegion {
338            location: Some((logical_start, logical_size)),
339            base_address,
340            attributes,
341        })
342    }
343
344    fn empty(region_num: usize) -> CortexMRegion {
345        CortexMRegion {
346            location: None,
347            base_address: RegionBaseAddress::VALID::UseRBAR
348                + RegionBaseAddress::REGION.val(region_num as u32),
349            attributes: RegionAttributes::ENABLE::CLEAR,
350        }
351    }
352
353    fn location(&self) -> Option<(*const u8, usize)> {
354        self.location
355    }
356
357    fn base_address(&self) -> FieldValue<u32, RegionBaseAddress::Register> {
358        self.base_address
359    }
360
361    fn attributes(&self) -> FieldValue<u32, RegionAttributes::Register> {
362        self.attributes
363    }
364
365    fn overlaps(&self, other_start: *const u8, other_size: usize) -> bool {
366        let other_start = other_start as usize;
367        let other_end = other_start + other_size;
368
369        let (region_start, region_end) = match self.location {
370            Some((region_start, region_size)) => {
371                let region_start = region_start as usize;
372                let region_end = region_start + region_size;
373                (region_start, region_end)
374            }
375            None => return false,
376        };
377
378        region_start < other_end && other_start < region_end
379    }
380}
381
382// `MPU` is an unsafe trait, and with this implementation we guarantee
383// that we adhere to the semantics documented on that trait and its
384// associated types and methods.
385unsafe impl<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> mpu::MPU
386    for MPU<NUM_REGIONS, MIN_REGION_SIZE>
387{
388    type MpuConfig = CortexMConfig<NUM_REGIONS>;
389
390    fn enable_app_mpu(&self) {
391        // Enable the MPU, disable it during HardFault/NMI handlers, and allow
392        // privileged code access to all unprotected memory.
393        self.registers
394            .ctrl
395            .write(Control::ENABLE::SET + Control::HFNMIENA::CLEAR + Control::PRIVDEFENA::SET);
396    }
397
398    unsafe fn disable_app_mpu(&self) {
399        // The MPU is not enabled for privileged mode, so we don't have to do
400        // anything
401        self.registers.ctrl.write(Control::ENABLE::CLEAR);
402    }
403
404    fn number_total_regions(&self) -> usize {
405        self.registers.mpu_type.read(Type::DREGION) as usize
406    }
407
408    fn new_config(&self) -> Option<Self::MpuConfig> {
409        let id = self.config_count.get();
410        self.config_count.set(id.checked_add(1)?);
411
412        // Allocate the regions with index `0` first, then use `reset_config` to
413        // write the properly-indexed `CortexMRegion`s:
414        let mut ret = CortexMConfig {
415            id,
416            regions: [CortexMRegion::empty(0); NUM_REGIONS],
417            is_dirty: Cell::new(true),
418        };
419
420        self.reset_config(&mut ret);
421
422        Some(ret)
423    }
424
425    fn reset_config(&self, config: &mut Self::MpuConfig) {
426        for i in 0..NUM_REGIONS {
427            config.regions[i] = CortexMRegion::empty(i);
428        }
429
430        config.is_dirty.set(true);
431    }
432
433    fn allocate_region(
434        &self,
435        unallocated_memory_start: *const u8,
436        unallocated_memory_size: usize,
437        min_region_size: usize,
438        permissions: mpu::Permissions,
439        config: &mut Self::MpuConfig,
440    ) -> Option<mpu::Region> {
441        // Check that no previously allocated regions overlap the unallocated memory.
442        for region in config.regions.iter() {
443            if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
444                return None;
445            }
446        }
447
448        let region_num = config.unused_region_number()?;
449
450        // Logical region
451        let mut start = unallocated_memory_start as usize;
452        let mut size = min_region_size;
453
454        // Region start always has to align to minimum region size bytes
455        start = start.next_multiple_of(MIN_REGION_SIZE);
456
457        // Regions must be at least minimum region size bytes
458        if size < MIN_REGION_SIZE {
459            size = MIN_REGION_SIZE;
460        }
461
462        // Physical MPU region (might be larger than logical region if some subregions are disabled)
463        let mut region_start = start;
464        let mut region_size = size;
465        let mut subregions = None;
466
467        // We can only create an MPU region if the size is a power of two and it divides
468        // the start address. If this is not the case, the first thing we try to do to
469        // cover the memory region is to use a larger MPU region and expose certain subregions.
470        if size.count_ones() > 1 || !start.is_multiple_of(size) {
471            // Which (power-of-two) subregion size would align with the start
472            // address?
473            //
474            // We find this by taking smallest binary substring of the start
475            // address with exactly one bit:
476            //
477            //      1 << (start.trailing_zeros())
478            let subregion_size = {
479                let tz = start.trailing_zeros();
480                if tz < 32 {
481                    // Find the largest power of two that divides `start`
482                    1_usize << tz
483                } else {
484                    // This case means `start` is 0.
485                    let mut ceil = (size as u32).next_power_of_two() as usize;
486                    if ceil < 256 {
487                        ceil = 256
488                    }
489                    ceil / 8
490                }
491            };
492
493            // Once we have a subregion size, we get a region size by
494            // multiplying it by the number of subregions per region.
495            let underlying_region_size = subregion_size * 8;
496
497            // Finally, we calculate the region base by finding the nearest
498            // address below `start` that aligns with the region size.
499            let underlying_region_start = start - (start % underlying_region_size);
500
501            // If `size` doesn't align to the subregion size, extend it.
502            size = size.next_multiple_of(subregion_size);
503
504            let end = start + size;
505            let underlying_region_end = underlying_region_start + underlying_region_size;
506
507            // To use subregions, the region must be at least 256 bytes. Also, we need
508            // the amount of left over space in the region after `start` to be at least as
509            // large as the memory region we want to cover.
510            if subregion_size >= 32 && underlying_region_end >= end {
511                // The index of the first subregion to activate is the number of
512                // regions between `region_start` (MPU) and `start` (memory).
513                let min_subregion = (start - underlying_region_start) / subregion_size;
514
515                // The index of the last subregion to activate is the number of
516                // regions that fit in `len`, plus the `min_subregion`, minus one
517                // (because subregions are zero-indexed).
518                let max_subregion = min_subregion + size / subregion_size - 1;
519
520                region_start = underlying_region_start;
521                region_size = underlying_region_size;
522                subregions = Some((min_subregion, max_subregion));
523            } else {
524                // In this case, we can't use subregions to solve the alignment
525                // problem. Instead, we round up `size` to a power of two and
526                // shift `start` up in memory to make it align with `size`.
527                size = (size as u32).next_power_of_two() as usize;
528                start += size - (start % size);
529
530                region_start = start;
531                region_size = size;
532            }
533        }
534
535        // Check that our logical region fits in memory.
536        if start + size > (unallocated_memory_start as usize) + unallocated_memory_size {
537            return None;
538        }
539
540        let region = CortexMRegion::new(
541            start as *const u8,
542            size,
543            region_start as *const u8,
544            region_size,
545            region_num,
546            subregions,
547            permissions,
548        )?;
549
550        config.regions[region_num] = region;
551        config.is_dirty.set(true);
552
553        Some(mpu::Region::new(start as *const u8, size))
554    }
555
556    fn remove_memory_region(
557        &self,
558        region: mpu::Region,
559        config: &mut Self::MpuConfig,
560    ) -> Result<(), ()> {
561        let (idx, _r) = config
562            .regions
563            .iter()
564            .enumerate()
565            .find(|(_idx, r)| **r == region)
566            .ok_or(())?;
567
568        if idx <= APP_MEMORY_REGION_MAX_NUM {
569            return Err(());
570        }
571
572        config.regions[idx] = CortexMRegion::empty(idx);
573        config.is_dirty.set(true);
574
575        Ok(())
576    }
577
578    // When allocating memory for apps, we use two regions, each a power of two
579    // in size. By using two regions we halve their size, and also halve their
580    // alignment restrictions.
581    fn allocate_app_memory_region(
582        &self,
583        unallocated_memory_start: *const u8,
584        unallocated_memory_size: usize,
585        min_memory_size: usize,
586        initial_app_memory_size: usize,
587        initial_kernel_memory_size: usize,
588        permissions: mpu::Permissions,
589        config: &mut Self::MpuConfig,
590    ) -> Option<(*const u8, usize)> {
591        // Check that no previously allocated regions overlap the unallocated
592        // memory.
593        for region in config.regions.iter() {
594            if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
595                return None;
596            }
597        }
598
599        // Make sure there is enough memory for app memory and kernel memory.
600        let memory_size = cmp::max(
601            min_memory_size,
602            initial_app_memory_size + initial_kernel_memory_size,
603        );
604
605        // Size must be a power of two, so:
606        // https://www.youtube.com/watch?v=ovo6zwv6DX4.
607        let mut memory_size_po2 = (memory_size as u32).next_power_of_two() as usize;
608        let exponent = math::log_base_two(memory_size_po2 as u32);
609
610        // Check for compliance with the constraints of the MPU.
611        if exponent < 9 {
612            // Region sizes must be 256 bytes or larger to support subregions.
613            // Since we are using two regions, and each must be at least 256
614            // bytes, we need the entire memory region to be at least 512 bytes.
615            memory_size_po2 = 512;
616        } else if exponent > 32 {
617            // Region sizes must be 4GB or smaller.
618            return None;
619        }
620
621        // Region size is the actual size the MPU region will be set to, and is
622        // half of the total power of two size we are allocating to the app.
623        let mut region_size = memory_size_po2 / 2;
624
625        // The region should start as close as possible to the start of the
626        // unallocated memory.
627        let mut region_start = unallocated_memory_start as usize;
628
629        // If the start and length don't align, move region up until it does.
630        region_start = region_start.next_multiple_of(region_size);
631
632        // We allocate two MPU regions exactly over the process memory block,
633        // and we disable subregions at the end of this region to disallow
634        // access to the memory past the app break. As the app break later
635        // increases, we will be able to linearly grow the logical region
636        // covering app-owned memory by enabling more and more subregions. The
637        // Cortex-M MPU supports 8 subregions per region, so the size of this
638        // logical region is always a multiple of a sixteenth of the MPU region
639        // length.
640
641        // Determine the number of subregions to enable.
642        // Want `round_up(app_memory_size / subregion_size)`.
643        let mut num_enabled_subregions = initial_app_memory_size * 8 / region_size + 1;
644
645        let subregion_size = region_size / 8;
646
647        // Calculates the end address of the enabled subregions and the initial
648        // kernel memory break.
649        let subregions_enabled_end = region_start + num_enabled_subregions * subregion_size;
650        let kernel_memory_break = region_start + memory_size_po2 - initial_kernel_memory_size;
651
652        // If the last subregion covering app-owned memory overlaps the start of
653        // kernel-owned memory, we make the entire process memory block twice as
654        // big so there is plenty of space between app-owned and kernel-owned
655        // memory.
656        if subregions_enabled_end > kernel_memory_break {
657            memory_size_po2 *= 2;
658            region_size *= 2;
659
660            region_start = region_start.next_multiple_of(region_size);
661
662            num_enabled_subregions = initial_app_memory_size * 8 / region_size + 1;
663        }
664
665        // Make sure the region fits in the unallocated memory.
666        if region_start + memory_size_po2
667            > (unallocated_memory_start as usize) + unallocated_memory_size
668        {
669            return None;
670        }
671
672        // Get the number of subregions enabled in each of the two MPU regions.
673        let num_enabled_subregions0 = cmp::min(num_enabled_subregions, 8);
674        let num_enabled_subregions1 = num_enabled_subregions.saturating_sub(8);
675
676        let region0 = CortexMRegion::new(
677            region_start as *const u8,
678            region_size,
679            region_start as *const u8,
680            region_size,
681            0,
682            Some((0, num_enabled_subregions0 - 1)),
683            permissions,
684        )?;
685
686        // We cannot have a completely unused MPU region
687        let region1 = if num_enabled_subregions1 == 0 {
688            CortexMRegion::empty(1)
689        } else {
690            CortexMRegion::new(
691                (region_start + region_size) as *const u8,
692                region_size,
693                (region_start + region_size) as *const u8,
694                region_size,
695                1,
696                Some((0, num_enabled_subregions1 - 1)),
697                permissions,
698            )?
699        };
700
701        config.regions[0] = region0;
702        config.regions[1] = region1;
703        config.is_dirty.set(true);
704
705        Some((region_start as *const u8, memory_size_po2))
706    }
707
708    fn update_app_memory_region(
709        &self,
710        app_memory_break: *const u8,
711        kernel_memory_break: *const u8,
712        permissions: mpu::Permissions,
713        config: &mut Self::MpuConfig,
714    ) -> Result<(), ()> {
715        // Get first region, or error if the process tried to update app memory
716        // MPU region before it was created.
717        let (region_start_ptr, region_size) = config.regions[0].location().ok_or(())?;
718        let region_start = region_start_ptr as usize;
719
720        let app_memory_break = app_memory_break as usize;
721        let kernel_memory_break = kernel_memory_break as usize;
722
723        // Out of memory
724        if app_memory_break > kernel_memory_break {
725            return Err(());
726        }
727
728        // Number of bytes the process wants access to.
729        let app_memory_size = app_memory_break - region_start;
730
731        // There are eight subregions for every region in the Cortex-M3/4 MPU.
732        let subregion_size = region_size / 8;
733
734        // Determine the number of subregions to enable.
735        // Want `round_up(app_memory_size / subregion_size)`.
736        let num_enabled_subregions = app_memory_size.div_ceil(subregion_size);
737
738        let subregions_enabled_end = region_start + subregion_size * num_enabled_subregions;
739
740        // If we can no longer cover app memory with an MPU region without
741        // overlapping kernel memory, we fail.
742        if subregions_enabled_end > kernel_memory_break {
743            return Err(());
744        }
745
746        // Get the number of subregions enabled in each of the two MPU regions.
747        let num_enabled_subregions0 = cmp::min(num_enabled_subregions, 8);
748        let num_enabled_subregions1 = num_enabled_subregions.saturating_sub(8);
749
750        let region0 = CortexMRegion::new(
751            region_start as *const u8,
752            region_size,
753            region_start as *const u8,
754            region_size,
755            0,
756            Some((0, num_enabled_subregions0 - 1)),
757            permissions,
758        )
759        .ok_or(())?;
760
761        let region1 = if num_enabled_subregions1 == 0 {
762            CortexMRegion::empty(1)
763        } else {
764            CortexMRegion::new(
765                (region_start + region_size) as *const u8,
766                region_size,
767                (region_start + region_size) as *const u8,
768                region_size,
769                1,
770                Some((0, num_enabled_subregions1 - 1)),
771                permissions,
772            )
773            .ok_or(())?
774        };
775
776        config.regions[0] = region0;
777        config.regions[1] = region1;
778        config.is_dirty.set(true);
779
780        Ok(())
781    }
782
783    unsafe fn configure_mpu(&self, config: &Self::MpuConfig) {
784        // If the hardware is already configured for this app and the app's MPU
785        // configuration has not changed, then skip the hardware update.
786        if !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
787            // Set MPU regions
788            for region in config.regions.iter() {
789                self.registers.rbar.write(region.base_address());
790                self.registers.rasr.write(region.attributes());
791            }
792            self.hardware_is_configured_for.set(config.id);
793            config.is_dirty.set(false);
794        }
795    }
796}