Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cortex-mpu"
version = "0.4.0"
version = "0.5.0"
description = """
An interface for the Memory Protection Unit (MPU) in Cortex-M microcontrollers
"""
Expand All @@ -12,7 +12,7 @@ keywords = ["memory", "cortex", "arm", "mpu", "mcu"]
categories = ["embedded", "no-std"]

[dependencies]
cortex-m = "0.6.1"
cortex-m = "0.7.6"
arrayvec = { version = "0.4.11", default-features = false }

[workspace]
Expand Down
156 changes: 148 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
use cortex_m::{asm, peripheral::MPU};

pub use arrayvec::ArrayVec;
/// Enable bit in MPU_CTRL
const CTRL_ENABLE: u32 = 1 << 0;
/// Enable for hard fault and non-maskable interrupt bit in MPU_CTRL
const _CTRL_HFNMIENA: u32 = 1 << 1;
/// Default memory map for privileged mode bit in MPU_CTRL
const CTRL_PRIVDEFENA: u32 = 1 << 2;

fn update_mpu_unprivileged(mpu: &mut MPU, f: impl FnOnce(&mut MPU)) {
const CTRL_ENABLE: u32 = 1 << 0;
const _CTRL_HFNMIENA: u32 = 1 << 1;
const CTRL_PRIVDEFENA: u32 = 1 << 2;

// Atomic MPU updates:
// Turn off interrupts, turn off MPU, reconfigure, turn it back on, reenable interrupts.
// Turning off interrupts is not needed when the old configuration only applied to
Expand All @@ -34,6 +36,29 @@ fn update_mpu_unprivileged(mpu: &mut MPU, f: impl FnOnce(&mut MPU)) {
asm::isb();
}

fn update_mpu(mpu: &mut MPU, f: impl FnOnce(&mut MPU)) {
// Atomic MPU updates:
// Turn off interrupts, turn off MPU, reconfigure, turn it back on, reenable interrupts.
// https://developer.arm.com/docs/dui0553/latest/cortex-m4-peripherals/optional-memory-protection-unit/updating-an-mpu-region
asm::dsb();
cortex_m::interrupt::free(|_| {
// Disable MPU while we update the regions
unsafe {
mpu.ctrl.write(0);
}

f(mpu);

unsafe {
// Enable MPU for privileged and unprivileged code
mpu.ctrl.write(CTRL_ENABLE);
}
});

asm::dsb();
asm::isb();
}

/// The Cortex-M0+ MPU.
pub mod cortex_m0p {
use super::*;
Expand Down Expand Up @@ -120,11 +145,59 @@ pub mod cortex_m0p {
}
});
}

/// Configures the MPU to restrict access to software running in both privileged and
/// unprivileged modes.
///
/// Any violation of the MPU settings will cause a *HardFault* exception. The Cortex-M0+
/// does not have a dedicated memory management exception.
///
/// Code will only be allowed to access memory inside one of the given `regions`.
pub fn configure(
&mut self,
regions: &ArrayVec<[Region<FullAccessPermissions>; Self::REGION_COUNT_USIZE]>,
) {
update_mpu(&mut self.0, |mpu| {
for (i, region) in regions.iter().enumerate() {
unsafe {
{
let addr = (region.base_addr as u32) & !0b11111;
let valid = 1 << 4;
let region = i as u32;
mpu.rbar.write(addr | valid | region);
}

{
let xn = if region.executable { 0 } else { 1 << 28 };
let ap = (region.permissions as u32) << 24;
let scb = region.attributes.to_bits() << 16;
let srd = u32::from(region.subregions.bits()) << 8;
let size = u32::from(region.size.bits()) << 1;
let enable = 1;

mpu.rasr.write(xn | ap | scb | srd | size | enable);
}
}
}

// Disable the remaining regions
for i in regions.len()..usize::from(Self::REGION_COUNT) {
unsafe {
let addr = 0;
let valid = 1 << 4;
let region = i as u32;
mpu.rbar.write(addr | valid | region);

mpu.rasr.write(0); // disable region
}
}
});
}
}

/// Memory region properties.
#[derive(Debug, Copy, Clone)]
pub struct Region {
pub struct Region<P = AccessPermission> {
/// Starting address of the region (lowest address).
///
/// This must be aligned to the region's `size`.
Expand All @@ -140,7 +213,7 @@ pub mod cortex_m0p {
/// other MPU settings.
pub executable: bool,
/// Data access permissions for the region.
pub permissions: AccessPermission,
pub permissions: P,
/// Memory type and cache policy attributes.
pub attributes: MemoryAttributes,
}
Expand Down Expand Up @@ -280,11 +353,61 @@ pub mod cortex_m4 {
}
});
}

/// Configures the MPU to restrict access to software running in both privileged and
/// unprivileged modes
///
/// This function disables the default memory map, so any areas of the address space that
/// are not covered by regions will not be accessible.
///
/// The changes take effect immediately on the code that called this function. If the
/// code being executed ends up not readable or not executable, a MemManage fault
/// will occur.
pub fn configure(
&mut self,
regions: &ArrayVec<[Region<FullAccessPermissions>; Self::REGION_COUNT_USIZE]>,
) {
update_mpu(&mut self.0, |mpu| {
for (i, region) in regions.iter().enumerate() {
unsafe {
{
let addr = (region.base_addr as u32) & !0b11111;
let valid = 1 << 4;
let region = i as u32;
mpu.rbar.write(addr | valid | region);
}

{
let xn = if region.executable { 0 } else { 1 << 28 };
let ap = (region.permissions as u32) << 24;
let texscb = region.attributes.to_bits() << 16;
let srd = u32::from(region.subregions.bits()) << 8;
let size = u32::from(region.size.bits()) << 1;
let enable = 1;

mpu.rasr.write(xn | ap | texscb | srd | size | enable);
}
}
}

// Disable the remaining regions
for i in regions.len()..usize::from(Self::REGION_COUNT) {
unsafe {
let addr = 0;
let valid = 1 << 4;
let region = i as u32;
mpu.rbar.write(addr | valid | region);

mpu.rasr.write(0); // disable region
}
}
});
}
}

/// Memory region properties.
#[derive(Debug, Copy, Clone)]
pub struct Region {
pub struct Region<P = AccessPermission> {
/// Starting address of the region (lowest address).
///
/// This must be aligned to the region's `size`.
Expand All @@ -300,7 +423,7 @@ pub mod cortex_m4 {
/// other MPU settings.
pub executable: bool,
/// Data access permissions for the region.
pub permissions: AccessPermission,
pub permissions: P,
/// Memory type and cache policy attributes.
pub attributes: MemoryAttributes,
}
Expand Down Expand Up @@ -393,6 +516,23 @@ pub enum AccessPermission {
ReadWrite = 0b11,
}

/// Data access permissions for privileged and unprivileged modes
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FullAccessPermissions {
/// Any access generates a permission fault
PrivilegedNoAccessUnprivilegedNoAccess = 0b000,
/// Privileged access only
PrivilegedReadWriteUnprivilegedNoAccess = 0b001,
/// Any unprivileged write generates a permission fault
PrivilegedReadWriteUnprivilegedReadOnly = 0b010,
/// Full access
PrivilegedReadWriteUnprivilegedReadWrite = 0b011,
/// Privileged read-only
PrivilegedReadOnlyUnprivilegedNoAccess = 0b101,
/// Privileged or unprivileged read-only
PrivilegedReadOnlyUnprivilegedReadOnly = 0b110,
}

/// Subregion Disable (SRD) bits for the 8 subregions in a region.
///
/// Note that some cores do not support subregions for small region sizes. Check the core's User
Expand Down