diff --git a/.travis.yml b/.travis.yml index 64ba50bf..aed4f94c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: rust matrix: + allow_failures: + - rust: nightly include: - env: TARGET=x86_64-unknown-linux-gnu rust: stable @@ -87,6 +89,7 @@ branches: - master - staging - trying + - v0.6.x notifications: email: diff --git a/CHANGELOG.md b/CHANGELOG.md index 92b58936..274eb801 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -572,7 +572,7 @@ fn main() { - Functions to get the vector table - Wrappers over miscellaneous instructions like `bkpt` -[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.6.1...HEAD +[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/v0.6.2...HEAD [v0.6.2]: https://github.com/rust-embedded/cortex-m/compare/v0.6.1...v0.6.2 [v0.6.1]: https://github.com/rust-embedded/cortex-m/compare/v0.6.0...v0.6.1 [v0.6.0]: https://github.com/rust-embedded/cortex-m/compare/v0.5.8...v0.6.0 diff --git a/Cargo.toml b/Cargo.toml index 540b67c9..8c5b0aea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["arm", "cortex-m", "register", "peripheral"] license = "MIT OR Apache-2.0" name = "cortex-m" readme = "README.md" -repository = "https://github.com/japaric/cortex-m" +repository = "https://github.com/rust-embedded/cortex-m" version = "0.6.2" edition = "2018" links = "cortex-m" # prevent multiple versions of this crate to be linked together @@ -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/asm-cm7-r0p1.s b/asm-cm7-r0p1.s index a9ebf4a0..28c3384e 100644 --- a/asm-cm7-r0p1.s +++ b/asm-cm7-r0p1.s @@ -1,7 +1,10 @@ + .cfi_sections .debug_frame + .section .text.__basepri_max_cm7_r0p1 .global __basepri_max_cm7_r0p1 .syntax unified .thumb_func + .cfi_startproc __basepri_max_cm7_r0p1: mrs r1, PRIMASK cpsid i @@ -11,11 +14,14 @@ __basepri_max_cm7_r0p1: bxne lr cpsie i bx lr + .cfi_endproc + .size __basepri_max_cm7_r0p1, . - __basepri_max_cm7_r0p1 .section .text.__basepri_w_cm7_r0p1 .global __basepri_w_cm7_r0p1 .syntax unified .thumb_func + .cfi_startproc __basepri_w_cm7_r0p1: mrs r1, PRIMASK cpsid i @@ -25,3 +31,5 @@ __basepri_w_cm7_r0p1: bxne lr cpsie i bx lr + .cfi_endproc + .size __basepri_w_cm7_r0p1, . - __basepri_w_cm7_r0p1 diff --git a/asm-v7.s b/asm-v7.s index c4c6dfb5..fad6f4c0 100644 --- a/asm-v7.s +++ b/asm-v7.s @@ -1,27 +1,41 @@ + .cfi_sections .debug_frame + .section .text.__basepri_max .global __basepri_max .thumb_func + .cfi_startproc __basepri_max: msr BASEPRI_MAX, r0 bx lr + .cfi_endproc + .size __basepri_max, . - __basepri_max .section .text.__basepri_r .global __basepri_r .thumb_func + .cfi_startproc __basepri_r: mrs r0, BASEPRI bx lr + .cfi_endproc + .size __basepri_r, . - __basepri_r .section .text.__basepri_w .global __basepri_w .thumb_func + .cfi_startproc __basepri_w: msr BASEPRI, r0 bx lr + .cfi_endproc + .size __basepri_w, . - __basepri_w .section .text.__faultmask .global __faultmask .thumb_func + .cfi_startproc __faultmask: mrs r0, FAULTMASK bx lr + .cfi_endproc + .size __faultmask, . - __faultmask diff --git a/asm-v8-main.s b/asm-v8-main.s index a59845ce..1fad1555 100644 --- a/asm-v8-main.s +++ b/asm-v8-main.s @@ -1,28 +1,43 @@ + + .cfi_sections .debug_frame + .section .text.__msplim_r .global __msplim_r .thumb_func + .cfi_startproc __msplim_r: mrs r0, MSPLIM bx lr + .cfi_endproc + .size __msplim_r, . - __msplim_r .section .text.__msplim_w .global __msplim_w .thumb_func + .cfi_startproc __msplim_w: msr MSPLIM, r0 bx lr + .cfi_endproc + .size __msplim_w, . - __msplim_w .section .text.__psplim_r .global __psplim_r .thumb_func + .cfi_startproc __psplim_r: mrs r0, PSPLIM bx lr + .cfi_endproc + .size __psplim_r, . - __psplim_r .section .text.__psplim_w .global __psplim_w .thumb_func + .cfi_startproc __psplim_w: msr PSPLIM, r0 bx lr + .cfi_endproc + .size __psplim_w, . - __psplim_w diff --git a/asm-v8.s b/asm-v8.s new file mode 100644 index 00000000..7d3a8c94 --- /dev/null +++ b/asm-v8.s @@ -0,0 +1,42 @@ + .cfi_sections .debug_frame + + .section .text.__tt + .global __tt + .thumb_func + .cfi_startproc +__tt: + tt r0, r0 + bx lr + .cfi_endproc + .size __tt, . - __tt + + .section .text.__ttt + .global __ttt + .thumb_func + .cfi_startproc +__ttt: + ttt r0, r0 + bx lr + .cfi_endproc + .size __ttt, . - __ttt + + .section .text.__tta + .global __tta + .thumb_func + .cfi_startproc +__tta: + tta r0, r0 + bx lr + .cfi_endproc + .size __tta, . - __tta + + + .section .text.__ttat + .global __ttat + .thumb_func + .cfi_startproc +__ttat: + ttat r0, r0 + bx lr + .cfi_endproc + .size __ttat, . - __ttat diff --git a/asm.s b/asm.s index fd2c7fa8..3d1a54bb 100644 --- a/asm.s +++ b/asm.s @@ -1,77 +1,109 @@ + .cfi_sections .debug_frame + .section .text.__bkpt .global __bkpt .thumb_func + .cfi_startproc __bkpt: bkpt bx lr + .cfi_endproc + .size __bkpt, . - __bkpt .section .text.__control_r .global __control_r .thumb_func + .cfi_startproc __control_r: mrs r0, CONTROL bx lr + .cfi_endproc + .size __control_r, . - __control_r .section .text.__control_w .global __control_w .thumb_func + .cfi_startproc __control_w: msr CONTROL, r0 bx lr + .cfi_endproc + .size __control_w, . - __control_w .section .text.__cpsid .global __cpsid .thumb_func + .cfi_startproc __cpsid: cpsid i bx lr + .cfi_endproc + .size __cpsid, . - __cpsid .section .text.__cpsie .global __cpsie .thumb_func + .cfi_startproc __cpsie: cpsie i bx lr + .cfi_endproc + .size __cpsie, . - __cpsie .section .text.__delay .global __delay .syntax unified .thumb_func + .cfi_startproc __delay: 1: nop subs r0, #1 bne 1b // Branch to 1 instead of __delay does not generate R_ARM_THM_JUMP8 relocation, which breaks linking on the thumbv6m-none-eabi target bx lr + .cfi_endproc + .size __delay, . - __delay .section .text.__dmb .global __dmb .thumb_func + .cfi_startproc __dmb: dmb 0xF bx lr + .cfi_endproc + .size __dmb, . - __dmb .section .text.__dsb .global __dsb .thumb_func + .cfi_startproc __dsb: dsb 0xF bx lr + .cfi_endproc + .size __dsb, . - __dsb .section .text.__isb .global __isb .thumb_func + .cfi_startproc __isb: isb 0xF bx lr + .cfi_endproc + .size __isb, . - __isb .section .text.__msp_r .global __msp_r .thumb_func + .cfi_startproc __msp_r: mrs r0, MSP bx lr + .cfi_endproc + .size __msp_r, . - __msp_r .section .text.__msp_w .global __msp_w @@ -79,26 +111,36 @@ __msp_r: __msp_w: msr MSP, r0 bx lr + .size __msp_w, . - __msp_w .section .text.__nop .global __nop .thumb_func + .cfi_startproc __nop: bx lr + .cfi_endproc + .size __nop, . - __nop .section .text.__primask .global __primask .thumb_func + .cfi_startproc __primask: mrs r0, PRIMASK bx lr + .cfi_endproc + .size __primask, . - __primask .section .text.__psp_r .global __psp_r .thumb_func + .cfi_startproc __psp_r: mrs r0, PSP bx lr + .cfi_endproc + .size __psp_r, . - __psp_r .section .text.__psp_w .global __psp_w @@ -106,24 +148,45 @@ __psp_r: __psp_w: msr PSP, r0 bx lr + .size __psp_w, . - __psp_w .section .text.__sev .global __sev .thumb_func + .cfi_startproc __sev: sev bx lr + .cfi_endproc + .size __sev, . - __sev + + + .section .text.__udf + .global __udf + .thumb_func + .cfi_startproc +__udf: + udf + .cfi_endproc + .size __udf, . - __udf .section .text.__wfe .global __wfe .thumb_func + .cfi_startproc __wfe: wfe bx lr + .cfi_endproc + .size __wfe, . - __wfe + .section .text.__wfi .global __wfi .thumb_func + .cfi_startproc __wfi: wfi bx lr + .cfi_endproc + .size __wfi, . - __wfi diff --git a/assemble.sh b/assemble.sh index 497925f5..3e9e59e0 100755 --- a/assemble.sh +++ b/assemble.sh @@ -9,29 +9,32 @@ crate=cortex-m # remove existing blobs because otherwise this will append object files to the old blobs rm -f bin/*.a -arm-none-eabi-as -march=armv6s-m asm.s -o bin/$crate.o +arm-none-eabi-as -g -march=armv6s-m asm.s -o bin/$crate.o ar crs bin/thumbv6m-none-eabi.a bin/$crate.o -arm-none-eabi-as -march=armv7-m asm.s -o bin/$crate.o -arm-none-eabi-as -march=armv7-m asm-v7.s -o bin/$crate-v7.o +arm-none-eabi-as -g -march=armv7-m asm.s -o bin/$crate.o +arm-none-eabi-as -g -march=armv7-m asm-v7.s -o bin/$crate-v7.o ar crs bin/thumbv7m-none-eabi.a bin/$crate.o bin/$crate-v7.o -arm-none-eabi-as -march=armv7e-m asm.s -o bin/$crate.o -arm-none-eabi-as -march=armv7e-m asm-v7.s -o bin/$crate-v7.o -arm-none-eabi-as -march=armv7e-m asm-cm7-r0p1.s -o bin/$crate-cm7-r0p1.o +arm-none-eabi-as -g -march=armv7e-m asm.s -o bin/$crate.o +arm-none-eabi-as -g -march=armv7e-m asm-v7.s -o bin/$crate-v7.o +arm-none-eabi-as -g -march=armv7e-m asm-cm7-r0p1.s -o bin/$crate-cm7-r0p1.o ar crs bin/thumbv7em-none-eabi.a bin/$crate.o bin/$crate-v7.o bin/$crate-cm7-r0p1.o 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 -g -march=armv8-m.base asm.s -o bin/$crate.o +arm-none-eabi-as -g -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-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 +arm-none-eabi-as -g -march=armv8-m.main asm.s -o bin/$crate.o +arm-none-eabi-as -g -march=armv8-m.main asm-v7.s -o bin/$crate-v7.o +arm-none-eabi-as -g -march=armv8-m.main asm-v8.s -o bin/$crate-v8.o +arm-none-eabi-as -g -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.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/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a index 0684d4e8..00143683 100644 Binary files a/bin/thumbv6m-none-eabi.a and b/bin/thumbv6m-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a index cbfe5ae9..f7dc35cd 100644 Binary files a/bin/thumbv7em-none-eabi.a and b/bin/thumbv7em-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a index cbfe5ae9..f7dc35cd 100644 Binary files a/bin/thumbv7em-none-eabihf.a and b/bin/thumbv7em-none-eabihf.a differ diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a index 6e77aebe..f1f686d0 100644 Binary files a/bin/thumbv7m-none-eabi.a and b/bin/thumbv7m-none-eabi.a differ diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a index 78ae15cf..7711268d 100644 Binary files a/bin/thumbv8m.base-none-eabi.a and b/bin/thumbv8m.base-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a index e7512328..ff78de28 100644 Binary files a/bin/thumbv8m.main-none-eabi.a and b/bin/thumbv8m.main-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a index e7512328..ff78de28 100644 Binary files a/bin/thumbv8m.main-none-eabihf.a and b/bin/thumbv8m.main-none-eabihf.a differ diff --git a/src/asm.rs b/src/asm.rs index 5a35fa32..ebd61b32 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -8,7 +8,7 @@ pub fn bkpt() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("bkpt" :::: "volatile") }, + () => unsafe { llvm_asm!("bkpt" :::: "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -36,13 +36,13 @@ pub fn delay(_n: u32) { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] () => unsafe { - asm!("1: + llvm_asm!("1: nop subs $0, $$1 bne.n 1b" : "+r"(_n / 4 + 1) : - : + : "cpsr" : "volatile"); }, @@ -65,7 +65,7 @@ pub fn delay(_n: u32) { pub fn nop() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("nop" :::: "volatile") }, + () => unsafe { llvm_asm!("nop" :::: "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -81,12 +81,41 @@ pub fn nop() { } } + +/// Generate an Undefined Instruction exception. +/// +/// Can be used as a stable alternative to `core::intrinsics::abort`. +#[inline] +pub fn udf() -> ! { + match () { + #[cfg(all(cortex_m, feature = "inline-asm"))] + () => unsafe { + llvm_asm!("udf" :::: "volatile"); + core::hint::unreachable_unchecked(); + }, + + #[cfg(all(cortex_m, not(feature = "inline-asm")))] + () => unsafe { + extern "C" { + fn __udf(); + } + + __udf(); + + core::hint::unreachable_unchecked(); + }, + + #[cfg(not(cortex_m))] + () => unimplemented!(), + } +} + /// Wait For Event #[inline] pub fn wfe() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("wfe" :::: "volatile") }, + () => unsafe { llvm_asm!("wfe" :::: "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -107,7 +136,7 @@ pub fn wfe() { pub fn wfi() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("wfi" :::: "volatile") }, + () => unsafe { llvm_asm!("wfi" :::: "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -128,7 +157,7 @@ pub fn wfi() { pub fn sev() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("sev" :::: "volatile") }, + () => unsafe { llvm_asm!("sev" :::: "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -152,7 +181,7 @@ pub fn sev() { pub fn isb() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("isb 0xF" ::: "memory" : "volatile") }, + () => unsafe { llvm_asm!("isb 0xF" ::: "memory" : "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -180,7 +209,7 @@ pub fn isb() { pub fn dsb() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("dsb 0xF" ::: "memory" : "volatile") }, + () => unsafe { llvm_asm!("dsb 0xF" ::: "memory" : "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -206,7 +235,7 @@ pub fn dsb() { pub fn dmb() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { asm!("dmb 0xF" ::: "memory" : "volatile") }, + () => unsafe { llvm_asm!("dmb 0xF" ::: "memory" : "volatile") }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => unsafe { @@ -222,3 +251,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 { + llvm_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 { + llvm_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 { + llvm_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 { + llvm_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 00000000..393e4638 --- /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 { + 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 { + 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 { + 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 { + 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/interrupt.rs b/src/interrupt.rs index 2d538658..4d5ef0f2 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -10,7 +10,7 @@ pub fn disable() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] () => unsafe { - asm!("cpsid i" ::: "memory" : "volatile"); + llvm_asm!("cpsid i" ::: "memory" : "volatile"); }, #[cfg(all(cortex_m, not(feature = "inline-asm")))] @@ -37,7 +37,7 @@ pub fn disable() { pub unsafe fn enable() { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => asm!("cpsie i" ::: "memory" : "volatile"), + () => llvm_asm!("cpsie i" ::: "memory" : "volatile"), #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => { diff --git a/src/lib.rs b/src/lib.rs index 481d84ea..7b70f0cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ //! ## `inline-asm` //! //! When this feature is enabled the implementation of all the functions inside the `asm` and -//! `register` modules use inline assembly (`asm!`) instead of external assembly (FFI into separate +//! `register` modules use inline assembly (`llvm_asm!`) instead of external assembly (FFI into separate //! assembly files pre-compiled using `arm-none-eabi-gcc`). The advantages of enabling `inline-asm` //! are: //! @@ -29,7 +29,7 @@ //! This crate is guaranteed to compile on stable Rust 1.31 and up. It *might* //! compile with older versions but that may change in any new patch release. -#![cfg_attr(feature = "inline-asm", feature(asm))] +#![cfg_attr(feature = "inline-asm", feature(llvm_asm))] #![deny(missing_docs)] #![no_std] #![allow(clippy::identity_op)] @@ -58,6 +58,8 @@ extern crate volatile_register; mod macros; pub mod asm; +#[cfg(armv8m)] +pub mod cmse; pub mod interrupt; #[cfg(not(armv6m))] pub mod itm; diff --git a/src/peripheral/scb.rs b/src/peripheral/scb.rs index 9d58b038..55ecdee8 100644 --- a/src/peripheral/scb.rs +++ b/src/peripheral/scb.rs @@ -1,6 +1,8 @@ //! System Control Block use core::ptr; +#[cfg(not(any(armv6m, armv8m_base)))] +use crate::interrupt; use volatile_register::RW; @@ -305,8 +307,8 @@ impl VectActive { #[cfg(not(armv6m))] mod scb_consts { - pub const SCB_CCR_IC_MASK: u32 = (1 << 17); - pub const SCB_CCR_DC_MASK: u32 = (1 << 16); + pub const SCB_CCR_IC_MASK: u32 = 1 << 17; + pub const SCB_CCR_DC_MASK: u32 = 1 << 16; } #[cfg(not(armv6m))] @@ -839,4 +841,96 @@ impl SCB { }); } } + + /// Enable the exception + /// + /// If the exception is enabled, when the exception is triggered, the exception handler will be executed instead of the + /// HardFault handler. + /// This function is only allowed on the following exceptions: + /// * `MemoryManagement` + /// * `BusFault` + /// * `UsageFault` + /// * `SecureFault` (can only be enabled from Secure state) + /// + /// Calling this function with any other exception will do nothing. + #[inline] + #[cfg(not(any(armv6m, armv8m_base)))] + pub fn enable(&mut self, exception: Exception) { + if self.is_enabled(exception) { + return; + } + + // Make sure that the read-modify-write sequence happens during a critical section to avoid + // modifying pending and active interrupts. + interrupt::free(|_| { + let shift = match exception { + Exception::MemoryManagement => 16, + Exception::BusFault => 17, + Exception::UsageFault => 18, + #[cfg(armv8m_main)] + Exception::SecureFault => 19, + _ => return, + }; + + unsafe { self.shcsr.modify(|value| value | (1 << shift)) } + }) + } + + /// Disable the exception + /// + /// If the exception is disabled, when the exception is triggered, the HardFault handler will be executed instead of the + /// exception handler. + /// This function is only allowed on the following exceptions: + /// * `MemoryManagement` + /// * `BusFault` + /// * `UsageFault` + /// * `SecureFault` (can not be changed from Non-secure state) + /// + /// Calling this function with any other exception will do nothing. + #[inline] + #[cfg(not(any(armv6m, armv8m_base)))] + pub fn disable(&mut self, exception: Exception) { + if !self.is_enabled(exception) { + return; + } + + // Make sure that the read-modify-write sequence happens during a critical section to avoid + // modifying pending and active interrupts. + interrupt::free(|_| { + let shift = match exception { + Exception::MemoryManagement => 16, + Exception::BusFault => 17, + Exception::UsageFault => 18, + #[cfg(armv8m_main)] + Exception::SecureFault => 19, + _ => return, + }; + + unsafe { self.shcsr.modify(|value| value & !(1 << shift)) } + }) + } + + /// Check if an exception is enabled + /// + /// This function is only allowed on the following exception: + /// * `MemoryManagement` + /// * `BusFault` + /// * `UsageFault` + /// * `SecureFault` (can not be read from Non-secure state) + /// + /// Calling this function with any other exception will read `false`. + #[inline] + #[cfg(not(any(armv6m, armv8m_base)))] + pub fn is_enabled(&mut self, exception: Exception) -> bool { + let shift = match exception { + Exception::MemoryManagement => 16, + Exception::BusFault => 17, + Exception::UsageFault => 18, + #[cfg(armv8m_main)] + Exception::SecureFault => 19, + _ => return false, + }; + + (self.shcsr.read() & (1 << shift)) > 0 + } } diff --git a/src/register/apsr.rs b/src/register/apsr.rs index 97a9f01d..3db8aebe 100644 --- a/src/register/apsr.rs +++ b/src/register/apsr.rs @@ -55,7 +55,7 @@ pub fn read() -> Apsr { () => { let r: u32; unsafe { - asm!("mrs $0, APSR" : "=r"(r) ::: "volatile"); + llvm_asm!("mrs $0, APSR" : "=r"(r) ::: "volatile"); } Apsr { bits: r } } diff --git a/src/register/basepri.rs b/src/register/basepri.rs index a09e34b9..6caf9386 100644 --- a/src/register/basepri.rs +++ b/src/register/basepri.rs @@ -8,7 +8,7 @@ pub fn read() -> u8 { () => { let r: u32; unsafe { - asm!("mrs $0, BASEPRI" : "=r"(r) ::: "volatile"); + llvm_asm!("mrs $0, BASEPRI" : "=r"(r) ::: "volatile"); } r as u8 } @@ -37,10 +37,10 @@ pub unsafe fn write(_basepri: u8) { #[cfg(all(cortex_m, feature = "inline-asm"))] () => match () { #[cfg(not(feature = "cm7-r0p1"))] - () => asm!("msr BASEPRI, $0" :: "r"(_basepri) : "memory" : "volatile"), + () => llvm_asm!("msr BASEPRI, $0" :: "r"(_basepri) : "memory" : "volatile"), #[cfg(feature = "cm7-r0p1")] () => crate::interrupt::free( - |_| asm!("msr BASEPRI, $0" :: "r"(_basepri) : "memory" : "volatile"), + |_| llvm_asm!("msr BASEPRI, $0" :: "r"(_basepri) : "memory" : "volatile"), ), }, diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs index 694fd757..0e66f691 100644 --- a/src/register/basepri_max.rs +++ b/src/register/basepri_max.rs @@ -14,10 +14,10 @@ pub fn write(_basepri: u8) { () => unsafe { match () { #[cfg(not(feature = "cm7-r0p1"))] - () => asm!("msr BASEPRI_MAX, $0" :: "r"(_basepri) : "memory" : "volatile"), + () => llvm_asm!("msr BASEPRI_MAX, $0" :: "r"(_basepri) : "memory" : "volatile"), #[cfg(feature = "cm7-r0p1")] () => crate::interrupt::free( - |_| asm!("msr BASEPRI_MAX, $0" :: "r"(_basepri) : "memory" : "volatile"), + |_| llvm_asm!("msr BASEPRI_MAX, $0" :: "r"(_basepri) : "memory" : "volatile"), ), } }, diff --git a/src/register/control.rs b/src/register/control.rs index 07b26c34..211b5327 100644 --- a/src/register/control.rs +++ b/src/register/control.rs @@ -163,7 +163,7 @@ pub fn read() -> Control { #[cfg(feature = "inline-asm")] () => { let r: u32; - unsafe { asm!("mrs $0, CONTROL" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0, CONTROL" : "=r"(r) ::: "volatile") } r } @@ -194,7 +194,7 @@ pub unsafe fn write(_control: Control) { #[cfg(feature = "inline-asm")] () => { let control = _control.bits(); - asm!("msr CONTROL, $0" :: "r"(control) : "memory" : "volatile"); + llvm_asm!("msr CONTROL, $0" :: "r"(control) : "memory" : "volatile"); } #[cfg(not(feature = "inline-asm"))] diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs index 811385f6..06f60fe7 100644 --- a/src/register/faultmask.rs +++ b/src/register/faultmask.rs @@ -34,7 +34,7 @@ pub fn read() -> Faultmask { #[cfg(feature = "inline-asm")] () => { let r: u32; - unsafe { asm!("mrs $0, FAULTMASK" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0, FAULTMASK" : "=r"(r) ::: "volatile") } r } diff --git a/src/register/lr.rs b/src/register/lr.rs index a17f7ac8..6919e107 100644 --- a/src/register/lr.rs +++ b/src/register/lr.rs @@ -9,7 +9,7 @@ pub fn read() -> u32 { #[cfg(cortex_m)] () => { let r: u32; - unsafe { asm!("mov $0,R14" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mov $0,R14" : "=r"(r) ::: "volatile") } r } @@ -25,7 +25,7 @@ pub fn read() -> u32 { pub unsafe fn write(_bits: u32) { match () { #[cfg(cortex_m)] - () => asm!("mov R14,$0" :: "r"(_bits) :: "volatile"), + () => llvm_asm!("mov R14,$0" :: "r"(_bits) :: "volatile"), #[cfg(not(cortex_m))] () => unimplemented!(), diff --git a/src/register/msp.rs b/src/register/msp.rs index 082a7fc1..b5460ed0 100644 --- a/src/register/msp.rs +++ b/src/register/msp.rs @@ -7,7 +7,7 @@ pub fn read() -> u32 { #[cfg(all(cortex_m, feature = "inline-asm"))] () => { let r; - unsafe { asm!("mrs $0,MSP" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0,MSP" : "=r"(r) ::: "volatile") } r } @@ -30,7 +30,7 @@ pub fn read() -> u32 { pub unsafe fn write(_bits: u32) { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => asm!("msr MSP,$0" :: "r"(_bits) :: "volatile"), + () => llvm_asm!("msr MSP,$0" :: "r"(_bits) :: "volatile"), #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => { diff --git a/src/register/msplim.rs b/src/register/msplim.rs index df3642a7..68915c4a 100644 --- a/src/register/msplim.rs +++ b/src/register/msplim.rs @@ -7,7 +7,7 @@ pub fn read() -> u32 { #[cfg(all(cortex_m, feature = "inline-asm"))] () => { let r; - unsafe { asm!("mrs $0,MSPLIM" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0,MSPLIM" : "=r"(r) ::: "volatile") } r } @@ -30,7 +30,7 @@ pub fn read() -> u32 { pub unsafe fn write(_bits: u32) { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => asm!("msr MSPLIM,$0" :: "r"(_bits) :: "volatile"), + () => llvm_asm!("msr MSPLIM,$0" :: "r"(_bits) :: "volatile"), #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => { diff --git a/src/register/pc.rs b/src/register/pc.rs index 37176e87..f4486c49 100644 --- a/src/register/pc.rs +++ b/src/register/pc.rs @@ -9,7 +9,7 @@ pub fn read() -> u32 { #[cfg(cortex_m)] () => { let r; - unsafe { asm!("mov $0,R15" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mov $0,R15" : "=r"(r) ::: "volatile") } r } @@ -25,7 +25,7 @@ pub fn read() -> u32 { pub unsafe fn write(_bits: u32) { match () { #[cfg(cortex_m)] - () => asm!("mov R15,$0" :: "r"(_bits) :: "volatile"), + () => llvm_asm!("mov R15,$0" :: "r"(_bits) :: "volatile"), #[cfg(not(cortex_m))] () => unimplemented!(), diff --git a/src/register/primask.rs b/src/register/primask.rs index 018c45bb..4b6df3c8 100644 --- a/src/register/primask.rs +++ b/src/register/primask.rs @@ -34,7 +34,7 @@ pub fn read() -> Primask { #[cfg(feature = "inline-asm")] () => { let r: u32; - unsafe { asm!("mrs $0, PRIMASK" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0, PRIMASK" : "=r"(r) ::: "volatile") } r } diff --git a/src/register/psp.rs b/src/register/psp.rs index b6618b0c..c020e4f9 100644 --- a/src/register/psp.rs +++ b/src/register/psp.rs @@ -7,7 +7,7 @@ pub fn read() -> u32 { #[cfg(all(cortex_m, feature = "inline-asm"))] () => { let r; - unsafe { asm!("mrs $0,PSP" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0,PSP" : "=r"(r) ::: "volatile") } r } @@ -30,7 +30,7 @@ pub fn read() -> u32 { pub unsafe fn write(_bits: u32) { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => asm!("msr PSP,$0" :: "r"(_bits) :: "volatile"), + () => llvm_asm!("msr PSP,$0" :: "r"(_bits) :: "volatile"), #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => { diff --git a/src/register/psplim.rs b/src/register/psplim.rs index 6c27008f..8cb8f1c7 100644 --- a/src/register/psplim.rs +++ b/src/register/psplim.rs @@ -7,7 +7,7 @@ pub fn read() -> u32 { #[cfg(all(cortex_m, feature = "inline-asm"))] () => { let r; - unsafe { asm!("mrs $0,PSPLIM" : "=r"(r) ::: "volatile") } + unsafe { llvm_asm!("mrs $0,PSPLIM" : "=r"(r) ::: "volatile") } r } @@ -30,7 +30,7 @@ pub fn read() -> u32 { pub unsafe fn write(_bits: u32) { match () { #[cfg(all(cortex_m, feature = "inline-asm"))] - () => asm!("msr PSPLIM,$0" :: "r"(_bits) :: "volatile"), + () => llvm_asm!("msr PSPLIM,$0" :: "r"(_bits) :: "volatile"), #[cfg(all(cortex_m, not(feature = "inline-asm")))] () => {