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