1use 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
20const CORTEXM_MIN_REGION_SIZE: usize = 32;
23
24#[repr(C)]
28pub struct MpuRegisters {
29 pub mpu_type: ReadOnly<u32, Type::Register>,
32
33 pub ctrl: ReadWrite<u32, Control::Register>,
38
39 pub rnr: ReadWrite<u32, RegionNumber::Register>,
42
43 pub rbar: ReadWrite<u32, RegionBaseAddress::Register>,
45
46 pub rasr: ReadWrite<u32, RegionAttributes::Register>,
49}
50
51register_bitfields![u32,
52 Type [
53 IREGION OFFSET(16) NUMBITS(8) [],
55 DREGION OFFSET(8) NUMBITS(8) [],
58 SEPARATE OFFSET(0) NUMBITS(1) []
62 ],
63
64 Control [
65 PRIVDEFENA OFFSET(2) NUMBITS(1) [
68 Enable = 0,
69 Disable = 1
70 ],
71 HFNMIENA OFFSET(1) NUMBITS(1) [
74 Enable = 0,
75 Disable = 1
76 ],
77 ENABLE OFFSET(0) NUMBITS(1) [
79 Disable = 0,
80 Enable = 1
81 ]
82 ],
83
84 RegionNumber [
85 REGION OFFSET(0) NUMBITS(8) []
88 ],
89
90 RegionBaseAddress [
91 ADDR OFFSET(5) NUMBITS(27) [],
93 VALID OFFSET(4) NUMBITS(1) [
95 UseRNR = 0,
97 UseRBAR = 1
99 ],
100 REGION OFFSET(0) NUMBITS(4) []
102 ],
103
104 RegionAttributes [
105 XN OFFSET(28) NUMBITS(1) [
107 Enable = 0,
108 Disable = 1
109 ],
110 AP OFFSET(24) NUMBITS(3) [
112 NoAccess = 0b000, PrivilegedOnly = 0b001, UnprivilegedReadOnly = 0b010, ReadWrite = 0b011, Reserved = 0b100, PrivilegedOnlyReadOnly = 0b101, ReadOnly = 0b110, ReadOnlyAlias = 0b111 ],
123 SRD OFFSET(8) NUMBITS(8) [],
125 SIZE OFFSET(1) NUMBITS(5) [],
127 ENABLE OFFSET(0) NUMBITS(1) []
129 ]
130];
131
132const MPU_BASE_ADDRESS: StaticRef<MpuRegisters> =
133 unsafe { StaticRef::new(0xE000ED90 as *const MpuRegisters) };
134
135pub struct MPU<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> {
140 registers: StaticRef<MpuRegisters>,
142 config_count: Cell<NonZeroUsize>,
145 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 pub unsafe fn clear_mpu(&self) {
163 self.registers.ctrl.write(Control::ENABLE::CLEAR);
164 }
165}
166
167pub struct CortexMConfig<const NUM_REGIONS: usize> {
173 id: NonZeroUsize,
176 regions: [CortexMRegion; NUM_REGIONS],
178 is_dirty: Cell<bool>,
181}
182
183const 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#[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 if logical_size < CORTEXM_MIN_REGION_SIZE || region_size < logical_size {
285 return None;
286 }
287
288 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 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 let mut attributes = RegionAttributes::ENABLE::SET
321 + RegionAttributes::SIZE.val(size_value)
322 + access
323 + execute;
324
325 if let Some((min_subregion, max_subregion)) = subregions {
330 let mask = (min_subregion..=max_subregion).fold(u8::MAX, |res, i| {
331 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 self.registers
391 .ctrl
392 .write(Control::ENABLE::SET + Control::HFNMIENA::CLEAR + Control::PRIVDEFENA::SET);
393 }
394
395 fn disable_app_mpu(&self) {
396 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 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 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 let mut start = unallocated_memory_start as usize;
449 let mut size = min_region_size;
450
451 if start % MIN_REGION_SIZE != 0 {
453 start += MIN_REGION_SIZE - (start % MIN_REGION_SIZE);
454 }
455
456 if size < MIN_REGION_SIZE {
458 size = MIN_REGION_SIZE;
459 }
460
461 let mut region_start = start;
463 let mut region_size = size;
464 let mut subregions = None;
465
466 if size.count_ones() > 1 || start % size != 0 {
470 let subregion_size = {
478 let tz = start.trailing_zeros();
479 if tz < 32 {
480 1_usize << tz
482 } else {
483 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 let underlying_region_size = subregion_size * 8;
495
496 let underlying_region_start = start - (start % underlying_region_size);
499
500 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 if subregion_size >= 32 && underlying_region_end >= end {
512 let min_subregion = (start - underlying_region_start) / subregion_size;
515
516 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 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 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 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 for region in config.regions.iter() {
595 if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
596 return None;
597 }
598 }
599
600 let memory_size = cmp::max(
602 min_memory_size,
603 initial_app_memory_size + initial_kernel_memory_size,
604 );
605
606 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 if exponent < 9 {
613 memory_size_po2 = 512;
617 } else if exponent > 32 {
618 return None;
620 }
621
622 let mut region_size = memory_size_po2 / 2;
625
626 let mut region_start = unallocated_memory_start as usize;
629
630 if region_start % region_size != 0 {
632 region_start += region_size - (region_start % region_size);
633 }
634
635 let mut num_enabled_subregions = initial_app_memory_size * 8 / region_size + 1;
647
648 let subregion_size = region_size / 8;
649
650 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 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 if region_start + memory_size_po2
672 > (unallocated_memory_start as usize) + unallocated_memory_size
673 {
674 return None;
675 }
676
677 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 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 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 if app_memory_break > kernel_memory_break {
730 return Err(());
731 }
732
733 let app_memory_size = app_memory_break - region_start;
735
736 let subregion_size = region_size / 8;
738
739 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 subregions_enabled_end > kernel_memory_break {
748 return Err(());
749 }
750
751 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 !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
792 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}