1use 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
22const CORTEXM_MIN_REGION_SIZE: usize = 32;
25
26#[repr(C)]
30pub struct MpuRegisters {
31 pub mpu_type: ReadOnly<u32, Type::Register>,
34
35 pub ctrl: ReadWrite<u32, Control::Register>,
40
41 pub rnr: ReadWrite<u32, RegionNumber::Register>,
44
45 pub rbar: ReadWrite<u32, RegionBaseAddress::Register>,
47
48 pub rasr: ReadWrite<u32, RegionAttributes::Register>,
51}
52
53register_bitfields![u32,
54 Type [
55 IREGION OFFSET(16) NUMBITS(8) [],
57 DREGION OFFSET(8) NUMBITS(8) [],
60 SEPARATE OFFSET(0) NUMBITS(1) []
64 ],
65
66 Control [
67 PRIVDEFENA OFFSET(2) NUMBITS(1) [
70 Enable = 0,
71 Disable = 1
72 ],
73 HFNMIENA OFFSET(1) NUMBITS(1) [
76 Enable = 0,
77 Disable = 1
78 ],
79 ENABLE OFFSET(0) NUMBITS(1) [
81 Disable = 0,
82 Enable = 1
83 ]
84 ],
85
86 RegionNumber [
87 REGION OFFSET(0) NUMBITS(8) []
90 ],
91
92 RegionBaseAddress [
93 ADDR OFFSET(5) NUMBITS(27) [],
95 VALID OFFSET(4) NUMBITS(1) [
97 UseRNR = 0,
99 UseRBAR = 1
101 ],
102 REGION OFFSET(0) NUMBITS(4) []
104 ],
105
106 RegionAttributes [
107 XN OFFSET(28) NUMBITS(1) [
109 Enable = 0,
110 Disable = 1
111 ],
112 AP OFFSET(24) NUMBITS(3) [
114 NoAccess = 0b000, PrivilegedOnly = 0b001, UnprivilegedReadOnly = 0b010, ReadWrite = 0b011, Reserved = 0b100, PrivilegedOnlyReadOnly = 0b101, ReadOnly = 0b110, ReadOnlyAlias = 0b111 ],
125 SRD OFFSET(8) NUMBITS(8) [],
127 SIZE OFFSET(1) NUMBITS(5) [],
129 ENABLE OFFSET(0) NUMBITS(1) []
131 ]
132];
133
134pub struct MPU<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> {
139 registers: StaticRef<MpuRegisters>,
141 config_count: Cell<NonZeroUsize>,
144 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 pub unsafe fn clear_mpu(&self) {
162 self.registers.ctrl.write(Control::ENABLE::CLEAR);
163 }
164}
165
166pub struct CortexMConfig<const NUM_REGIONS: usize> {
172 id: NonZeroUsize,
175 regions: [CortexMRegion; NUM_REGIONS],
177 is_dirty: Cell<bool>,
180}
181
182const 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#[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 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
382unsafe 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 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 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 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 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 let mut start = unallocated_memory_start as usize;
452 let mut size = min_region_size;
453
454 start = start.next_multiple_of(MIN_REGION_SIZE);
456
457 if size < MIN_REGION_SIZE {
459 size = MIN_REGION_SIZE;
460 }
461
462 let mut region_start = start;
464 let mut region_size = size;
465 let mut subregions = None;
466
467 if size.count_ones() > 1 || !start.is_multiple_of(size) {
471 let subregion_size = {
479 let tz = start.trailing_zeros();
480 if tz < 32 {
481 1_usize << tz
483 } else {
484 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 let underlying_region_size = subregion_size * 8;
496
497 let underlying_region_start = start - (start % underlying_region_size);
500
501 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 if subregion_size >= 32 && underlying_region_end >= end {
511 let min_subregion = (start - underlying_region_start) / subregion_size;
514
515 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 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 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 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 for region in config.regions.iter() {
594 if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
595 return None;
596 }
597 }
598
599 let memory_size = cmp::max(
601 min_memory_size,
602 initial_app_memory_size + initial_kernel_memory_size,
603 );
604
605 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 if exponent < 9 {
612 memory_size_po2 = 512;
616 } else if exponent > 32 {
617 return None;
619 }
620
621 let mut region_size = memory_size_po2 / 2;
624
625 let mut region_start = unallocated_memory_start as usize;
628
629 region_start = region_start.next_multiple_of(region_size);
631
632 let mut num_enabled_subregions = initial_app_memory_size * 8 / region_size + 1;
644
645 let subregion_size = region_size / 8;
646
647 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 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 if region_start + memory_size_po2
667 > (unallocated_memory_start as usize) + unallocated_memory_size
668 {
669 return None;
670 }
671
672 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 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 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 if app_memory_break > kernel_memory_break {
725 return Err(());
726 }
727
728 let app_memory_size = app_memory_break - region_start;
730
731 let subregion_size = region_size / 8;
733
734 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 subregions_enabled_end > kernel_memory_break {
743 return Err(());
744 }
745
746 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 !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
787 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}