aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--asm-v8.s27
-rwxr-xr-xassemble.sh9
-rw-r--r--bin/thumbv8m.base-none-eabi.abin2960 -> 4056 bytes
-rw-r--r--bin/thumbv8m.main-none-eabi.abin5370 -> 6466 bytes
-rw-r--r--bin/thumbv8m.main-none-eabihf.abin5370 -> 6466 bytes
-rw-r--r--src/asm.rs141
-rw-r--r--src/cmse.rs240
-rw-r--r--src/lib.rs2
9 files changed, 417 insertions, 3 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 5137dce..64b1162 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ links = "cortex-m" # prevent multiple versions of this crate to be linked toget
[dependencies]
bare-metal = { version = "0.2.0", features = ["const-fn"] }
volatile-register = "0.2.0"
+bitfield = "0.13.2"
[features]
cm7-r0p1 = []
diff --git a/asm-v8.s b/asm-v8.s
new file mode 100644
index 0000000..b667bf0
--- /dev/null
+++ b/asm-v8.s
@@ -0,0 +1,27 @@
+ .section .text.__tt
+ .global __tt
+ .thumb_func
+__tt:
+ tt r0, r0
+ bx lr
+
+ .section .text.__ttt
+ .global __ttt
+ .thumb_func
+__ttt:
+ ttt r0, r0
+ bx lr
+
+ .section .text.__tta
+ .global __tta
+ .thumb_func
+__tta:
+ tta r0, r0
+ bx lr
+
+ .section .text.__ttat
+ .global __ttat
+ .thumb_func
+__ttat:
+ ttat r0, r0
+ bx lr
diff --git a/assemble.sh b/assemble.sh
index 497925f..f63e837 100755
--- a/assemble.sh
+++ b/assemble.sh
@@ -23,15 +23,18 @@ ar crs bin/thumbv7em-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p
ar crs bin/thumbv7em-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p1.o
arm-none-eabi-as -march=armv8-m.base asm.s -o bin/$crate.o
-ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o
+arm-none-eabi-as -march=armv8-m.base asm-v8.s -o bin/$crate-v8.o
+ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o bin/$crate-v8.o
arm-none-eabi-as -march=armv8-m.main asm.s -o bin/$crate.o
arm-none-eabi-as -march=armv8-m.main asm-v7.s -o bin/$crate-v7.o
+arm-none-eabi-as -march=armv8-m.main asm-v8.s -o bin/$crate-v8.o
arm-none-eabi-as -march=armv8-m.main asm-v8-main.s -o bin/$crate-v8-main.o
-ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8-main.o
-ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8-main.o
+ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8.o bin/$crate-v8-main.o
+ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o bin/$crate-v7.o bin/$crate-v8.o bin/$crate-v8-main.o
rm bin/$crate.o
rm bin/$crate-v7.o
rm bin/$crate-cm7-r0p1.o
+rm bin/$crate-v8.o
rm bin/$crate-v8-main.o
diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a
index 78ae15c..44ee9af 100644
--- a/bin/thumbv8m.base-none-eabi.a
+++ b/bin/thumbv8m.base-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a
index e751232..6590c92 100644
--- a/bin/thumbv8m.main-none-eabi.a
+++ b/bin/thumbv8m.main-none-eabi.a
Binary files differ
diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a
index e751232..6590c92 100644
--- a/bin/thumbv8m.main-none-eabihf.a
+++ b/bin/thumbv8m.main-none-eabihf.a
Binary files differ
diff --git a/src/asm.rs b/src/asm.rs
index 5a35fa3..7b1a9ce 100644
--- a/src/asm.rs
+++ b/src/asm.rs
@@ -222,3 +222,144 @@ pub fn dmb() {
() => unimplemented!(),
}
}
+
+/// Test Target
+///
+/// Queries the Security state and access permissions of a memory location.
+/// Returns a Test Target Response Payload (cf section D1.2.215 of
+/// Armv8-M Architecture Reference Manual).
+#[inline]
+#[cfg(armv8m)]
+// The __tt function does not dereference the pointer received.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn tt(addr: *mut u32) -> u32 {
+ match () {
+ #[cfg(all(cortex_m, feature = "inline-asm"))]
+ () => {
+ let tt_resp: u32;
+ unsafe {
+ asm!("tt $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile");
+ }
+ tt_resp
+ }
+
+ #[cfg(all(cortex_m, not(feature = "inline-asm")))]
+ () => unsafe {
+ extern "C" {
+ fn __tt(_: *mut u32) -> u32;
+ }
+
+ __tt(addr)
+ },
+
+ #[cfg(not(cortex_m))]
+ () => unimplemented!(),
+ }
+}
+
+/// Test Target Unprivileged
+///
+/// Queries the Security state and access permissions of a memory location for an unprivileged
+/// access to that location.
+/// Returns a Test Target Response Payload (cf section D1.2.215 of
+/// Armv8-M Architecture Reference Manual).
+#[inline]
+#[cfg(armv8m)]
+// The __ttt function does not dereference the pointer received.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn ttt(addr: *mut u32) -> u32 {
+ match () {
+ #[cfg(all(cortex_m, feature = "inline-asm"))]
+ () => {
+ let tt_resp: u32;
+ unsafe {
+ asm!("ttt $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile");
+ }
+ tt_resp
+ }
+
+ #[cfg(all(cortex_m, not(feature = "inline-asm")))]
+ () => unsafe {
+ extern "C" {
+ fn __ttt(_: *mut u32) -> u32;
+ }
+
+ __ttt(addr)
+ },
+
+ #[cfg(not(cortex_m))]
+ () => unimplemented!(),
+ }
+}
+
+/// Test Target Alternate Domain
+///
+/// Queries the Security state and access permissions of a memory location for a Non-Secure access
+/// to that location. This instruction is only valid when executing in Secure state and is
+/// undefined if used from Non-Secure state.
+/// Returns a Test Target Response Payload (cf section D1.2.215 of
+/// Armv8-M Architecture Reference Manual).
+#[inline]
+#[cfg(armv8m)]
+// The __tta function does not dereference the pointer received.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn tta(addr: *mut u32) -> u32 {
+ match () {
+ #[cfg(all(cortex_m, feature = "inline-asm"))]
+ () => {
+ let tt_resp: u32;
+ unsafe {
+ asm!("tta $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile");
+ }
+ tt_resp
+ }
+
+ #[cfg(all(cortex_m, not(feature = "inline-asm")))]
+ () => unsafe {
+ extern "C" {
+ fn __tta(_: *mut u32) -> u32;
+ }
+
+ __tta(addr)
+ },
+
+ #[cfg(not(cortex_m))]
+ () => unimplemented!(),
+ }
+}
+
+/// Test Target Alternate Domain Unprivileged
+///
+/// Queries the Security state and access permissions of a memory location for a Non-Secure and
+/// unprivileged access to that location. This instruction is only valid when executing in Secure
+/// state and is undefined if used from Non-Secure state.
+/// Returns a Test Target Response Payload (cf section D1.2.215 of
+/// Armv8-M Architecture Reference Manual).
+#[inline]
+#[cfg(armv8m)]
+// The __ttat function does not dereference the pointer received.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+pub fn ttat(addr: *mut u32) -> u32 {
+ match () {
+ #[cfg(all(cortex_m, feature = "inline-asm"))]
+ () => {
+ let tt_resp: u32;
+ unsafe {
+ asm!("ttat $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile");
+ }
+ tt_resp
+ }
+
+ #[cfg(all(cortex_m, not(feature = "inline-asm")))]
+ () => unsafe {
+ extern "C" {
+ fn __ttat(_: *mut u32) -> u32;
+ }
+
+ __ttat(addr)
+ },
+
+ #[cfg(not(cortex_m))]
+ () => unimplemented!(),
+ }
+}
diff --git a/src/cmse.rs b/src/cmse.rs
new file mode 100644
index 0000000..393e463
--- /dev/null
+++ b/src/cmse.rs
@@ -0,0 +1,240 @@
+//! Cortex-M Security Extensions
+//!
+//! This module provides several helper functions to support Armv8-M and Armv8.1-M Security
+//! Extensions.
+//! Most of this implementation is directly inspired by the "Armv8-M Security Extensions:
+//! Requirements on Development Tools" document available here:
+//! https://developer.arm.com/docs/ecm0359818/latest
+//!
+//! Please note that the TT instructions support as described part 4 of the document linked above is
+//! not part of CMSE but is still present in this module. The TT instructions return the
+//! configuration of the Memory Protection Unit at an address.
+//!
+//! # Notes
+//!
+//! * Non-Secure Unprivileged code will always read zeroes from TestTarget and should not use it.
+//! * Non-Secure Privileged code can check current (AccessType::Current) and Non-Secure Unprivileged
+//! accesses (AccessType::Unprivileged).
+//! * Secure Unprivileged code can check Non-Secure Unprivileged accesses (AccessType::NonSecure).
+//! * Secure Privileged code can check all access types.
+//!
+//! # Example
+//!
+//! ```
+//! use cortex_m::cmse::{TestTarget, AccessType};
+//!
+//! // suspect_address was given by Non-Secure to a Secure function to write at it.
+//! // But is it allowed to?
+//! let suspect_address_test = TestTarget::check(0xDEADBEEF as *mut u32,
+//! AccessType::NonSecureUnprivileged);
+//! if suspect_address_test.ns_read_and_writable() {
+//! // Non-Secure can not read or write this address!
+//! }
+//! ```
+
+use crate::asm::{tt, tta, ttat, ttt};
+use bitfield::bitfield;
+
+/// Memory access behaviour: determine which privilege execution mode is used and which Memory
+/// Protection Unit (MPU) is used.
+#[allow(clippy::missing_inline_in_public_items)]
+#[derive(PartialEq, Copy, Clone, Debug)]
+pub enum AccessType {
+ /// Access using current privilege level and reading from current security state MPU.
+ /// Uses the TT instruction.
+ Current,
+ /// Unprivileged access reading from current security state MPU. Uses the TTT instruction.
+ Unprivileged,
+ /// Access using current privilege level reading from Non-Secure MPU. Uses the TTA instruction.
+ /// Undefined if used from Non-Secure state.
+ NonSecure,
+ /// Unprivilege access reading from Non-Secure MPU. Uses the TTAT instruction.
+ /// Undefined if used from Non-Secure state.
+ NonSecureUnprivileged,
+}
+
+/// Abstraction of TT instructions and helper functions to determine the security and privilege
+/// attribute of a target address, accessed in different ways.
+#[allow(clippy::missing_inline_in_public_items)]
+#[derive(PartialEq, Copy, Clone, Debug)]
+pub struct TestTarget {
+ tt_resp: TtResp,
+ access_type: AccessType,
+}
+
+bitfield! {
+ /// Test Target Response Payload
+ ///
+ /// Provides the response payload from a TT, TTA, TTT or TTAT instruction.
+ #[derive(PartialEq, Copy, Clone)]
+ struct TtResp(u32);
+ impl Debug;
+ mregion, _: 7, 0;
+ sregion, _: 15, 8;
+ mrvalid, _: 16;
+ srvalid, _: 17;
+ r, _: 18;
+ rw, _: 19;
+ nsr, _: 20;
+ nsrw, _: 21;
+ s, _: 22;
+ irvalid, _: 23;
+ iregion, _: 31, 24;
+}
+
+impl TestTarget {
+ /// Creates a Test Target Response Payload by testing addr using access_type.
+ #[inline]
+ pub fn check(addr: *mut u32, access_type: AccessType) -> Self {
+ let tt_resp = match access_type {
+ AccessType::Current => TtResp(tt(addr)),
+ AccessType::Unprivileged => TtResp(ttt(addr)),
+ AccessType::NonSecure => TtResp(tta(addr)),
+ AccessType::NonSecureUnprivileged => TtResp(ttat(addr)),
+ };
+
+ TestTarget {
+ tt_resp,
+ access_type,
+ }
+ }
+
+ /// Creates a Test Target Response Payload by testing the zone from addr to addr + size - 1
+ /// using access_type.
+ /// Returns None if:
+ /// * the address zone overlaps SAU, IDAU or MPU region boundaries
+ /// * size is 0
+ /// * addr + size - 1 overflows
+ #[inline]
+ pub fn check_range(addr: *mut u32, size: usize, access_type: AccessType) -> Option<Self> {
+ let begin: usize = addr as usize;
+ // Last address of the range (addr + size - 1). This also checks if size is 0.
+ let end: usize = begin.checked_add(size.checked_sub(1)?)?;
+
+ // Regions are aligned at 32-byte boundaries. If the address range fits in one 32-byte
+ // address line, a single TT instruction suffices. This is the case when the following
+ // constraint holds.
+ let single_check: bool = (begin % 32).checked_add(size)? <= 32usize;
+
+ let test_start = TestTarget::check(addr, access_type);
+
+ if single_check {
+ Some(test_start)
+ } else {
+ let test_end = TestTarget::check(end as *mut u32, access_type);
+ // Check that the range does not cross SAU, IDAU or MPU region boundaries.
+ if test_start != test_end {
+ None
+ } else {
+ Some(test_start)
+ }
+ }
+ }
+
+ /// Access type that was used for this test target.
+ #[inline]
+ pub fn access_type(self) -> AccessType {
+ self.access_type
+ }
+
+ /// Get the raw u32 value returned by the TT instruction used.
+ #[inline]
+ pub fn as_u32(self) -> u32 {
+ self.tt_resp.0
+ }
+
+ /// Read accessibility of the target address. Only returns the MPU settings without checking
+ /// the Security state of the target.
+ /// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
+ /// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
+ /// Returns false if the TT instruction was executed from an unprivileged mode
+ /// and the NonSecure access type was not specified.
+ /// Returns false if the address matches multiple MPU regions.
+ #[inline]
+ pub fn readable(self) -> bool {
+ self.tt_resp.r()
+ }
+
+ /// Read and write accessibility of the target address. Only returns the MPU settings without
+ /// checking the Security state of the target.
+ /// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
+ /// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
+ /// Returns false if the TT instruction was executed from an unprivileged mode
+ /// and the NonSecure access type was not specified.
+ /// Returns false if the address matches multiple MPU regions.
+ #[inline]
+ pub fn read_and_writable(self) -> bool {
+ self.tt_resp.rw()
+ }
+
+ /// Indicate the MPU region number containing the target address.
+ /// Returns None if the value is not valid:
+ /// * the MPU is not implemented or MPU_CTRL.ENABLE is set to zero
+ /// * the register argument specified by the MREGION field does not match any enabled MPU regions
+ /// * the address matched multiple MPU regions
+ /// * the address specified by the SREGION field is exempt from the secure memory attribution
+ /// * the TT instruction was executed from an unprivileged mode and the A flag was not specified.
+ #[inline]
+ pub fn mpu_region(self) -> Option<u8> {
+ if self.tt_resp.srvalid() {
+ // Cast is safe as SREGION field is defined on 8 bits.
+ Some(self.tt_resp.sregion() as u8)
+ } else {
+ None
+ }
+ }
+
+ /// Indicates the Security attribute of the target address. Independent of AccessType.
+ /// Always zero when the test target is done in the Non-Secure state.
+ #[inline]
+ pub fn secure(self) -> bool {
+ self.tt_resp.s()
+ }
+
+ /// Non-Secure Read accessibility of the target address.
+ /// Same as readable() && !secure()
+ #[inline]
+ pub fn ns_readable(self) -> bool {
+ self.tt_resp.nsr()
+ }
+
+ /// Non-Secure Read and Write accessibility of the target address.
+ /// Same as read_and_writable() && !secure()
+ #[inline]
+ pub fn ns_read_and_writable(self) -> bool {
+ self.tt_resp.nsrw()
+ }
+
+ /// Indicate the IDAU region number containing the target address. Independent of AccessType.
+ /// Returns None if the value is not valid:
+ /// * the IDAU cannot provide a region number
+ /// * the address is exempt from security attribution
+ /// * the test target is done from Non-Secure state
+ #[inline]
+ pub fn idau_region(self) -> Option<u8> {
+ if self.tt_resp.irvalid() {
+ // Cast is safe as IREGION field is defined on 8 bits.
+ Some(self.tt_resp.iregion() as u8)
+ } else {
+ None
+ }
+ }
+
+ /// Indicate the SAU region number containing the target address. Independent of AccessType.
+ /// Returns None if the value is not valid:
+ /// * SAU_CTRL.ENABLE is set to zero
+ /// * the register argument specified in the SREGION field does not match any enabled SAU regions
+ /// * the address specified matches multiple enabled SAU regions
+ /// * the address specified by the SREGION field is exempt from the secure memory attribution
+ /// * the TT instruction was executed from the Non-secure state or the Security Extension is not
+ /// implemented
+ #[inline]
+ pub fn sau_region(self) -> Option<u8> {
+ if self.tt_resp.srvalid() {
+ // Cast is safe as SREGION field is defined on 8 bits.
+ Some(self.tt_resp.sregion() as u8)
+ } else {
+ None
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index f8b5606..beaecd2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -57,6 +57,8 @@ extern crate volatile_register;
mod macros;
pub mod asm;
+#[cfg(armv8m)]
+pub mod cmse;
pub mod interrupt;
#[cfg(not(armv6m))]
pub mod itm;