diff --git a/Cargo.toml b/Cargo.toml index 8454e827..58014813 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ links = "cortex-m" # prevent multiple versions of this crate to be linked toget aligned = "0.3.1" bare-metal = { version = "0.2.0", features = ["const-fn"] } volatile-register = "0.2.0" +bitfield = "0.13.2" [features] const-fn = [] diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index 8854830a..22dd9cfd 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -70,7 +70,6 @@ // TODO stand-alone registers: ICTR, ACTLR and STIR - use core::marker::PhantomData; use core::ops; @@ -90,6 +89,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))] @@ -130,6 +131,9 @@ pub struct Peripherals { /// Nested Vector Interrupt Controller pub NVIC: NVIC, + /// Security Attribution Unit + pub SAU: SAU, + /// System Control Block pub SCB: SCB, @@ -191,6 +195,9 @@ impl Peripherals { NVIC: NVIC { _marker: PhantomData, }, + SAU: SAU { + _marker: PhantomData, + }, SCB: SCB { _marker: PhantomData, }, @@ -443,6 +450,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 00000000..da91aca9 --- /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, + /// Type Register + pub _type: RO, + /// Region Number Register + pub rnr: RW, + /// Region Base Address Register + pub rbar: RW, + /// Region Limit Address Register + pub rlar: RW, + /// Secure Fault Status Register + pub sfsr: RO, + /// Secure Fault Address Register + pub sfar: RO, +} + +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 { + 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, + }) + } + }) + } +}