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
132const MPU_BASE_ADDRESS: StaticRef<MpuRegisters> =
133    unsafe { StaticRef::new(0xE000ED90 as *const MpuRegisters) };
134
135/// State related to the real physical MPU.
136///
137/// There should only be one instantiation of this object as it represents
138/// real hardware.
139pub struct MPU<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> {
140    /// MMIO reference to MPU registers.
141    registers: StaticRef<MpuRegisters>,
142    /// Monotonically increasing counter for allocated regions, used
143    /// to assign unique IDs to `CortexMConfig` instances.
144    config_count: Cell<NonZeroUsize>,
145    /// Optimization logic. This is used to indicate which application the MPU
146    /// is currently configured for so that the MPU can skip updating when the
147    /// kernel returns to the same app.
148    hardware_is_configured_for: OptionalCell<NonZeroUsize>,
149}
150
151impl<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> MPU<NUM_REGIONS, MIN_REGION_SIZE> {
152    pub const unsafe fn new() -> Self {
153        Self {
154            registers: MPU_BASE_ADDRESS,
155            config_count: Cell::new(NonZeroUsize::MIN),
156            hardware_is_configured_for: OptionalCell::empty(),
157        }
158    }
159
160    // Function useful for boards where the bootloader sets up some
161    // MPU configuration that conflicts with Tock's configuration:
162    pub unsafe fn clear_mpu(&self) {
163        self.registers.ctrl.write(Control::ENABLE::CLEAR);
164    }
165}
166
167/// Per-process struct storing MPU configuration for cortex-m MPUs.
168///
169/// The cortex-m MPU has eight regions, all of which must be configured (though
170/// unused regions may be configured as disabled). This struct caches the result
171/// of region configuration calculation.
172pub struct CortexMConfig<const NUM_REGIONS: usize> {
173    /// Unique ID for this configuration, assigned from a
174    /// monotonically increasing counter in the MPU struct.
175    id: NonZeroUsize,
176    /// The computed region configuration for this process.
177    regions: [CortexMRegion; NUM_REGIONS],
178    /// Has the configuration changed since the last time the this process
179    /// configuration was written to hardware?
180    is_dirty: Cell<bool>,
181}
182
183/// Records the index of the last region used for application RAM memory.
184/// Regions 0-APP_MEMORY_REGION_MAX_NUM are used for application RAM. Regions
185/// with indices above APP_MEMORY_REGION_MAX_NUM can be used for other MPU
186/// needs.
187const APP_MEMORY_REGION_MAX_NUM: usize = 1;
188
189impl<const NUM_REGIONS: usize> fmt::Display for CortexMConfig<NUM_REGIONS> {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        write!(f, "\r\n Cortex-M MPU")?;
192        for (i, region) in self.regions.iter().enumerate() {
193            if let Some(location) = region.location() {
194                let access_bits = region.attributes().read(RegionAttributes::AP);
195                let access_str = match access_bits {
196                    0b000 => "NoAccess",
197                    0b001 => "PrivilegedOnly",
198                    0b010 => "UnprivilegedReadOnly",
199                    0b011 => "ReadWrite",
200                    0b100 => "Reserved",
201                    0b101 => "PrivilegedOnlyReadOnly",
202                    0b110 => "ReadOnly",
203                    0b111 => "ReadOnlyAlias",
204                    _ => "ERR",
205                };
206                let start = location.0 as usize;
207                write!(
208                    f,
209                    "\
210                     \r\n  Region {}: [{:#010X}:{:#010X}], length: {} bytes; {} ({:#x})",
211                    i,
212                    start,
213                    start + location.1,
214                    location.1,
215                    access_str,
216                    access_bits,
217                )?;
218                let subregion_bits = region.attributes().read(RegionAttributes::SRD);
219                let subregion_size = location.1 / 8;
220                for j in 0..8 {
221                    write!(
222                        f,
223                        "\
224                         \r\n    Sub-region {}: [{:#010X}:{:#010X}], {}",
225                        j,
226                        start + j * subregion_size,
227                        start + (j + 1) * subregion_size,
228                        if (subregion_bits >> j) & 1 == 0 {
229                            "Enabled"
230                        } else {
231                            "Disabled"
232                        },
233                    )?;
234                }
235            } else {
236                write!(f, "\r\n  Region {}: Unused", i)?;
237            }
238        }
239        write!(f, "\r\n")
240    }
241}
242
243impl<const NUM_REGIONS: usize> CortexMConfig<NUM_REGIONS> {
244    fn unused_region_number(&self) -> Option<usize> {
245        for (number, region) in self.regions.iter().enumerate() {
246            if number <= APP_MEMORY_REGION_MAX_NUM {
247                continue;
248            }
249            if let None = region.location() {
250                return Some(number);
251            }
252        }
253        None
254    }
255}
256
257/// Struct storing configuration for a Cortex-M MPU region.
258#[derive(Copy, Clone)]
259pub struct CortexMRegion {
260    location: Option<(*const u8, usize)>,
261    base_address: FieldValue<u32, RegionBaseAddress::Register>,
262    attributes: FieldValue<u32, RegionAttributes::Register>,
263}
264
265impl PartialEq<mpu::Region> for CortexMRegion {
266    fn eq(&self, other: &mpu::Region) -> bool {
267        self.location
268            .is_some_and(|(addr, size)| addr == other.start_address() && size == other.size())
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
382impl<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> mpu::MPU
383    for MPU<NUM_REGIONS, MIN_REGION_SIZE>
384{
385    type MpuConfig = CortexMConfig<NUM_REGIONS>;
386
387    fn enable_app_mpu(&self) {
388        // Enable the MPU, disable it during HardFault/NMI handlers, and allow
389        // privileged code access to all unprotected memory.
390        self.registers
391            .ctrl
392            .write(Control::ENABLE::SET + Control::HFNMIENA::CLEAR + Control::PRIVDEFENA::SET);
393    }
394
395    fn disable_app_mpu(&self) {
396        // The MPU is not enabled for privileged mode, so we don't have to do
397        // anything
398        self.registers.ctrl.write(Control::ENABLE::CLEAR);
399    }
400
401    fn number_total_regions(&self) -> usize {
402        self.registers.mpu_type.read(Type::DREGION) as usize
403    }
404
405    fn new_config(&self) -> Option<Self::MpuConfig> {
406        let id = self.config_count.get();
407        self.config_count.set(id.checked_add(1)?);
408
409        // Allocate the regions with index `0` first, then use `reset_config` to
410        // write the properly-indexed `CortexMRegion`s:
411        let mut ret = CortexMConfig {
412            id,
413            regions: [CortexMRegion::empty(0); NUM_REGIONS],
414            is_dirty: Cell::new(true),
415        };
416
417        self.reset_config(&mut ret);
418
419        Some(ret)
420    }
421
422    fn reset_config(&self, config: &mut Self::MpuConfig) {
423        for i in 0..NUM_REGIONS {
424            config.regions[i] = CortexMRegion::empty(i);
425        }
426
427        config.is_dirty.set(true);
428    }
429
430    fn allocate_region(
431        &self,
432        unallocated_memory_start: *const u8,
433        unallocated_memory_size: usize,
434        min_region_size: usize,
435        permissions: mpu::Permissions,
436        config: &mut Self::MpuConfig,
437    ) -> Option<mpu::Region> {
438        // Check that no previously allocated regions overlap the unallocated memory.
439        for region in config.regions.iter() {
440            if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
441                return None;
442            }
443        }
444
445        let region_num = config.unused_region_number()?;
446
447        // Logical region
448        let mut start = unallocated_memory_start as usize;
449        let mut size = min_region_size;
450
451        // Region start always has to align to minimum region size bytes
452        if start % MIN_REGION_SIZE != 0 {
453            start += MIN_REGION_SIZE - (start % MIN_REGION_SIZE);
454        }
455
456        // Regions must be at least minimum region size bytes
457        if size < MIN_REGION_SIZE {
458            size = MIN_REGION_SIZE;
459        }
460
461        // Physical MPU region (might be larger than logical region if some subregions are disabled)
462        let mut region_start = start;
463        let mut region_size = size;
464        let mut subregions = None;
465
466        // We can only create an MPU region if the size is a power of two and it divides
467        // the start address. If this is not the case, the first thing we try to do to
468        // cover the memory region is to use a larger MPU region and expose certain subregions.
469        if size.count_ones() > 1 || start % size != 0 {
470            // Which (power-of-two) subregion size would align with the start
471            // address?
472            //
473            // We find this by taking smallest binary substring of the start
474            // address with exactly one bit:
475            //
476            //      1 << (start.trailing_zeros())
477            let subregion_size = {
478                let tz = start.trailing_zeros();
479                if tz < 32 {
480                    // Find the largest power of two that divides `start`
481                    1_usize << tz
482                } else {
483                    // This case means `start` is 0.
484                    let mut ceil = math::closest_power_of_two(size as u32) as usize;
485                    if ceil < 256 {
486                        ceil = 256
487                    }
488                    ceil / 8
489                }
490            };
491
492            // Once we have a subregion size, we get a region size by
493            // multiplying it by the number of subregions per region.
494            let underlying_region_size = subregion_size * 8;
495
496            // Finally, we calculate the region base by finding the nearest
497            // address below `start` that aligns with the region size.
498            let underlying_region_start = start - (start % underlying_region_size);
499
500            // If `size` doesn't align to the subregion size, extend it.
501            if size % subregion_size != 0 {
502                size += subregion_size - (size % subregion_size);
503            }
504
505            let end = start + size;
506            let underlying_region_end = underlying_region_start + underlying_region_size;
507
508            // To use subregions, the region must be at least 256 bytes. Also, we need
509            // the amount of left over space in the region after `start` to be at least as
510            // large as the memory region we want to cover.
511            if subregion_size >= 32 && underlying_region_end >= end {
512                // The index of the first subregion to activate is the number of
513                // regions between `region_start` (MPU) and `start` (memory).
514                let min_subregion = (start - underlying_region_start) / subregion_size;
515
516                // The index of the last subregion to activate is the number of
517                // regions that fit in `len`, plus the `min_subregion`, minus one
518                // (because subregions are zero-indexed).
519                let max_subregion = min_subregion + size / subregion_size - 1;
520
521                region_start = underlying_region_start;
522                region_size = underlying_region_size;
523                subregions = Some((min_subregion, max_subregion));
524            } else {
525                // In this case, we can't use subregions to solve the alignment
526                // problem. Instead, we round up `size` to a power of two and
527                // shift `start` up in memory to make it align with `size`.
528                size = math::closest_power_of_two(size as u32) as usize;
529                start += size - (start % size);
530
531                region_start = start;
532                region_size = size;
533            }
534        }
535
536        // Check that our logical region fits in memory.
537        if start + size > (unallocated_memory_start as usize) + unallocated_memory_size {
538            return None;
539        }
540
541        let region = CortexMRegion::new(
542            start as *const u8,
543            size,
544            region_start as *const u8,
545            region_size,
546            region_num,
547            subregions,
548            permissions,
549        )?;
550
551        config.regions[region_num] = region;
552        config.is_dirty.set(true);
553
554        Some(mpu::Region::new(start as *const u8, size))
555    }
556
557    fn remove_memory_region(
558        &self,
559        region: mpu::Region,
560        config: &mut Self::MpuConfig,
561    ) -> Result<(), ()> {
562        let (idx, _r) = config
563            .regions
564            .iter()
565            .enumerate()
566            .find(|(_idx, r)| **r == region)
567            .ok_or(())?;
568
569        if idx <= APP_MEMORY_REGION_MAX_NUM {
570            return Err(());
571        }
572
573        config.regions[idx] = CortexMRegion::empty(idx);
574        config.is_dirty.set(true);
575
576        Ok(())
577    }
578
579    // When allocating memory for apps, we use two regions, each a power of two
580    // in size. By using two regions we halve their size, and also halve their
581    // alignment restrictions.
582    fn allocate_app_memory_region(
583        &self,
584        unallocated_memory_start: *const u8,
585        unallocated_memory_size: usize,
586        min_memory_size: usize,
587        initial_app_memory_size: usize,
588        initial_kernel_memory_size: usize,
589        permissions: mpu::Permissions,
590        config: &mut Self::MpuConfig,
591    ) -> Option<(*const u8, usize)> {
592        // Check that no previously allocated regions overlap the unallocated
593        // memory.
594        for region in config.regions.iter() {
595            if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
596                return None;
597            }
598        }
599
600        // Make sure there is enough memory for app memory and kernel memory.
601        let memory_size = cmp::max(
602            min_memory_size,
603            initial_app_memory_size + initial_kernel_memory_size,
604        );
605
606        // Size must be a power of two, so:
607        // https://www.youtube.com/watch?v=ovo6zwv6DX4.
608        let mut memory_size_po2 = math::closest_power_of_two(memory_size as u32) as usize;
609        let exponent = math::log_base_two(memory_size_po2 as u32);
610
611        // Check for compliance with the constraints of the MPU.
612        if exponent < 9 {
613            // Region sizes must be 256 bytes or larger to support subregions.
614            // Since we are using two regions, and each must be at least 256
615            // bytes, we need the entire memory region to be at least 512 bytes.
616            memory_size_po2 = 512;
617        } else if exponent > 32 {
618            // Region sizes must be 4GB or smaller.
619            return None;
620        }
621
622        // Region size is the actual size the MPU region will be set to, and is
623        // half of the total power of two size we are allocating to the app.
624        let mut region_size = memory_size_po2 / 2;
625
626        // The region should start as close as possible to the start of the
627        // unallocated memory.
628        let mut region_start = unallocated_memory_start as usize;
629
630        // If the start and length don't align, move region up until it does.
631        if region_start % region_size != 0 {
632            region_start += region_size - (region_start % region_size);
633        }
634
635        // We allocate two MPU regions exactly over the process memory block,
636        // and we disable subregions at the end of this region to disallow
637        // access to the memory past the app break. As the app break later
638        // increases, we will be able to linearly grow the logical region
639        // covering app-owned memory by enabling more and more subregions. The
640        // Cortex-M MPU supports 8 subregions per region, so the size of this
641        // logical region is always a multiple of a sixteenth of the MPU region
642        // length.
643
644        // Determine the number of subregions to enable.
645        // Want `round_up(app_memory_size / subregion_size)`.
646        let mut num_enabled_subregions = initial_app_memory_size * 8 / region_size + 1;
647
648        let subregion_size = region_size / 8;
649
650        // Calculates the end address of the enabled subregions and the initial
651        // kernel memory break.
652        let subregions_enabled_end = region_start + num_enabled_subregions * subregion_size;
653        let kernel_memory_break = region_start + memory_size_po2 - initial_kernel_memory_size;
654
655        // If the last subregion covering app-owned memory overlaps the start of
656        // kernel-owned memory, we make the entire process memory block twice as
657        // big so there is plenty of space between app-owned and kernel-owned
658        // memory.
659        if subregions_enabled_end > kernel_memory_break {
660            memory_size_po2 *= 2;
661            region_size *= 2;
662
663            if region_start % region_size != 0 {
664                region_start += region_size - (region_start % region_size);
665            }
666
667            num_enabled_subregions = initial_app_memory_size * 8 / region_size + 1;
668        }
669
670        // Make sure the region fits in the unallocated memory.
671        if region_start + memory_size_po2
672            > (unallocated_memory_start as usize) + unallocated_memory_size
673        {
674            return None;
675        }
676
677        // Get the number of subregions enabled in each of the two MPU regions.
678        let num_enabled_subregions0 = cmp::min(num_enabled_subregions, 8);
679        let num_enabled_subregions1 = num_enabled_subregions.saturating_sub(8);
680
681        let region0 = CortexMRegion::new(
682            region_start as *const u8,
683            region_size,
684            region_start as *const u8,
685            region_size,
686            0,
687            Some((0, num_enabled_subregions0 - 1)),
688            permissions,
689        )?;
690
691        // We cannot have a completely unused MPU region
692        let region1 = if num_enabled_subregions1 == 0 {
693            CortexMRegion::empty(1)
694        } else {
695            CortexMRegion::new(
696                (region_start + region_size) as *const u8,
697                region_size,
698                (region_start + region_size) as *const u8,
699                region_size,
700                1,
701                Some((0, num_enabled_subregions1 - 1)),
702                permissions,
703            )?
704        };
705
706        config.regions[0] = region0;
707        config.regions[1] = region1;
708        config.is_dirty.set(true);
709
710        Some((region_start as *const u8, memory_size_po2))
711    }
712
713    fn update_app_memory_region(
714        &self,
715        app_memory_break: *const u8,
716        kernel_memory_break: *const u8,
717        permissions: mpu::Permissions,
718        config: &mut Self::MpuConfig,
719    ) -> Result<(), ()> {
720        // Get first region, or error if the process tried to update app memory
721        // MPU region before it was created.
722        let (region_start_ptr, region_size) = config.regions[0].location().ok_or(())?;
723        let region_start = region_start_ptr as usize;
724
725        let app_memory_break = app_memory_break as usize;
726        let kernel_memory_break = kernel_memory_break as usize;
727
728        // Out of memory
729        if app_memory_break > kernel_memory_break {
730            return Err(());
731        }
732
733        // Number of bytes the process wants access to.
734        let app_memory_size = app_memory_break - region_start;
735
736        // There are eight subregions for every region in the Cortex-M3/4 MPU.
737        let subregion_size = region_size / 8;
738
739        // Determine the number of subregions to enable.
740        // Want `round_up(app_memory_size / subregion_size)`.
741        let num_enabled_subregions = app_memory_size.div_ceil(subregion_size);
742
743        let subregions_enabled_end = region_start + subregion_size * num_enabled_subregions;
744
745        // If we can no longer cover app memory with an MPU region without
746        // overlapping kernel memory, we fail.
747        if subregions_enabled_end > kernel_memory_break {
748            return Err(());
749        }
750
751        // Get the number of subregions enabled in each of the two MPU regions.
752        let num_enabled_subregions0 = cmp::min(num_enabled_subregions, 8);
753        let num_enabled_subregions1 = num_enabled_subregions.saturating_sub(8);
754
755        let region0 = CortexMRegion::new(
756            region_start as *const u8,
757            region_size,
758            region_start as *const u8,
759            region_size,
760            0,
761            Some((0, num_enabled_subregions0 - 1)),
762            permissions,
763        )
764        .ok_or(())?;
765
766        let region1 = if num_enabled_subregions1 == 0 {
767            CortexMRegion::empty(1)
768        } else {
769            CortexMRegion::new(
770                (region_start + region_size) as *const u8,
771                region_size,
772                (region_start + region_size) as *const u8,
773                region_size,
774                1,
775                Some((0, num_enabled_subregions1 - 1)),
776                permissions,
777            )
778            .ok_or(())?
779        };
780
781        config.regions[0] = region0;
782        config.regions[1] = region1;
783        config.is_dirty.set(true);
784
785        Ok(())
786    }
787
788    fn configure_mpu(&self, config: &Self::MpuConfig) {
789        // If the hardware is already configured for this app and the app's MPU
790        // configuration has not changed, then skip the hardware update.
791        if !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
792            // Set MPU regions
793            for region in config.regions.iter() {
794                self.registers.rbar.write(region.base_address());
795                self.registers.rasr.write(region.attributes());
796            }
797            self.hardware_is_configured_for.set(config.id);
798            config.is_dirty.set(false);
799        }
800    }
801}