diff options
author | 2020-04-15 07:45:00 +0000 | |
---|---|---|
committer | 2020-04-15 07:45:00 +0000 | |
commit | f2a56ec9022ccf88186e125234e4e21fc585b8a8 (patch) | |
tree | 738d181435dac3a7fd97149b431a50d255c5c3f8 /src | |
parent | c7ac37f3b848d2ad10b31749004c4ef40f0a2e4a (diff) | |
parent | 41d88439174234bad109d0c04d6c8a352b23497b (diff) | |
download | cortex-m-f2a56ec9022ccf88186e125234e4e21fc585b8a8.tar.gz cortex-m-f2a56ec9022ccf88186e125234e4e21fc585b8a8.tar.zst cortex-m-f2a56ec9022ccf88186e125234e4e21fc585b8a8.zip |
Merge #180
180: Add Security Attribution Unit support r=thejpster a=hug-dev
The SAU is a Armv8-M core peripheral that, alongside the Implementation
Defined Attribution Unit, manages the security attribution of the memory
zones.
This driver provides abstraction to help setting the SAU up.
This pull-request adds a new dependency, `bitfield` which provides a really nice way to describe bit fields in registers with methods to access them. It makes it really easier to use/write rather than having to manually declare the shift number and mask constants.
Although `bitfield` does not have any dependency, I am well aware of the increased cost of memory its addition could cause to embedded targets.
I am really open to criticism about its use, if you would prefer me not to use it, or if you would like to see benchmarks of memory usage for some reference targets of embedded Rust 😃
Co-authored-by: Hugues de Valon <hugues.devalon@arm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/peripheral/mod.rs | 35 | ||||
-rw-r--r-- | src/peripheral/sau.rs | 243 |
2 files changed, 277 insertions, 1 deletions
diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index c84003f..7af4b90 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -57,7 +57,6 @@ // TODO stand-alone registers: ICTR, ACTLR and STIR - use core::marker::PhantomData; use core::ops; @@ -77,6 +76,8 @@ pub mod fpu; pub mod itm; pub mod mpu; pub mod nvic; +#[cfg(armv8m)] +pub mod sau; pub mod scb; pub mod syst; #[cfg(not(armv6m))] @@ -117,6 +118,9 @@ pub struct Peripherals { /// Nested Vector Interrupt Controller pub NVIC: NVIC, + /// Security Attribution Unit + pub SAU: SAU, + /// System Control Block pub SCB: SCB, @@ -182,6 +186,9 @@ impl Peripherals { NVIC: NVIC { _marker: PhantomData, }, + SAU: SAU { + _marker: PhantomData, + }, SCB: SCB { _marker: PhantomData, }, @@ -435,6 +442,32 @@ impl ops::Deref for NVIC { } } +/// Security Attribution Unit +pub struct SAU { + _marker: PhantomData<*const ()>, +} + +unsafe impl Send for SAU {} + +#[cfg(armv8m)] +impl SAU { + /// Returns a pointer to the register block + #[inline(always)] + pub fn ptr() -> *const sau::RegisterBlock { + 0xE000_EDD0 as *const _ + } +} + +#[cfg(armv8m)] +impl ops::Deref for SAU { + type Target = self::sau::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::ptr() } + } +} + /// System Control Block pub struct SCB { _marker: PhantomData<*const ()>, diff --git a/src/peripheral/sau.rs b/src/peripheral/sau.rs new file mode 100644 index 0000000..da91aca --- /dev/null +++ b/src/peripheral/sau.rs @@ -0,0 +1,243 @@ +//! Security Attribution Unit +//! +//! *NOTE* Available only on Armv8-M and Armv8.1-M, for the following Rust target triples: +//! * `thumbv8m.base-none-eabi` +//! * `thumbv8m.main-none-eabi` +//! * `thumbv8m.main-none-eabihf` +//! +//! For reference please check the section B8.3 of the Armv8-M Architecture Reference Manual. + +use crate::interrupt; +use crate::peripheral::SAU; +use bitfield::bitfield; +use volatile_register::{RO, RW}; + +/// Register block +#[repr(C)] +pub struct RegisterBlock { + /// Control Register + pub ctrl: RW<Ctrl>, + /// Type Register + pub _type: RO<Type>, + /// Region Number Register + pub rnr: RW<Rnr>, + /// Region Base Address Register + pub rbar: RW<Rbar>, + /// Region Limit Address Register + pub rlar: RW<Rlar>, + /// Secure Fault Status Register + pub sfsr: RO<Sfsr>, + /// Secure Fault Address Register + pub sfar: RO<Sfar>, +} + +bitfield! { + /// Control Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Ctrl(u32); + get_enable, set_enable: 0; + get_allns, set_allns: 1; +} + +bitfield! { + /// Type Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Type(u32); + u8; + sregion, _: 7, 0; +} + +bitfield! { + /// Region Number Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Rnr(u32); + u8; + get_region, set_region: 7, 0; +} + +bitfield! { + /// Region Base Address Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Rbar(u32); + u32; + get_baddr, set_baddr: 31, 5; +} + +bitfield! { + /// Region Limit Address Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Rlar(u32); + u32; + get_laddr, set_laddr: 31, 5; + get_nsc, set_nsc: 1; + get_enable, set_enable: 0; +} + +bitfield! { + /// Secure Fault Status Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Sfsr(u32); + invep, _: 0; + invis, _: 1; + inver, _: 2; + auviol, _: 3; + invtran, _: 4; + lsperr, _: 5; + sfarvalid, _: 6; + lserr, _: 7; +} + +bitfield! { + /// Secure Fault Address Register description + #[repr(C)] + #[derive(Copy, Clone)] + pub struct Sfar(u32); + u32; + address, _: 31, 0; +} + +/// Possible attribute of a SAU region. +#[derive(Debug)] +pub enum SauRegionAttribute { + /// SAU region is Secure + Secure, + /// SAU region is Non-Secure Callable + NonSecureCallable, + /// SAU region is Non-Secure + NonSecure, +} + +/// Description of a SAU region. +#[derive(Debug)] +pub struct SauRegion { + /// First address of the region, its 5 least significant bits must be set to zero. + pub base_address: u32, + /// Last address of the region, its 5 least significant bits must be set to one. + pub limit_address: u32, + /// Attribute of the region. + pub attribute: SauRegionAttribute, +} + +/// Possible error values returned by the SAU methods. +#[derive(Debug)] +pub enum SauError { + /// The region number parameter to set or get a region must be between 0 and + /// region_numbers() - 1. + RegionNumberTooBig, + /// Bits 0 to 4 of the base address of a SAU region must be set to zero. + WrongBaseAddress, + /// Bits 0 to 4 of the limit address of a SAU region must be set to one. + WrongLimitAddress, +} + +impl SAU { + /// Get the number of implemented SAU regions. + #[inline] + pub fn region_numbers(&self) -> u8 { + self._type.read().sregion() + } + + /// Enable the SAU. + #[inline] + pub fn enable(&mut self) { + unsafe { + self.ctrl.modify(|mut ctrl| { + ctrl.set_enable(true); + ctrl + }); + } + } + + /// Set a SAU region to a region number. + /// SAU regions must be 32 bytes aligned and their sizes must be a multiple of 32 bytes. It + /// means that the 5 least significant bits of the base address of a SAU region must be set to + /// zero and the 5 least significant bits of the limit address must be set to one. + /// The region number must be valid. + /// This function is executed under a critical section to prevent having inconsistent results. + #[inline] + pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> { + interrupt::free(|_| { + let base_address = region.base_address; + let limit_address = region.limit_address; + let attribute = region.attribute; + + if region_number >= self.region_numbers() { + Err(SauError::RegionNumberTooBig) + } else if base_address & 0x1F != 0 { + Err(SauError::WrongBaseAddress) + } else if limit_address & 0x1F != 0x1F { + Err(SauError::WrongLimitAddress) + } else { + // All fields of these registers are going to be modified so we don't need to read them + // before. + let mut rnr = Rnr(0); + let mut rbar = Rbar(0); + let mut rlar = Rlar(0); + + rnr.set_region(region_number); + rbar.set_baddr(base_address >> 5); + rlar.set_laddr(limit_address >> 5); + + match attribute { + SauRegionAttribute::Secure => { + rlar.set_nsc(false); + rlar.set_enable(false); + } + SauRegionAttribute::NonSecureCallable => { + rlar.set_nsc(true); + rlar.set_enable(true); + } + SauRegionAttribute::NonSecure => { + rlar.set_nsc(false); + rlar.set_enable(true); + } + } + + unsafe { + self.rnr.write(rnr); + self.rbar.write(rbar); + self.rlar.write(rlar); + } + + Ok(()) + } + }) + } + + /// Get a region from the SAU. + /// The region number must be valid. + /// This function is executed under a critical section to prevent having inconsistent results. + #[inline] + pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> { + interrupt::free(|_| { + if region_number >= self.region_numbers() { + Err(SauError::RegionNumberTooBig) + } else { + unsafe { + self.rnr.write(Rnr(region_number.into())); + } + + let rbar = self.rbar.read(); + let rlar = self.rlar.read(); + + let attribute = match (rlar.get_enable(), rlar.get_nsc()) { + (false, _) => SauRegionAttribute::Secure, + (true, false) => SauRegionAttribute::NonSecure, + (true, true) => SauRegionAttribute::NonSecureCallable, + }; + + Ok(SauRegion { + base_address: rbar.get_baddr() << 5, + limit_address: (rlar.get_laddr() << 5) | 0x1F, + attribute, + }) + } + }) + } +} |