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
132pub struct MPU<const NUM_REGIONS: usize, const MIN_REGION_SIZE: usize> {
137 registers: StaticRef<MpuRegisters>,
139 config_count: Cell<NonZeroUsize>,
142 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 pub unsafe fn clear_mpu(&self) {
160 self.registers.ctrl.write(Control::ENABLE::CLEAR);
161 }
162}
163
164pub struct CortexMConfig<const NUM_REGIONS: usize> {
170 id: NonZeroUsize,
173 regions: [CortexMRegion; NUM_REGIONS],
175 is_dirty: Cell<bool>,
178}
179
180const 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#[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 if logical_size < CORTEXM_MIN_REGION_SIZE || region_size < logical_size {
282 return None;
283 }
284
285 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 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 let mut attributes = RegionAttributes::ENABLE::SET
318 + RegionAttributes::SIZE.val(size_value)
319 + access
320 + execute;
321
322 if let Some((min_subregion, max_subregion)) = subregions {
327 let mask = (min_subregion..=max_subregion).fold(u8::MAX, |res, i| {
328 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 self.registers
388 .ctrl
389 .write(Control::ENABLE::SET + Control::HFNMIENA::CLEAR + Control::PRIVDEFENA::SET);
390 }
391
392 fn disable_app_mpu(&self) {
393 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 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 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 let mut start = unallocated_memory_start as usize;
446 let mut size = min_region_size;
447
448 if start % MIN_REGION_SIZE != 0 {
450 start += MIN_REGION_SIZE - (start % MIN_REGION_SIZE);
451 }
452
453 if size < MIN_REGION_SIZE {
455 size = MIN_REGION_SIZE;
456 }
457
458 let mut region_start = start;
460 let mut region_size = size;
461 let mut subregions = None;
462
463 if size.count_ones() > 1 || start % size != 0 {
467 let subregion_size = {
475 let tz = start.trailing_zeros();
476 if tz < 32 {
477 1_usize << tz
479 } else {
480 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 let underlying_region_size = subregion_size * 8;
492
493 let underlying_region_start = start - (start % underlying_region_size);
496
497 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 if subregion_size >= 32 && underlying_region_end >= end {
509 let min_subregion = (start - underlying_region_start) / subregion_size;
512
513 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 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 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 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 for region in config.regions.iter() {
592 if region.overlaps(unallocated_memory_start, unallocated_memory_size) {
593 return None;
594 }
595 }
596
597 let memory_size = cmp::max(
599 min_memory_size,
600 initial_app_memory_size + initial_kernel_memory_size,
601 );
602
603 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 if exponent < 9 {
610 memory_size_po2 = 512;
614 } else if exponent > 32 {
615 return None;
617 }
618
619 let mut region_size = memory_size_po2 / 2;
622
623 let mut region_start = unallocated_memory_start as usize;
626
627 if region_start % region_size != 0 {
629 region_start += region_size - (region_start % region_size);
630 }
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 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 if region_start + memory_size_po2
669 > (unallocated_memory_start as usize) + unallocated_memory_size
670 {
671 return None;
672 }
673
674 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 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 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 if app_memory_break > kernel_memory_break {
727 return Err(());
728 }
729
730 let app_memory_size = app_memory_break - region_start;
732
733 let subregion_size = region_size / 8;
735
736 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 subregions_enabled_end > kernel_memory_break {
745 return Err(());
746 }
747
748 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 !self.hardware_is_configured_for.contains(&config.id) || config.is_dirty.get() {
789 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}