use bern_units::memory_size::Byte;
use core::mem;
use cortex_m::asm;
use cortex_m::peripheral::{self, mpu, MPU};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Size {
S32 = 4,
S64 = 5,
S128 = 6,
S256 = 7,
S512 = 8,
S1K = 9,
S2K = 10,
S4K = 11,
S8K = 12,
S16K = 13,
S32K = 14,
S64K = 15,
S128K = 16,
S256K = 17,
S512K = 18,
S1M = 19,
S2M = 20,
S4M = 21,
S8M = 22,
S16M = 23,
S32M = 24,
S64M = 25,
S128M = 26,
S256M = 27,
S512M = 28,
S1G = 29,
S2G = 30,
S4G = 31,
}
impl Size {
pub const fn bits(self) -> usize {
self as usize
}
pub const fn size_bytes(self) -> usize {
2u32.pow((self as u32) + 1) as usize
}
pub unsafe fn from_bytes(mut size: usize) -> Self {
let mut log2: u8 = 0;
while size > 1 {
size = size >> 1;
log2 += 1;
}
mem::transmute(log2 - 1)
}
}
impl Into<Size> for Byte {
fn into(self) -> Size {
unsafe { Size::from_bytes(self.0 as usize) }
}
}
pub const MPU_ENABLE: u32 = 1;
pub const MPU_HARD_FAULT_ENABLED: u32 = 1 << 1;
pub const MPU_PRIVILEGED_DEFAULT_ENABLE: u32 = 1 << 2;
pub enum RegionNumber {
Ignore,
Use(u8),
}
pub const MPU_REGION_VALID: u32 = 1 << 4;
pub const MPU_REGION_ENABLE: u32 = 1;
pub enum Permission {
NoAccess,
ReadOnly,
ReadWrite,
}
impl From<crate::memory_protection::Permission> for Permission {
fn from(permission: crate::memory_protection::Permission) -> Self {
match permission {
crate::memory_protection::Permission::NoAccess => Permission::NoAccess,
crate::memory_protection::Permission::ReadOnly => Permission::ReadOnly,
crate::memory_protection::Permission::ReadWrite => Permission::ReadWrite,
}
}
}
pub enum Attributes {
StronglyOrdered,
Device {
shareable: bool,
},
Normal {
shareable: bool,
cache_policy: (CachePolicy, CachePolicy),
},
}
pub enum CachePolicy {
NoCache,
WriteThrough,
WriteBack {
wa: bool,
},
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Subregions(u8);
impl Subregions {
pub const ALL: Subregions = Subregions(0xFF);
pub const NONE: Subregions = Subregions(0);
pub const fn bits(self) -> u32 {
!self.0 as u32
}
}
impl Default for Subregions {
fn default() -> Self {
Subregions::ALL
}
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct MemoryRegion {
pub region_base_address_reg: u32,
pub region_attribute_size_reg: u32,
}
pub struct Mpu<'a>(&'a mut mpu::RegisterBlock);
impl Mpu<'_> {
#[inline]
pub unsafe fn take() -> Self {
Self(&mut *(peripheral::MPU::PTR as *mut _))
}
#[inline]
pub fn enable(&mut self) {
unsafe {
self.0
.ctrl
.write(MPU_ENABLE | MPU_PRIVILEGED_DEFAULT_ENABLE);
}
asm::dsb();
asm::isb();
}
#[inline]
pub fn disable(&mut self) {
asm::dmb();
unsafe {
self.0.ctrl.write(0);
}
}
pub const fn prepare_region_base_address(addr: u32, region: RegionNumber) -> u32 {
let base_addr = addr & !0x1F;
let (valid, region) = match region {
RegionNumber::Ignore => (0, 0),
RegionNumber::Use(region) => (MPU_REGION_VALID, region),
};
base_addr | valid | region as u32
}
#[inline]
pub fn set_region_base_address(&mut self, addr: u32, region: RegionNumber) {
let register = Self::prepare_region_base_address(addr, region);
unsafe {
self.0.rbar.write(register);
}
}
pub const fn prepare_region_attributes(
executable: bool,
access: (Permission, Permission),
attributes: Attributes,
subregions: Subregions,
region_size: Size,
) -> u32 {
let ap = match access {
(Permission::NoAccess, Permission::NoAccess) => 0b000,
(Permission::ReadWrite, Permission::NoAccess) => 0b001,
(Permission::ReadWrite, Permission::ReadOnly) => 0b010,
(Permission::ReadWrite, Permission::ReadWrite) => 0b011,
(Permission::ReadOnly, Permission::NoAccess) => 0b101,
(Permission::ReadOnly, Permission::ReadOnly) => 0b111,
(_, _) => 0b000, };
let (tex, c, b, s) = match attributes {
Attributes::StronglyOrdered => (0b000, 0, 0, 0),
Attributes::Device { shareable: true } => (0b000, 0, 1, 0),
Attributes::Device { shareable: false } => (0b010, 0, 0, 0),
Attributes::Normal {
shareable,
cache_policy,
} => match cache_policy {
(CachePolicy::WriteThrough, CachePolicy::WriteThrough) => {
(0b000, 1, 0, shareable as u32)
}
(CachePolicy::WriteBack { wa: false }, CachePolicy::WriteBack { wa: false }) => {
(0b000, 1, 1, shareable as u32)
}
(CachePolicy::NoCache, CachePolicy::NoCache) => (0b001, 0, 0, shareable as u32),
(CachePolicy::WriteBack { wa: true }, CachePolicy::WriteBack { wa: true }) => {
(0b001, 1, 1, shareable as u32)
}
(CachePolicy::NoCache, CachePolicy::WriteBack { wa: true }) => {
(0b100, 0, 1, shareable as u32)
}
(CachePolicy::NoCache, CachePolicy::WriteThrough) => {
(0b100, 1, 0, shareable as u32)
}
(CachePolicy::NoCache, CachePolicy::WriteBack { wa: false }) => {
(0b100, 1, 1, shareable as u32)
}
(CachePolicy::WriteBack { wa: true }, CachePolicy::NoCache) => {
(0b101, 0, 0, shareable as u32)
}
(CachePolicy::WriteBack { wa: true }, CachePolicy::WriteThrough) => {
(0b101, 1, 0, shareable as u32)
}
(CachePolicy::WriteBack { wa: true }, CachePolicy::WriteBack { wa: false }) => {
(0b101, 1, 1, shareable as u32)
}
(CachePolicy::WriteThrough, CachePolicy::NoCache) => {
(0b110, 0, 0, shareable as u32)
}
(CachePolicy::WriteThrough, CachePolicy::WriteBack { wa: true }) => {
(0b110, 0, 1, shareable as u32)
}
(CachePolicy::WriteThrough, CachePolicy::WriteBack { wa: false }) => {
(0b110, 1, 1, shareable as u32)
}
(CachePolicy::WriteBack { wa: false }, CachePolicy::NoCache) => {
(0b111, 0, 0, shareable as u32)
}
(CachePolicy::WriteBack { wa: false }, CachePolicy::WriteBack { wa: true }) => {
(0b111, 0, 1, shareable as u32)
}
(CachePolicy::WriteBack { wa: false }, CachePolicy::WriteThrough) => {
(0b111, 1, 0, shareable as u32)
}
},
};
let register = (!executable as u32) << 28
| ap << 24
| tex << 19
| s << 18
| c << 17
| b << 16
| (subregions.bits() as u32) << 8
| (region_size.bits() as u32) << 1
| MPU_REGION_ENABLE;
register
}
#[inline]
pub fn set_region_attributes(
&mut self,
executable: bool,
access: (Permission, Permission),
attributes: Attributes,
subregions: Subregions,
region_size: Size,
) {
let register = Self::prepare_region_attributes(
executable,
access,
attributes,
subregions,
region_size,
);
unsafe {
self.0.rasr.write(register);
}
}
pub fn set_region(&mut self, memory_region: &MemoryRegion) {
unsafe {
self.0.rbar.write(memory_region.region_base_address_reg);
self.0.rasr.write(memory_region.region_attribute_size_reg);
}
}
pub fn set_regions(&mut self, memory_region: &[MemoryRegion; 3]) {
unsafe {
self.0
.rbar_a1
.write(memory_region[0].region_base_address_reg);
self.0
.rasr_a1
.write(memory_region[0].region_attribute_size_reg);
self.0
.rbar_a2
.write(memory_region[1].region_base_address_reg);
self.0
.rasr_a2
.write(memory_region[1].region_attribute_size_reg);
self.0
.rbar_a3
.write(memory_region[2].region_base_address_reg);
self.0
.rasr_a3
.write(memory_region[2].region_attribute_size_reg);
}
}
#[inline]
pub fn disable_region(&mut self, region: u8) {
unsafe {
self.0.rnr.write(region as u32);
self.0.rasr.write(0);
}
}
}