/src/mpu.rs
#![allow(dead_code)]
use core::cell::RefCell;
use cortex_m::interrupt::{self, Mutex};
use cortex_m::peripheral::MPU;
#[derive(Copy, Clone, Debug)]
pub enum MemoryPermission {
None,
RO,
RW,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct MemoryRange {
pub start: u32,
pub length: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct MemoryRegion {
pub start: u32,
pub length: u32,
pub permissions: MemoryPermission,
pub xn: bool,
}
static MPU_MANAGER: Mutex<RefCell<Option<MpuManager>>> = Mutex::new(RefCell::new(None));
pub fn init(mpu: MPU) {
interrupt::free(|cs| MPU_MANAGER.borrow(cs).replace(Some(MpuManager::new(mpu))));
}
pub fn add_region(region: &MemoryRegion) {
interrupt::free(|cs| {
let mut rm = MPU_MANAGER.borrow(cs).borrow_mut();
let manager = rm.as_mut().unwrap();
unsafe { manager.add_region(®ion) };
});
}
pub fn add_regions(regions: &[MemoryRegion]) {
interrupt::free(|cs| {
let mut rm = MPU_MANAGER.borrow(cs).borrow_mut();
let manager = rm.as_mut().unwrap();
for region in regions {
unsafe { manager.add_region(®ion) };
}
});
}
pub fn enable() {
interrupt::free(|cs| {
let mut rm = MPU_MANAGER.borrow(cs).borrow_mut();
let manager = rm.as_mut().unwrap();
manager.set_mpu_enable(true);
});
}
pub fn disable() {
interrupt::free(|cs| {
let mut rm = MPU_MANAGER.borrow(cs).borrow_mut();
let manager = rm.as_mut().unwrap();
manager.set_mpu_enable(false);
});
}
struct MpuManager {
mpu: MPU,
n_mpu_regions: u32,
}
impl MpuManager {
pub fn new(mpu: MPU) -> MpuManager {
// Enable default memory map for privileged software
unsafe { mpu.ctrl.write(0x4) };
MpuManager {
mpu,
n_mpu_regions: 0,
}
}
unsafe fn write_mpu_region(&mut self, start: u32, length: u32, region: &MemoryRegion) {
if self.n_mpu_regions == 8 {
panic!("Out of MPU regions");
}
// We don't check the start value here. Let the fault handler handle it.
self.mpu.rbar.write(start | 0b10000 | self.n_mpu_regions);
self.mpu.rasr.write(
if region.xn { 1 << 28 } else { 0 } | // XN
match region.permissions {
MemoryPermission::None => 0b001,
MemoryPermission::RO => 0b010,
MemoryPermission::RW => 0b011,
} << 24 | // AP
0b001000 << 16 | // TEX S C B
(length.trailing_zeros() - 1) << 1 | // SIZE
1 // ENABLE
);
self.n_mpu_regions += 1;
}
unsafe fn map_mpu_region(&mut self, start: u32, length: u32, region: &MemoryRegion) {
if length.count_ones() > 1 {
// The region is not a power of two. Create a MPU region for
// the next lowest power of two and then iterate on the
// remainder.
let nl_length = length.next_power_of_two() >> 1;
self.write_mpu_region(start, nl_length, region);
self.map_mpu_region(start + nl_length, length - nl_length, region);
} else {
self.write_mpu_region(start, length, region);
}
}
pub unsafe fn add_region(&mut self, region: &MemoryRegion) {
if region.length == 0 {
return;
}
self.map_mpu_region(region.start, region.length, region);
}
pub fn set_mpu_enable(&mut self, enable: bool) {
unsafe {
self.mpu.ctrl.modify(|v| (v & 0xFFFFFFFE) | (enable as u32))
};
}
}