diff options
author | 2022-02-21 19:57:18 +0000 | |
---|---|---|
committer | 2022-02-24 01:44:11 +0000 | |
commit | 894f2aabdbd65f85eecf25debc2326f0387863c7 (patch) | |
tree | f08ad0ca10df764c5b29549421e874c4c3512bec | |
parent | 9e8dd294b04510d727d50039a7f84292789aed0e (diff) | |
download | cortex-m-894f2aabdbd65f85eecf25debc2326f0387863c7.tar.gz cortex-m-894f2aabdbd65f85eecf25debc2326f0387863c7.tar.zst cortex-m-894f2aabdbd65f85eecf25debc2326f0387863c7.zip |
Remove outlined asm, replace with stable inline asm.
54 files changed, 301 insertions, 1005 deletions
diff --git a/.github/bors.toml b/.github/bors.toml index 4402e95..c12731a 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -3,7 +3,7 @@ delete_merged_branches = true required_approvals = 1 status = [ "ci-linux (stable)", - "ci-linux (1.42.0)", + "ci-linux (1.59.0)", "rt-ci-linux (stable)", "rt-ci-linux (1.42.0)", "rt-ci-other-os (macOS-latest)", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8caebd0..701e46a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: include: # Test MSRV - - rust: 1.42.0 + - rust: 1.59.0 # Test nightly but don't fail - rust: nightly diff --git a/CHANGELOG.md b/CHANGELOG.md index 421dce7..2349607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,8 +21,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fixed `singleton!()` statics sometimes ending up in `.data` instead of `.bss` (#364, #380). +### Changed +- Inline assembly is now always used, requiring Rust 1.59. + ### Removed - removed all peripherals `ptr()` functions in favor of the associated constant `PTR` (#385). +- removed `inline-asm` feature which is now always enabled ## [v0.7.4] - 2021-12-31 @@ -29,7 +29,6 @@ optional = true [features] cm7 = [] cm7-r0p1 = ["cm7"] -inline-asm = [] linker-plugin-lto = [] std = [] @@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team]. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.42 and up. It might compile with older versions but that may change in any new patch release. +This crate is guaranteed to compile on stable Rust 1.59 and up. It might compile with older versions but that may change in any new patch release. ## License diff --git a/asm-toolchain b/asm-toolchain deleted file mode 100644 index cc5dbb2..0000000 --- a/asm-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2021-12-16 diff --git a/asm/inline.rs b/asm/inline.rs deleted file mode 100644 index bbc04d2..0000000 --- a/asm/inline.rs +++ /dev/null @@ -1,448 +0,0 @@ -//! Inline assembly implementing the routines exposed in `cortex_m::asm`. -//! -//! If the `inline-asm` feature is enabled, these functions will be directly called by the -//! `cortex-m` wrappers. Otherwise, `cortex-m` links against them via prebuilt archives. -//! -//! All of these functions should be blanket-`unsafe`. `cortex-m` provides safe wrappers where -//! applicable. - -use core::arch::asm; -use core::sync::atomic::{compiler_fence, Ordering}; - -#[inline(always)] -pub unsafe fn __bkpt() { - asm!("bkpt", options(nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __control_r() -> u32 { - let r; - asm!("mrs {}, CONTROL", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -#[inline(always)] -pub unsafe fn __control_w(w: u32) { - // ISB is required after writing to CONTROL, - // per ARM architectural requirements (see Application Note 321). - asm!( - "msr CONTROL, {}", - "isb", - in(reg) w, - options(nomem, nostack, preserves_flags), - ); - - // Ensure memory accesses are not reordered around the CONTROL update. - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __cpsid() { - asm!("cpsid i", options(nomem, nostack, preserves_flags)); - - // Ensure no subsequent memory accesses are reordered to before interrupts are disabled. - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __cpsie() { - // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. - compiler_fence(Ordering::SeqCst); - - asm!("cpsie i", options(nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __delay(cyc: u32) { - // The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores - // (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying - // for more cycles is okay. - // Add 1 to prevent an integer underflow which would cause a long freeze - let real_cyc = 1 + cyc / 2; - asm!( - // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m. - "1:", - "subs {}, #1", - "bne 1b", - inout(reg) real_cyc => _, - options(nomem, nostack), - ); -} - -#[inline(always)] -pub unsafe fn __dmb() { - compiler_fence(Ordering::SeqCst); - asm!("dmb", options(nomem, nostack, preserves_flags)); - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __dsb() { - compiler_fence(Ordering::SeqCst); - asm!("dsb", options(nomem, nostack, preserves_flags)); - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __isb() { - compiler_fence(Ordering::SeqCst); - asm!("isb", options(nomem, nostack, preserves_flags)); - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __msp_r() -> u32 { - let r; - asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -#[inline(always)] -pub unsafe fn __msp_w(val: u32) { - // Technically is writing to the stack pointer "not pushing any data to the stack"? - // In any event, if we don't set `nostack` here, this method is useless as the new - // stack value is immediately mutated by returning. Really this is just not a good - // method and its higher-level use is marked as deprecated in cortex-m. - asm!("msr MSP, {}", in(reg) val, options(nomem, nostack, preserves_flags)); -} - -// NOTE: No FFI shim, this requires inline asm. -#[inline(always)] -pub unsafe fn __apsr_r() -> u32 { - let r; - asm!("mrs {}, APSR", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -#[inline(always)] -pub unsafe fn __nop() { - // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate - // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N - // nops when they call `nop` N times, let's not add that option. - asm!("nop", options(nomem, nostack, preserves_flags)); -} - -// NOTE: No FFI shim, this requires inline asm. -#[inline(always)] -pub unsafe fn __pc_r() -> u32 { - let r; - asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -// NOTE: No FFI shim, this requires inline asm. -#[inline(always)] -pub unsafe fn __pc_w(val: u32) { - asm!("mov pc, {}", in(reg) val, options(nomem, nostack, preserves_flags)); -} - -// NOTE: No FFI shim, this requires inline asm. -#[inline(always)] -pub unsafe fn __lr_r() -> u32 { - let r; - asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -// NOTE: No FFI shim, this requires inline asm. -#[inline(always)] -pub unsafe fn __lr_w(val: u32) { - asm!("mov lr, {}", in(reg) val, options(nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __primask_r() -> u32 { - let r; - asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -#[inline(always)] -pub unsafe fn __psp_r() -> u32 { - let r; - asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags)); - r -} - -#[inline(always)] -pub unsafe fn __psp_w(val: u32) { - // See comment on __msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP - // if MSP is currently being used as the stack pointer. - asm!("msr PSP, {}", in(reg) val, options(nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __sev() { - asm!("sev", options(nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __udf() -> ! { - asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __wfe() { - asm!("wfe", options(nomem, nostack, preserves_flags)); -} - -#[inline(always)] -pub unsafe fn __wfi() { - asm!("wfi", options(nomem, nostack, preserves_flags)); -} - -/// Semihosting syscall. -#[inline(always)] -pub unsafe fn __sh_syscall(mut nr: u32, arg: u32) -> u32 { - asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags)); - nr -} - -/// Set CONTROL.SPSEL to 0, write `msp` to MSP, branch to `rv`. -#[inline(always)] -pub unsafe fn __bootstrap(msp: u32, rv: u32) -> ! { - asm!( - "mrs {tmp}, CONTROL", - "bics {tmp}, {spsel}", - "msr CONTROL, {tmp}", - "isb", - "msr MSP, {msp}", - "bx {rv}", - // `out(reg) _` is not permitted in a `noreturn` asm! call, - // so instead use `in(reg) 0` and don't restore it afterwards. - tmp = in(reg) 0, - spsel = in(reg) 2, - msp = in(reg) msp, - rv = in(reg) rv, - options(noreturn, nomem, nostack), - ); -} - -// v7m *AND* v8m.main, but *NOT* v8m.base -#[cfg(any(armv7m, armv8m_main))] -pub use self::v7m::*; -#[cfg(any(armv7m, armv8m_main))] -mod v7m { - use core::arch::asm; - use core::sync::atomic::{compiler_fence, Ordering}; - - #[inline(always)] - pub unsafe fn __basepri_max(val: u8) { - asm!("msr BASEPRI_MAX, {}", in(reg) val, options(nomem, nostack, preserves_flags)); - } - - #[inline(always)] - pub unsafe fn __basepri_r() -> u8 { - let r; - asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags)); - r - } - - #[inline(always)] - pub unsafe fn __basepri_w(val: u8) { - asm!("msr BASEPRI, {}", in(reg) val, options(nomem, nostack, preserves_flags)); - } - - #[inline(always)] - pub unsafe fn __faultmask_r() -> u32 { - let r; - asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags)); - r - } - - #[inline(always)] - pub unsafe fn __enable_icache() { - asm!( - "ldr {0}, =0xE000ED14", // CCR - "mrs {2}, PRIMASK", // save critical nesting info - "cpsid i", // mask interrupts - "ldr {1}, [{0}]", // read CCR - "orr.w {1}, {1}, #(1 << 17)", // Set bit 17, IC - "str {1}, [{0}]", // write it back - "dsb", // ensure store completes - "isb", // synchronize pipeline - "msr PRIMASK, {2}", // unnest critical section - out(reg) _, - out(reg) _, - out(reg) _, - options(nostack), - ); - compiler_fence(Ordering::SeqCst); - } - - #[inline(always)] - pub unsafe fn __enable_dcache() { - asm!( - "ldr {0}, =0xE000ED14", // CCR - "mrs {2}, PRIMASK", // save critical nesting info - "cpsid i", // mask interrupts - "ldr {1}, [{0}]", // read CCR - "orr.w {1}, {1}, #(1 << 16)", // Set bit 16, DC - "str {1}, [{0}]", // write it back - "dsb", // ensure store completes - "isb", // synchronize pipeline - "msr PRIMASK, {2}", // unnest critical section - out(reg) _, - out(reg) _, - out(reg) _, - options(nostack), - ); - compiler_fence(Ordering::SeqCst); - } -} - -#[cfg(armv7em)] -pub use self::v7em::*; -#[cfg(armv7em)] -mod v7em { - use core::arch::asm; - - #[inline(always)] - pub unsafe fn __basepri_max_cm7_r0p1(val: u8) { - asm!( - "mrs {1}, PRIMASK", - "cpsid i", - "tst.w {1}, #1", - "msr BASEPRI_MAX, {0}", - "it ne", - "bxne lr", - "cpsie i", - in(reg) val, - out(reg) _, - options(nomem, nostack, preserves_flags), - ); - } - - #[inline(always)] - pub unsafe fn __basepri_w_cm7_r0p1(val: u8) { - asm!( - "mrs {1}, PRIMASK", - "cpsid i", - "tst.w {1}, #1", - "msr BASEPRI, {0}", - "it ne", - "bxne lr", - "cpsie i", - in(reg) val, - out(reg) _, - options(nomem, nostack, preserves_flags), - ); - } -} - -#[cfg(armv8m)] -pub use self::v8m::*; -/// Baseline and Mainline. -#[cfg(armv8m)] -mod v8m { - use core::arch::asm; - - #[inline(always)] - pub unsafe fn __tt(mut target: u32) -> u32 { - asm!( - "tt {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ); - target - } - - #[inline(always)] - pub unsafe fn __ttt(mut target: u32) -> u32 { - asm!( - "ttt {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ); - target - } - - #[inline(always)] - pub unsafe fn __tta(mut target: u32) -> u32 { - asm!( - "tta {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ); - target - } - - #[inline(always)] - pub unsafe fn __ttat(mut target: u32) -> u32 { - asm!( - "ttat {target}, {target}", - target = inout(reg) target, - options(nomem, nostack, preserves_flags), - ); - target - } - - #[inline(always)] - pub unsafe fn __msp_ns_r() -> u32 { - let r; - asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags)); - r - } - - #[inline(always)] - pub unsafe fn __msp_ns_w(val: u32) { - asm!("msr MSP_NS, {}", in(reg) val, options(nomem, nostack, preserves_flags)); - } - - #[inline(always)] - pub unsafe fn __bxns(val: u32) { - asm!("BXNS {}", in(reg) val, options(nomem, nostack, preserves_flags)); - } -} - -#[cfg(armv8m_main)] -pub use self::v8m_main::*; -/// Mainline only. -#[cfg(armv8m_main)] -mod v8m_main { - use core::arch::asm; - - #[inline(always)] - pub unsafe fn __msplim_r() -> u32 { - let r; - asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)); - r - } - - #[inline(always)] - pub unsafe fn __msplim_w(val: u32) { - asm!("msr MSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags)); - } - - #[inline(always)] - pub unsafe fn __psplim_r() -> u32 { - let r; - asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)); - r - } - - #[inline(always)] - pub unsafe fn __psplim_w(val: u32) { - asm!("msr PSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags)); - } -} - -#[cfg(has_fpu)] -pub use self::fpu::*; -/// All targets with FPU. -#[cfg(has_fpu)] -mod fpu { - use core::arch::asm; - - #[inline(always)] - pub unsafe fn __fpscr_r() -> u32 { - let r; - asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags)); - r - } - - #[inline(always)] - pub unsafe fn __fpscr_w(val: u32) { - asm!("vmsr fpscr, {}", in(reg) val, options(nomem, nostack)); - } -} diff --git a/asm/lib.rs b/asm/lib.rs deleted file mode 100644 index 48f3dc2..0000000 --- a/asm/lib.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! FFI shim around the inline assembly in `inline.rs`. -//! -//! We use this file to precompile some assembly stubs into the static libraries you can find in -//! `bin`. Apps using the `cortex-m` crate then link against those static libraries and don't need -//! to build this file themselves. -//! -//! Nowadays the assembly stubs are no longer actual assembly files, but actually just this small -//! Rust crate that uses unstable inline assembly, coupled with the `xtask` tool to invoke rustc -//! and build the files. -//! -//! Precompiling this to a static lib allows users to call assembly routines from stable Rust, but -//! also perform [linker plugin LTO] with the precompiled artifacts to completely inline the -//! assembly routines into their code, which brings the "outline assembly" on par with "real" inline -//! assembly. -//! -//! For developers and contributors to `cortex-m`, this setup means that they don't have to install -//! any binutils, assembler, or C compiler to hack on the crate. All they need is to run `cargo -//! xtask assemble` to rebuild the archives from this file. -//! -//! Cool, right? -//! -//! # Rust version management -//! -//! Since inline assembly is still unstable, and we want to ensure that the created blobs are -//! up-to-date in CI, we have to pin the nightly version we use for this. The nightly toolchain is -//! stored in `asm-toolchain`. -//! -//! The `cargo xtask` automation will automatically install the `asm-toolchain` as well as all -//! Cortex-M targets needed to generate the blobs. -//! -//! [linker plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html - -#![feature(asm)] -#![no_std] -#![crate_type = "staticlib"] -#![deny(warnings)] -// Don't warn about feature(asm) being stable on Rust >= 1.59.0 -#![allow(stable_features)] - -mod inline; - -macro_rules! shims { - ( - $( fn $name:ident( $($arg:ident: $argty:ty),* ) $(-> $ret:ty)?; )+ - ) => { - $( - #[no_mangle] - pub unsafe extern "C" fn $name( - $($arg: $argty),* - ) $(-> $ret)? { - crate::inline::$name($($arg),*) - } - )+ - }; -} - -shims! { - fn __bkpt(); - fn __control_r() -> u32; - fn __control_w(w: u32); - fn __cpsid(); - fn __cpsie(); - fn __delay(cyc: u32); - fn __dmb(); - fn __dsb(); - fn __isb(); - fn __msp_r() -> u32; - fn __msp_w(val: u32); - fn __nop(); - fn __primask_r() -> u32; - fn __psp_r() -> u32; - fn __psp_w(val: u32); - fn __sev(); - fn __udf() -> !; - fn __wfe(); - fn __wfi(); - fn __sh_syscall(nr: u32, arg: u32) -> u32; - fn __bootstrap(msp: u32, rv: u32) -> !; -} - -// v7m *AND* v8m.main, but *NOT* v8m.base -#[cfg(any(armv7m, armv8m_main))] -shims! { - fn __basepri_max(val: u8); - fn __basepri_r() -> u8; - fn __basepri_w(val: u8); - fn __faultmask_r() -> u32; - fn __enable_icache(); - fn __enable_dcache(); -} - -#[cfg(armv7em)] -shims! { - fn __basepri_max_cm7_r0p1(val: u8); - fn __basepri_w_cm7_r0p1(val: u8); -} - -// Baseline and Mainline. -#[cfg(armv8m)] -shims! { - fn __tt(target: u32) -> u32; - fn __ttt(target: u32) -> u32; - fn __tta(target: u32) -> u32; - fn __ttat(target: u32) -> u32; - fn __msp_ns_r() -> u32; - fn __msp_ns_w(val: u32); - fn __bxns(val: u32); -} - -// Mainline only. -#[cfg(armv8m_main)] -shims! { - fn __msplim_r() -> u32; - fn __msplim_w(val: u32); - fn __psplim_r() -> u32; - fn __psplim_w(val: u32); -} - -// All targets with FPU. -#[cfg(has_fpu)] -shims! { - fn __fpscr_r() -> u32; - fn __fpscr_w(val: u32); -} - -/// We *must* define a panic handler here, even though nothing here should ever be able to panic. -/// -/// We prove that nothing will ever panic by calling a function that doesn't exist. If the panic -/// handler gets linked in, this causes a linker error. We always build this file with optimizations -/// enabled, but even without them the panic handler should never be linked in. -#[panic_handler] -#[link_section = ".text.asm_panic_handler"] -fn panic(_: &core::panic::PanicInfo) -> ! { - extern "C" { - #[link_name = "cortex-m internal error: panic handler not optimized out, please file an \ - issue at https://github.com/rust-embedded/cortex-m"] - fn __cortex_m_should_not_panic() -> !; - } - - unsafe { - __cortex_m_should_not_panic(); - } -} diff --git a/bin/thumbv6m-none-eabi-lto.a b/bin/thumbv6m-none-eabi-lto.a Binary files differdeleted file mode 100644 index a203d7a..0000000 --- a/bin/thumbv6m-none-eabi-lto.a +++ /dev/null diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a Binary files differdeleted file mode 100644 index 9640a69..0000000 --- a/bin/thumbv6m-none-eabi.a +++ /dev/null diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a Binary files differdeleted file mode 100644 index b34ac64..0000000 --- a/bin/thumbv7em-none-eabi-lto.a +++ /dev/null diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a Binary files differdeleted file mode 100644 index 88acbdd..0000000 --- a/bin/thumbv7em-none-eabi.a +++ /dev/null diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a Binary files differdeleted file mode 100644 index 6de94bb..0000000 --- a/bin/thumbv7em-none-eabihf-lto.a +++ /dev/null diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a Binary files differdeleted file mode 100644 index cf91a7a..0000000 --- a/bin/thumbv7em-none-eabihf.a +++ /dev/null diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a Binary files differdeleted file mode 100644 index 7f677a9..0000000 --- a/bin/thumbv7m-none-eabi-lto.a +++ /dev/null diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a Binary files differdeleted file mode 100644 index ff4bf21..0000000 --- a/bin/thumbv7m-none-eabi.a +++ /dev/null diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a Binary files differdeleted file mode 100644 index f62acaf..0000000 --- a/bin/thumbv8m.base-none-eabi-lto.a +++ /dev/null diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a Binary files differdeleted file mode 100644 index c0cc96c..0000000 --- a/bin/thumbv8m.base-none-eabi.a +++ /dev/null diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a Binary files differdeleted file mode 100644 index 1a51515..0000000 --- a/bin/thumbv8m.main-none-eabi-lto.a +++ /dev/null diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a Binary files differdeleted file mode 100644 index d017a15..0000000 --- a/bin/thumbv8m.main-none-eabi.a +++ /dev/null diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a Binary files differdeleted file mode 100644 index fd3dc92..0000000 --- a/bin/thumbv8m.main-none-eabihf-lto.a +++ /dev/null diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a Binary files differdeleted file mode 100644 index 223ff1d..0000000 --- a/bin/thumbv8m.main-none-eabihf.a +++ /dev/null @@ -1,33 +1,13 @@ -use std::path::PathBuf; -use std::{env, fs}; +use std::env; fn main() { let target = env::var("TARGET").unwrap(); let host_triple = env::var("HOST").unwrap(); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let name = env::var("CARGO_PKG_NAME").unwrap(); if host_triple == target { println!("cargo:rustc-cfg=native"); } - if target.starts_with("thumb") { - let suffix = if env::var_os("CARGO_FEATURE_LINKER_PLUGIN_LTO").is_some() { - "-lto" - } else { - "" - }; - - fs::copy( - format!("bin/{}{}.a", target, suffix), - out_dir.join(format!("lib{}.a", name)), - ) - .unwrap(); - - println!("cargo:rustc-link-lib=static={}", name); - println!("cargo:rustc-link-search={}", out_dir.display()); - } - if target.starts_with("thumbv6m-") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv6m"); @@ -37,7 +17,7 @@ fn main() { } else if target.starts_with("thumbv7em-") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) + println!("cargo:rustc-cfg=armv7em"); } else if target.starts_with("thumbv8m.base") { println!("cargo:rustc-cfg=cortex_m"); println!("cargo:rustc-cfg=armv8m"); diff --git a/cortex-m-semihosting/CHANGELOG.md b/cortex-m-semihosting/CHANGELOG.md index 0a942cf..38ee835 100644 --- a/cortex-m-semihosting/CHANGELOG.md +++ b/cortex-m-semihosting/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Always use inline-asm, requiring Rust 1.59. + ## [v0.4.1] - 2020-10-20 0.4.1 was yanked because the pre-built binaries contain conflicting symbols diff --git a/cortex-m-semihosting/README.md b/cortex-m-semihosting/README.md index bfbfb44..6036d4e 100644 --- a/cortex-m-semihosting/README.md +++ b/cortex-m-semihosting/README.md @@ -11,7 +11,7 @@ This project is developed and maintained by the [Cortex-M team][team]. # Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.33.0 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59.0 and up. It *might* compile with older versions but that may change in any new patch release. ## License diff --git a/cortex-m-semihosting/bin b/cortex-m-semihosting/bin deleted file mode 120000 index 19f285a..0000000 --- a/cortex-m-semihosting/bin +++ /dev/null @@ -1 +0,0 @@ -../bin
\ No newline at end of file diff --git a/cortex-m-semihosting/build.rs b/cortex-m-semihosting/build.rs index 315035e..ed0d069 100644 --- a/cortex-m-semihosting/build.rs +++ b/cortex-m-semihosting/build.rs @@ -1,23 +1,9 @@ -use std::path::PathBuf; -use std::{env, fs}; +use std::env; fn main() { let target = env::var("TARGET").unwrap(); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let name = env::var("CARGO_PKG_NAME").unwrap(); if target.starts_with("thumbv") { - if env::var_os("CARGO_FEATURE_INLINE_ASM").is_none() { - fs::copy( - format!("bin/{}.a", target), - out_dir.join(format!("lib{}.a", name)), - ) - .unwrap(); - - println!("cargo:rustc-link-lib=static={}", name); - println!("cargo:rustc-link-search={}", out_dir.display()); - } - println!("cargo:rustc-cfg=thumb"); } } diff --git a/cortex-m-semihosting/src/lib.rs b/cortex-m-semihosting/src/lib.rs index 3bc23ea..4ff975d 100644 --- a/cortex-m-semihosting/src/lib.rs +++ b/cortex-m-semihosting/src/lib.rs @@ -151,14 +151,6 @@ //! //! # Optional features //! -//! ## `inline-asm` -//! -//! When this feature is enabled semihosting is implemented using inline assembly and -//! compiling this crate requires nightly. -//! -//! When this feature is disabled semihosting is implemented using FFI calls into an external -//! assembly file and compiling this crate works on stable and beta. -//! //! ## `jlink-quirks` //! //! When this feature is enabled, return values above `0xfffffff0` from semihosting operation @@ -191,11 +183,6 @@ pub mod export; pub mod hio; pub mod nr; -#[cfg(all(thumb, not(feature = "inline-asm")))] -extern "C" { - fn __sh_syscall(nr: usize, arg: usize) -> usize; -} - /// Performs a semihosting operation, takes a pointer to an argument block #[inline(always)] pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize { @@ -206,10 +193,7 @@ pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize { #[inline(always)] pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize { match () { - #[cfg(all(thumb, not(feature = "inline-asm"), not(feature = "no-semihosting")))] - () => __sh_syscall(_nr, _arg), - - #[cfg(all(thumb, feature = "inline-asm", not(feature = "no-semihosting")))] + #[cfg(all(thumb, not(feature = "no-semihosting")))] () => { let mut nr = _nr; core::arch::asm!( diff --git a/panic-semihosting/CHANGELOG.md b/panic-semihosting/CHANGELOG.md index 95c3890..c7a2e78 100644 --- a/panic-semihosting/CHANGELOG.md +++ b/panic-semihosting/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Always use inline-asm, requiring Rust 1.59. + ## [v0.5.6] - 2020-11-14 - Fix update to docs.rs to build for an embedded target diff --git a/panic-semihosting/Cargo.toml b/panic-semihosting/Cargo.toml index 46a3d2f..dd3511e 100644 --- a/panic-semihosting/Cargo.toml +++ b/panic-semihosting/Cargo.toml @@ -18,7 +18,7 @@ cortex-m-semihosting = { path = "../cortex-m-semihosting", version = ">= 0.3, < [features] exit = [] -inline-asm = ["cortex-m-semihosting/inline-asm", "cortex-m/inline-asm"] +inline-asm = [] jlink-quirks = ["cortex-m-semihosting/jlink-quirks"] [package.metadata.docs.rs] diff --git a/panic-semihosting/README.md b/panic-semihosting/README.md index baacf1a..f8057d3 100644 --- a/panic-semihosting/README.md +++ b/panic-semihosting/README.md @@ -8,7 +8,7 @@ This project is developed and maintained by the [Cortex-M team][team]. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.32.0 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59.0 and up. It *might* compile with older versions but that may change in any new patch release. ## License diff --git a/panic-semihosting/src/lib.rs b/panic-semihosting/src/lib.rs index 1db7b72..1d7379e 100644 --- a/panic-semihosting/src/lib.rs +++ b/panic-semihosting/src/lib.rs @@ -47,14 +47,6 @@ //! //! We discourage using this feature when the program will run on hardware as the exit call can //! leave the hardware debugger in an inconsistent state. -//! -//! ## `inline-asm` -//! -//! When this feature is enabled semihosting is implemented using inline assembly (`asm!`) and -//! compiling this crate requires nightly. -//! -//! When this feature is disabled semihosting is implemented using FFI calls into an external -//! assembly file and compiling this crate works on stable and beta. #![cfg(all(target_arch = "arm", target_os = "none"))] #![deny(missing_docs)] @@ -1,18 +1,17 @@ //! Miscellaneous assembly instructions -// When inline assembly is enabled, pull in the assembly routines here. `call_asm!` will invoke -// these routines. -#[cfg(feature = "inline-asm")] -#[path = "../asm/inline.rs"] -pub(crate) mod inline; +#[cfg(cortex_m)] +use core::arch::asm; +use core::sync::atomic::{compiler_fence, Ordering}; /// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". /// /// **NOTE** calling `bkpt` when the processor is not connected to a debugger will cause an /// exception. +#[cfg(cortex_m)] #[inline(always)] pub fn bkpt() { - call_asm!(__bkpt()); + unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) }; } /// Blocks the program for *at least* `cycles` CPU cycles. @@ -24,41 +23,66 @@ pub fn bkpt() { /// and the execution time may vary with other factors. This delay is mainly useful for simple /// timer-less initialization of peripherals if and only if accurate timing is not essential. In /// any other case please use a more accurate method to produce a delay. +#[cfg(cortex_m)] #[inline] pub fn delay(cycles: u32) { - call_asm!(__delay(cycles: u32)); + // The loop will normally take 3 to 4 CPU cycles per iteration, but superscalar cores + // (eg. Cortex-M7) can potentially do it in 2, so we use that as the lower bound, since delaying + // for more cycles is okay. + // Add 1 to prevent an integer underflow which would cause a long freeze + let real_cycles = 1 + cycles / 2; + unsafe { + asm!( + // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m. + "1:", + "subs {}, #1", + "bne 1b", + inout(reg) real_cycles => _, + options(nomem, nostack), + ) + }; } /// A no-operation. Useful to prevent delay loops from being optimized away. #[inline] pub fn nop() { - call_asm!(__nop()); + // NOTE: This is a `pure` asm block, but applying that option allows the compiler to eliminate + // the nop entirely (or to collapse multiple subsequent ones). Since the user probably wants N + // nops when they call `nop` N times, let's not add that option. + #[cfg(cortex_m)] + unsafe { + asm!("nop", options(nomem, nostack, preserves_flags)) + }; } /// Generate an Undefined Instruction exception. /// /// Can be used as a stable alternative to `core::intrinsics::abort`. +#[cfg(cortex_m)] #[inline] pub fn udf() -> ! { - call_asm!(__udf() -> !) + unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) }; } /// Wait For Event +#[cfg(cortex_m)] #[inline] pub fn wfe() { - call_asm!(__wfe()) + unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) }; } /// Wait For Interrupt +#[cfg(cortex_m)] #[inline] pub fn wfi() { - call_asm!(__wfi()) + unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) }; } /// Send Event +#[cfg(cortex_m)] #[inline] pub fn sev() { - call_asm!(__sev()) + unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) }; } /// Instruction Synchronization Barrier @@ -67,7 +91,12 @@ pub fn sev() { /// from cache or memory, after the instruction has been completed. #[inline] pub fn isb() { - call_asm!(__isb()) + compiler_fence(Ordering::SeqCst); + #[cfg(cortex_m)] + unsafe { + asm!("isb", options(nomem, nostack, preserves_flags)) + }; + compiler_fence(Ordering::SeqCst); } /// Data Synchronization Barrier @@ -79,7 +108,12 @@ pub fn isb() { /// * all cache and branch predictor maintenance operations before this instruction complete #[inline] pub fn dsb() { - call_asm!(__dsb()) + compiler_fence(Ordering::SeqCst); + #[cfg(cortex_m)] + unsafe { + asm!("dsb", options(nomem, nostack, preserves_flags)) + }; + compiler_fence(Ordering::SeqCst); } /// Data Memory Barrier @@ -89,7 +123,12 @@ pub fn dsb() { /// after the `DMB` instruction. #[inline] pub fn dmb() { - call_asm!(__dmb()) + compiler_fence(Ordering::SeqCst); + #[cfg(cortex_m)] + unsafe { + asm!("dmb", options(nomem, nostack, preserves_flags)) + }; + compiler_fence(Ordering::SeqCst); } /// Test Target @@ -102,8 +141,15 @@ pub fn dmb() { // The __tt function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn tt(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__tt(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "tt {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Test Target Unprivileged @@ -117,8 +163,15 @@ pub fn tt(addr: *mut u32) -> u32 { // The __ttt function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn ttt(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__ttt(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "ttt {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Test Target Alternate Domain @@ -133,8 +186,15 @@ pub fn ttt(addr: *mut u32) -> u32 { // The __tta function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn tta(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__tta(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "tta {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Test Target Alternate Domain Unprivileged @@ -149,8 +209,15 @@ pub fn tta(addr: *mut u32) -> u32 { // The __ttat function does not dereference the pointer received. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn ttat(addr: *mut u32) -> u32 { - let addr = addr as u32; - call_asm!(__ttat(addr: u32) -> u32) + let mut target = addr as u32; + unsafe { + asm!( + "ttat {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target } /// Branch and Exchange Non-secure @@ -160,15 +227,17 @@ pub fn ttat(addr: *mut u32) -> u32 { #[inline] #[cfg(armv8m)] pub unsafe fn bx_ns(addr: u32) { - call_asm!(__bxns(addr: u32)); + asm!("bxns {}", in(reg) addr, options(nomem, nostack, preserves_flags)); } /// Semihosting syscall. /// /// This method is used by cortex-m-semihosting to provide semihosting syscalls. +#[cfg(cortex_m)] #[inline] -pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { - call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32) +pub unsafe fn semihosting_syscall(mut nr: u32, arg: u32) -> u32 { + asm!("bkpt #0xab", inout("r0") nr, in("r1") arg, options(nomem, nostack, preserves_flags)); + nr } /// Bootstrap. @@ -181,12 +250,27 @@ pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { /// /// `msp` and `rv` must point to valid stack memory and executable code, /// respectively. +#[cfg(cortex_m)] #[inline] pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { // Ensure thumb mode is set. let rv = (rv as u32) | 1; let msp = msp as u32; - call_asm!(__bootstrap(msp: u32, rv: u32) -> !); + asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); } /// Bootload. @@ -201,6 +285,7 @@ pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { /// The provided `vector_table` must point to a valid vector /// table, with a valid stack pointer as the first word and /// a valid reset vector as the second word. +#[cfg(cortex_m)] #[inline] pub unsafe fn bootload(vector_table: *const u32) -> ! { let msp = core::ptr::read_volatile(vector_table); diff --git a/src/call_asm.rs b/src/call_asm.rs deleted file mode 100644 index 295277f..0000000 --- a/src/call_asm.rs +++ /dev/null @@ -1,24 +0,0 @@ -/// An internal macro to invoke an assembly routine. -/// -/// Depending on whether the unstable `inline-asm` feature is enabled, this will either call into -/// the inline assembly implementation directly, or through the FFI shim (see `asm/lib.rs`). -macro_rules! call_asm { - ( $func:ident ( $($args:ident: $tys:ty),* ) $(-> $ret:ty)? ) => {{ - #[allow(unused_unsafe)] - unsafe { - match () { - #[cfg(feature = "inline-asm")] - () => crate::asm::inline::$func($($args),*), - - #[cfg(not(feature = "inline-asm"))] - () => { - extern "C" { - fn $func($($args: $tys),*) $(-> $ret)?; - } - - $func($($args),*) - }, - } - } - }}; -} diff --git a/src/interrupt.rs b/src/interrupt.rs index 68719ec..06b91f1 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -1,6 +1,10 @@ //! Interrupts pub use bare_metal::{CriticalSection, Mutex}; +#[cfg(cortex_m)] +use core::arch::asm; +#[cfg(cortex_m)] +use core::sync::atomic::{compiler_fence, Ordering}; /// Trait for enums of external interrupt numbers. /// @@ -24,9 +28,15 @@ pub unsafe trait InterruptNumber: Copy { } /// Disables all interrupts +#[cfg(cortex_m)] #[inline] pub fn disable() { - call_asm!(__cpsid()); + unsafe { + asm!("cpsid i", options(nomem, nostack, preserves_flags)); + } + + // Ensure no subsequent memory accesses are reordered to before interrupts are disabled. + compiler_fence(Ordering::SeqCst); } /// Enables all the interrupts @@ -34,14 +44,19 @@ pub fn disable() { /// # Safety /// /// - Do not call this function inside an `interrupt::free` critical section +#[cfg(cortex_m)] #[inline] pub unsafe fn enable() { - call_asm!(__cpsie()); + // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. + compiler_fence(Ordering::SeqCst); + + asm!("cpsie i", options(nomem, nostack, preserves_flags)); } /// Execute closure `f` in an interrupt-free context. /// /// This as also known as a "critical section". +#[cfg(cortex_m)] #[inline] pub fn free<F, R>(f: F) -> R where @@ -9,53 +9,17 @@ //! //! # Optional features //! -//! ## `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 -//! assembly files pre-compiled using `arm-none-eabi-gcc`). The advantages of enabling `inline-asm` -//! are: -//! -//! - Reduced overhead. FFI eliminates the possibility of inlining so all operations include a -//! function call overhead when `inline-asm` is not enabled. -//! -//! - Some of the `register` API only becomes available only when `inline-asm` is enabled. Check the -//! API docs for details. -//! -//! The disadvantage is that `inline-asm` requires a nightly toolchain. -//! //! ## `cm7-r0p1` //! //! This feature enables workarounds for errata found on Cortex-M7 chips with revision r0p1. Some //! functions in this crate only work correctly on those chips if this Cargo feature is enabled //! (the functions are documented accordingly). //! -//! ## `linker-plugin-lto` -//! -//! This feature links against prebuilt assembly blobs that are compatible with [Linker-Plugin LTO]. -//! This allows inlining assembly routines into the caller, even without the `inline-asm` feature, -//! and works on stable Rust (but note the drawbacks below!). -//! -//! If you want to use this feature, you need to be aware of a few things: -//! -//! - You need to make sure that `-Clinker-plugin-lto` is passed to rustc. Please refer to the -//! [Linker-Plugin LTO] documentation for details. -//! -//! - You have to use a Rust version whose LLVM version is compatible with the toolchain in -//! `asm-toolchain`. -//! -//! - Due to a [Rust bug][rust-lang/rust#75940] in compiler versions **before 1.49**, this option -//! does not work with optimization levels `s` and `z`. -//! -//! [Linker-Plugin LTO]: https://doc.rust-lang.org/stable/rustc/linker-plugin-lto.html -//! [rust-lang/rust#75940]: https://github.com/rust-lang/rust/issues/75940 -//! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.42 and up. It *might* +//! This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* //! compile with older versions but that may change in any new patch release. -#![cfg_attr(feature = "inline-asm", feature(asm))] #![deny(missing_docs)] #![no_std] #![allow(clippy::identity_op)] @@ -83,8 +47,6 @@ extern crate bare_metal; extern crate volatile_register; #[macro_use] -mod call_asm; -#[macro_use] mod macros; pub mod asm; diff --git a/src/peripheral/mod.rs b/src/peripheral/mod.rs index af922b1..d1dfb6a 100644 --- a/src/peripheral/mod.rs +++ b/src/peripheral/mod.rs @@ -60,6 +60,7 @@ use core::marker::PhantomData; use core::ops; +#[cfg(cortex_m)] use crate::interrupt; #[cfg(cm7)] @@ -163,6 +164,7 @@ static mut TAKEN: bool = false; impl Peripherals { /// Returns all the core peripherals *once* + #[cfg(cortex_m)] #[inline] pub fn take() -> Option<Self> { interrupt::free(|_| { diff --git a/src/register/apsr.rs b/src/register/apsr.rs index e83435c..edb8737 100644 --- a/src/register/apsr.rs +++ b/src/register/apsr.rs @@ -1,5 +1,8 @@ //! Application Program Status Register +#[cfg(cortex_m)] +use core::arch::asm; + /// Application Program Status Register #[derive(Clone, Copy, Debug)] pub struct Apsr { @@ -45,10 +48,10 @@ impl Apsr { } /// Reads the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub fn read() -> Apsr { - let bits: u32 = call_asm!(__apsr_r() -> u32); + let bits; + unsafe { asm!("mrs {}, APSR", out(reg) bits, options(nomem, nostack, preserves_flags)) }; Apsr { bits } } diff --git a/src/register/basepri.rs b/src/register/basepri.rs index 07084cd..cffb379 100644 --- a/src/register/basepri.rs +++ b/src/register/basepri.rs @@ -1,24 +1,42 @@ //! Base Priority Mask Register +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> u8 { - call_asm!(__basepri_r() -> u8) + let r; + unsafe { asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes to the CPU register /// /// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the /// `cm7-r0p1` Cargo feature or this function WILL misbehave. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(basepri: u8) { #[cfg(feature = "cm7-r0p1")] { - call_asm!(__basepri_w_cm7_r0p1(basepri: u8)); + asm!( + "mrs {1}, PRIMASK", + "cpsid i", + "tst.w {1}, #1", + "msr BASEPRI, {0}", + "it ne", + "bxne lr", + "cpsie i", + in(reg) basepri, + out(reg) _, + options(nomem, nostack, preserves_flags), + ); } #[cfg(not(feature = "cm7-r0p1"))] { - call_asm!(__basepri_w(basepri: u8)); + asm!("msr BASEPRI, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)); } } diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs index cea3838..2881c4f 100644 --- a/src/register/basepri_max.rs +++ b/src/register/basepri_max.rs @@ -1,5 +1,8 @@ //! Base Priority Mask Register (conditional write) +#[cfg(cortex_m)] +use core::arch::asm; + /// Writes to BASEPRI *if* /// /// - `basepri != 0` AND `basepri::read() == 0`, OR @@ -7,15 +10,31 @@ /// /// **IMPORTANT** If you are using a Cortex-M7 device with revision r0p1 you MUST enable the /// `cm7-r0p1` Cargo feature or this function WILL misbehave. +#[cfg(cortex_m)] #[inline] pub fn write(basepri: u8) { #[cfg(feature = "cm7-r0p1")] { - call_asm!(__basepri_max_cm7_r0p1(basepri: u8)); + unsafe { + asm!( + "mrs {1}, PRIMASK", + "cpsid i", + "tst.w {1}, #1", + "msr BASEPRI_MAX, {0}", + "it ne", + "bxne lr", + "cpsie i", + in(reg) basepri, + out(reg) _, + options(nomem, nostack, preserves_flags), + ); + } } #[cfg(not(feature = "cm7-r0p1"))] { - call_asm!(__basepri_max(basepri: u8)); + unsafe { + asm!("msr BASEPRI_MAX, {}", in(reg) basepri, options(nomem, nostack, preserves_flags)); + } } } diff --git a/src/register/control.rs b/src/register/control.rs index a991625..d781913 100644 --- a/src/register/control.rs +++ b/src/register/control.rs @@ -1,5 +1,10 @@ //! Control register +#[cfg(cortex_m)] +use core::arch::asm; +#[cfg(cortex_m)] +use core::sync::atomic::{compiler_fence, Ordering}; + /// Control register #[derive(Clone, Copy, Debug)] pub struct Control { @@ -150,15 +155,29 @@ impl Fpca { } /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> Control { - let bits: u32 = call_asm!(__control_r() -> u32); + let bits; + unsafe { asm!("mrs {}, CONTROL", out(reg) bits, options(nomem, nostack, preserves_flags)) }; Control { bits } } /// Writes to the CPU register. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(control: Control) { let control = control.bits(); - call_asm!(__control_w(control: u32)); + + // ISB is required after writing to CONTROL, + // per ARM architectural requirements (see Application Note 321). + asm!( + "msr CONTROL, {}", + "isb", + in(reg) control, + options(nomem, nostack, preserves_flags), + ); + + // Ensure memory accesses are not reordered around the CONTROL update. + compiler_fence(Ordering::SeqCst); } diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs index e57fa28..1d32709 100644 --- a/src/register/faultmask.rs +++ b/src/register/faultmask.rs @@ -1,5 +1,8 @@ //! Fault Mask Register +#[cfg(cortex_m)] +use core::arch::asm; + /// All exceptions are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Faultmask { @@ -24,9 +27,11 @@ impl Faultmask { } /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> Faultmask { - let r: u32 = call_asm!(__faultmask_r() -> u32); + let r: u32; + unsafe { asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; if r & (1 << 0) == (1 << 0) { Faultmask::Inactive } else { diff --git a/src/register/fpscr.rs b/src/register/fpscr.rs index 68692c7..bffed6c 100644 --- a/src/register/fpscr.rs +++ b/src/register/fpscr.rs @@ -1,5 +1,7 @@ //! Floating-point Status Control Register +use core::arch::asm; + /// Floating-point Status Control Register #[derive(Clone, Copy, Debug)] pub struct Fpscr { @@ -293,7 +295,8 @@ impl RMode { /// Read the FPSCR register #[inline] pub fn read() -> Fpscr { - let r: u32 = call_asm!(__fpscr_r() -> u32); + let r; + unsafe { asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags)) }; Fpscr::from_bits(r) } @@ -301,5 +304,5 @@ pub fn read() -> Fpscr { #[inline] pub unsafe fn write(fpscr: Fpscr) { let fpscr = fpscr.bits(); - call_asm!(__fpscr_w(fpscr: u32)); + asm!("vmsr fpscr, {}", in(reg) fpscr, options(nomem, nostack)); } diff --git a/src/register/lr.rs b/src/register/lr.rs index 1aa546c..02708ae 100644 --- a/src/register/lr.rs +++ b/src/register/lr.rs @@ -1,17 +1,20 @@ //! Link register +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__lr_r() -> u32) + let r; + unsafe { asm!("mov {}, lr", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__lr_w(bits: u32)); + asm!("mov lr, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/mod.rs b/src/register/mod.rs index 48d157a..aee7d21 100644 --- a/src/register/mod.rs +++ b/src/register/mod.rs @@ -56,13 +56,8 @@ pub mod msplim; #[cfg(armv8m_main)] pub mod psplim; -// Accessing these registers requires inline assembly because their contents are tied to the current -// stack frame -#[cfg(feature = "inline-asm")] pub mod apsr; -#[cfg(feature = "inline-asm")] pub mod lr; -#[cfg(feature = "inline-asm")] pub mod pc; diff --git a/src/register/msp.rs b/src/register/msp.rs index bccc2ae..22ce7d9 100644 --- a/src/register/msp.rs +++ b/src/register/msp.rs @@ -1,16 +1,27 @@ //! Main Stack Pointer +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__msp_r() -> u32) + let r; + unsafe { asm!("mrs {}, MSP", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register +#[cfg(cortex_m)] #[inline] #[deprecated = "calling this function invokes Undefined Behavior, consider asm::bootstrap as an alternative"] pub unsafe fn write(bits: u32) { - call_asm!(__msp_w(bits: u32)); + // Technically is writing to the stack pointer "not pushing any data to the stack"? + // In any event, if we don't set `nostack` here, this method is useless as the new + // stack value is immediately mutated by returning. Really this is just not a good + // method and its use is marked as deprecated. + asm!("msr MSP, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } /// Reads the Non-Secure CPU register from Secure state. @@ -19,7 +30,9 @@ pub unsafe fn write(bits: u32) { #[cfg(armv8m)] #[inline] pub fn read_ns() -> u32 { - call_asm!(__msp_ns_r() -> u32) + let r; + unsafe { asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the Non-Secure CPU register from Secure state. @@ -28,5 +41,5 @@ pub fn read_ns() -> u32 { #[cfg(armv8m)] #[inline] pub unsafe fn write_ns(bits: u32) { - call_asm!(__msp_ns_w(bits: u32)); + asm!("msr MSP_NS, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/msplim.rs b/src/register/msplim.rs index ac6f9ed..7b45b33 100644 --- a/src/register/msplim.rs +++ b/src/register/msplim.rs @@ -1,13 +1,17 @@ //! Main Stack Pointer Limit Register +use core::arch::asm; + /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__msplim_r() -> u32) + let r; + unsafe { asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__msplim_w(bits: u32)) + asm!("msr MSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/pc.rs b/src/register/pc.rs index 0b33629..3460664 100644 --- a/src/register/pc.rs +++ b/src/register/pc.rs @@ -1,17 +1,20 @@ //! Program counter +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__pc_r() -> u32) + let r; + unsafe { asm!("mov {}, pc", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register -/// -/// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. +#[cfg(cortex_m)] #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__pc_w(bits: u32)); + asm!("mov pc, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/primask.rs b/src/register/primask.rs index 842ca49..e95276f 100644 --- a/src/register/primask.rs +++ b/src/register/primask.rs @@ -1,5 +1,8 @@ //! Priority mask register +#[cfg(cortex_m)] +use core::arch::asm; + /// All exceptions with configurable priority are ... #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primask { @@ -24,9 +27,11 @@ impl Primask { } /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> Primask { - let r: u32 = call_asm!(__primask_r() -> u32); + let r: u32; + unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; if r & (1 << 0) == (1 << 0) { Primask::Inactive } else { diff --git a/src/register/psp.rs b/src/register/psp.rs index 0bca22c..c8f53b9 100644 --- a/src/register/psp.rs +++ b/src/register/psp.rs @@ -1,13 +1,22 @@ //! Process Stack Pointer +#[cfg(cortex_m)] +use core::arch::asm; + /// Reads the CPU register +#[cfg(cortex_m)] #[inline] pub fn read() -> u32 { - call_asm!(__psp_r() -> u32) + let r; + unsafe { asm!("mrs {}, PSP", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register +#[cfg(cortex_m)] #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__psp_w(bits: u32)) + // See comment on msp_w. Unlike MSP, there are legitimate use-cases for modifying PSP + // if MSP is currently being used as the stack pointer. + asm!("msr PSP, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/src/register/psplim.rs b/src/register/psplim.rs index 8ee1e94..832f9c6 100644 --- a/src/register/psplim.rs +++ b/src/register/psplim.rs @@ -1,13 +1,17 @@ //! Process Stack Pointer Limit Register +use core::arch::asm; + /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__psplim_r() -> u32) + let r; + unsafe { asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__psplim_w(bits: u32)) + asm!("msr PSPLIM, {}", in(reg) bits, options(nomem, nostack, preserves_flags)); } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index f6a57b3..9d96686 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -1,117 +1,8 @@ //! `cargo xtask` automation. //! //! Please refer to <https://github.com/matklad/cargo-xtask/> for an explanation of the concept. -//! -//! Also see the docs in `asm.rs`. - -use std::collections::BTreeMap; -use std::env::current_dir; -use std::fs::{self, File}; -use std::process::{Command, Stdio}; - -fn toolchain() -> String { - fs::read_to_string("asm-toolchain") - .unwrap() - .trim() - .to_string() -} - -fn rustc() -> Command { - let mut cmd = Command::new("rustc"); - cmd.arg(format!("+{}", toolchain())); - cmd -} - -fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { - let mut cmd = rustc(); - - // Set the codegen target. - cmd.arg("--target").arg(target); - // Set all the `--cfg` directives for the target. - cmd.args(cfgs.iter().map(|cfg| format!("--cfg={}", cfg))); - - // We want some level of debuginfo to allow unwinding through the functions. - cmd.arg("-g"); - // We always optimize the assembly shims. There's not really any reason not to. - cmd.arg("-O"); - - // We use LTO on the archive to ensure the (unused) panic handler is removed, preventing - // a linker error when the archives are linked into final crates with two panic handlers. - cmd.arg("-Clto=yes"); - - // rustc will usually add frame pointers by default to aid with debugging, but that is a high - // overhead for the tiny assembly routines. - cmd.arg("-Cforce-frame-pointers=no"); - // We don't want any system-specific paths to show up since we ship the result to other users. - // Add `--remap-path-prefix $(pwd)=.`. - let mut dir = current_dir().unwrap().as_os_str().to_os_string(); - dir.push("=."); - cmd.arg("--remap-path-prefix").arg(dir); - - // We let rustc build a single object file, not a staticlib, since the latter pulls in loads of - // code that will never be used (`compiler_builtins` and `core::fmt`, etc.). We build the static - // archive by hand after compiling. - cmd.arg("--emit=obj"); - - if plugin_lto { - // Make artifacts compatible with Linker-Plugin LTO (and incompatible with everything else). - cmd.arg("-Clinker-plugin-lto"); - } - - let file_stub = if plugin_lto { - format!("{}-lto", target) - } else { - target.to_string() - }; - - let obj_file = format!("bin/{}.o", file_stub); - - // Pass output and input file. - cmd.arg("-o").arg(&obj_file); - cmd.arg("asm/lib.rs"); - - println!("{:?}", cmd); - let status = cmd.status().unwrap(); - assert!(status.success()); - - // Archive `target.o` -> `bin/target.a`. - let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap()); - - // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times, - // etc.). - let file = fs::read(&obj_file).unwrap(); - builder - .append( - &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64), - &*file, - ) - .unwrap(); - - fs::remove_file(&obj_file).unwrap(); -} - -fn assemble(target: &str, cfgs: &[&str]) { - assemble_really(target, cfgs, false); - assemble_really(target, cfgs, true); -} - -// `--target` -> `--cfg` list (mirrors what `build.rs` does). -static TARGETS: &[(&str, &[&str])] = &[ - ("thumbv6m-none-eabi", &[]), - ("thumbv7m-none-eabi", &["armv7m"]), - ("thumbv7em-none-eabi", &["armv7m", "armv7em"]), - ("thumbv7em-none-eabihf", &["armv7m", "armv7em", "has_fpu"]), - ("thumbv8m.base-none-eabi", &["armv8m", "armv8m_base"]), - ( - "thumbv8m.main-none-eabi", - &["armv7m", "armv8m", "armv8m_main"], - ), - ( - "thumbv8m.main-none-eabihf", - &["armv7m", "armv8m", "armv8m_main", "has_fpu"], - ), -]; +use std::process::Command; pub fn install_targets(targets: &mut dyn Iterator<Item = &str>, toolchain: Option<&str>) { let mut rustup = Command::new("rustup"); @@ -125,90 +16,6 @@ pub fn install_targets(targets: &mut dyn Iterator<Item = &str>, toolchain: Optio assert!(status.success(), "rustup command failed: {:?}", rustup); } -pub fn assemble_blobs() { - let mut cmd = rustc(); - cmd.arg("-V"); - cmd.stdout(Stdio::null()); - let status = cmd.status().unwrap(); - let toolchain = toolchain(); - - if !status.success() { - println!( - "asm toolchain {} does not seem to be installed. installing it now.", - toolchain - ); - - let mut rustup = Command::new("rustup"); - let status = rustup.arg("install").arg(&toolchain).status().unwrap(); - assert!(status.success(), "rustup command failed: {:?}", rustup); - } - - install_targets( - &mut TARGETS.iter().map(|(target, _)| *target), - Some(&*toolchain), - ); - - for (target, cfgs) in TARGETS { - println!("building artifacts for {}", target); - assemble(target, cfgs); - } -} - -pub fn check_blobs() { - // Load each `.a` file in `bin` into memory. - let mut files_before = BTreeMap::new(); - for entry in fs::read_dir("bin").unwrap() { - let entry = entry.unwrap(); - if entry.path().extension().unwrap() == "a" { - files_before.insert( - entry - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - fs::read(entry.path()).unwrap(), - ); - } - } - - assemble_blobs(); - - let mut files_after = BTreeMap::new(); - for entry in fs::read_dir("bin").unwrap() { - let entry = entry.unwrap(); - if entry.path().extension().unwrap() == "a" { - files_after.insert( - entry - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - fs::read(entry.path()).unwrap(), - ); - } - } - - // Ensure they contain the same files. - let before = files_before.keys().collect::<Vec<_>>(); - let after = files_after.keys().collect::<Vec<_>>(); - assert_eq!(before, after); - - for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) { - if before != after { - panic!( - "{} is not up-to-date, please run `cargo xtask assemble`", - file - ); - } - } - - println!("Blobs identical."); -} - // Check that serde and PartialOrd works with VectActive pub fn check_host_side() { use cortex_m::peripheral::{itm::LocalTimestampOptions, scb::VectActive}; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 26dce31..4673a45 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,18 +1,14 @@ use std::{env, process}; -use xtask::{assemble_blobs, check_blobs, check_host_side}; +use xtask::check_host_side; fn main() { let subcommand = env::args().nth(1); match subcommand.as_deref() { - Some("assemble") => assemble_blobs(), - Some("check-blobs") => check_blobs(), Some("check-host-side") => check_host_side(), _ => { eprintln!("usage: cargo xtask <subcommand>"); eprintln!(); eprintln!("subcommands:"); - eprintln!(" assemble Reassemble the pre-built artifacts"); - eprintln!(" check-blobs Check that the pre-built artifacts are up-to-date and reproducible"); eprintln!(" check-host-side Build the crate in a non-Cortex-M host application and check host side usage of certain types"); process::exit(1); } diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs index 37466e9..603491c 100644 --- a/xtask/tests/ci.rs +++ b/xtask/tests/ci.rs @@ -1,6 +1,6 @@ use std::process::Command; use std::{env, str}; -use xtask::{check_blobs, check_host_side, install_targets}; +use xtask::{check_host_side, install_targets}; /// List of all compilation targets we support. /// @@ -44,13 +44,13 @@ fn build(package: &str, target: &str, features: &[&str]) { #[rustfmt::skip] static PACKAGE_FEATURES: &[(&str, &[&str], &[&str])] = &[ - ("cortex-m", ALL_TARGETS, &["inline-asm", "cm7-r0p1"]), // no `linker-plugin-lto` since it's experimental - ("cortex-m-semihosting", ALL_TARGETS, &["inline-asm", "no-semihosting", "jlink-quirks"]), - ("panic-semihosting", ALL_TARGETS, &["inline-asm", "exit", "jlink-quirks"]), + ("cortex-m", ALL_TARGETS, &["cm7-r0p1"]), + ("cortex-m-semihosting", ALL_TARGETS, &["no-semihosting", "jlink-quirks"]), + ("panic-semihosting", ALL_TARGETS, &["exit", "jlink-quirks"]), ("panic-itm", NON_BASE_TARGETS, &[]), ]; -fn check_crates_build(is_nightly: bool) { +fn check_crates_build(_is_nightly: bool) { // Build all crates for each supported target. for (package, targets, all_features) in PACKAGE_FEATURES { for target in *targets { @@ -58,11 +58,8 @@ fn check_crates_build(is_nightly: bool) { // Relies on all crates in this repo to use the same convention. let should_use_feature = |feat: &str| { match feat { - // This is nightly-only, so don't use it on stable. - "inline-asm" => is_nightly, // This only affects thumbv7em targets. "cm7-r0p1" => target.starts_with("thumbv7em"), - _ => true, } }; @@ -98,9 +95,6 @@ fn main() { install_targets(&mut ALL_TARGETS.iter().cloned(), None); - // Check that the ASM blobs are up-to-date. - check_blobs(); - let output = Command::new("rustc").arg("-V").output().unwrap(); let is_nightly = str::from_utf8(&output.stdout).unwrap().contains("nightly"); |