From 94f42e03361464578049735cf6afd771f97d338f Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Sun, 9 May 2021 14:18:30 -0700 Subject: [PATCH 1/5] Added privileged configuration functions --- src/lib.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 87ea968..d6de710 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -34,6 +36,29 @@ fn update_mpu_unprivileged(mpu: &mut MPU, f: impl FnOnce(&mut MPU)) { asm::isb(); } +unsafe fn update_mpu_privileged(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::*; @@ -280,11 +305,54 @@ pub mod cortex_m4 { } }); } + + /// Configures the MPU to restrict access to software running in both privileged and + /// unprivileged modes + pub unsafe fn configure_privileged( + &mut self, + regions: &ArrayVec<[Region; Self::REGION_COUNT_USIZE]>, + ) { + update_mpu_privileged(&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

{ /// Starting address of the region (lowest address). /// /// This must be aligned to the region's `size`. @@ -300,7 +368,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, } @@ -393,6 +461,22 @@ pub enum AccessPermission { ReadWrite = 0b11, } +/// Data access permissions for privileged and unprivileged modes +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 From 91335fa6acd254a175c2b0160d484b6247fa3dec Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Sun, 9 May 2021 14:35:50 -0700 Subject: [PATCH 2/5] Added derives for FullAccessPermissions --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index d6de710..3c92c93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -462,6 +462,7 @@ pub enum AccessPermission { } /// 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, From bb443d7f45b398faa63d3a1f49a1f0c0b0a06d56 Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Sun, 9 May 2021 15:53:03 -0700 Subject: [PATCH 3/5] Documentation, improved names, marked functions safe --- src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3c92c93..1d203f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ fn update_mpu_unprivileged(mpu: &mut MPU, f: impl FnOnce(&mut MPU)) { asm::isb(); } -unsafe fn update_mpu_privileged(mpu: &mut MPU, f: impl FnOnce(&mut MPU)) { +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 @@ -308,11 +308,15 @@ pub mod cortex_m4 { /// Configures the MPU to restrict access to software running in both privileged and /// unprivileged modes - pub unsafe fn configure_privileged( + /// + /// 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 memory management fault + /// will occur. + pub fn configure( &mut self, regions: &ArrayVec<[Region; Self::REGION_COUNT_USIZE]>, ) { - update_mpu_privileged(&mut self.0, |mpu| { + update_mpu(&mut self.0, |mpu| { for (i, region) in regions.iter().enumerate() { unsafe { { From 2310dfe1906409e656d5a7dee920c0dacfac193d Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Sun, 9 May 2021 16:03:30 -0700 Subject: [PATCH 4/5] Documentation, M0 privileged configuration function --- src/lib.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1d203f5..98d0dac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -145,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; 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

{ /// Starting address of the region (lowest address). /// /// This must be aligned to the region's `size`. @@ -165,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, } @@ -309,8 +357,11 @@ 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 memory management fault + /// code being executed ends up not readable or not executable, a MemManage fault /// will occur. pub fn configure( &mut self, From 96b4f1606a2233ec002748ceb194ddf1b032daea Mon Sep 17 00:00:00 2001 From: Sam Crow Date: Thu, 1 Dec 2022 21:57:49 -0800 Subject: [PATCH 5/5] Upgraded cortex-m to 0.7 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c5e0c0b..3a90f19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 """ @@ -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]