diff --git a/cortex-m/asm-toolchain b/cortex-m/asm-toolchain deleted file mode 100644 index cc5dbb24a..000000000 --- a/cortex-m/asm-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly-2021-12-16 diff --git a/cortex-m/asm/inline.rs b/cortex-m/asm/inline.rs deleted file mode 100644 index 3444c62d5..000000000 --- a/cortex-m/asm/inline.rs +++ /dev/null @@ -1,452 +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::{Ordering, compiler_fence}; - -#[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!( - // The `bne` on some cores (eg Cortex-M4) will take a different number of instructions - // depending on the alignment of the branch target. Set the alignment of the top of the - // loop to prevent surprising timing changes when the alignment of the delay() changes. - ".p2align 3", - // 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(nostack, preserves_flags)); - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __dsb() { - compiler_fence(Ordering::SeqCst); - asm!("dsb", options(nostack, preserves_flags)); - compiler_fence(Ordering::SeqCst); -} - -#[inline(always)] -pub unsafe fn __isb() { - compiler_fence(Ordering::SeqCst); - asm!("isb", options(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::{Ordering, compiler_fence}; - - #[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/cortex-m/asm/lib.rs b/cortex-m/asm/lib.rs deleted file mode 100644 index 48f3dc211..000000000 --- a/cortex-m/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/cortex-m/bin/thumbv6m-none-eabi-lto.a b/cortex-m/bin/thumbv6m-none-eabi-lto.a deleted file mode 100644 index 529cd6466..000000000 Binary files a/cortex-m/bin/thumbv6m-none-eabi-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv6m-none-eabi.a b/cortex-m/bin/thumbv6m-none-eabi.a deleted file mode 100644 index 30d33eb9d..000000000 Binary files a/cortex-m/bin/thumbv6m-none-eabi.a and /dev/null differ diff --git a/cortex-m/bin/thumbv7em-none-eabi-lto.a b/cortex-m/bin/thumbv7em-none-eabi-lto.a deleted file mode 100644 index 49b03fc6b..000000000 Binary files a/cortex-m/bin/thumbv7em-none-eabi-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv7em-none-eabi.a b/cortex-m/bin/thumbv7em-none-eabi.a deleted file mode 100644 index d79d709af..000000000 Binary files a/cortex-m/bin/thumbv7em-none-eabi.a and /dev/null differ diff --git a/cortex-m/bin/thumbv7em-none-eabihf-lto.a b/cortex-m/bin/thumbv7em-none-eabihf-lto.a deleted file mode 100644 index a477de834..000000000 Binary files a/cortex-m/bin/thumbv7em-none-eabihf-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv7em-none-eabihf.a b/cortex-m/bin/thumbv7em-none-eabihf.a deleted file mode 100644 index f3130e022..000000000 Binary files a/cortex-m/bin/thumbv7em-none-eabihf.a and /dev/null differ diff --git a/cortex-m/bin/thumbv7m-none-eabi-lto.a b/cortex-m/bin/thumbv7m-none-eabi-lto.a deleted file mode 100644 index a6a238cad..000000000 Binary files a/cortex-m/bin/thumbv7m-none-eabi-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv7m-none-eabi.a b/cortex-m/bin/thumbv7m-none-eabi.a deleted file mode 100644 index 30dbd187f..000000000 Binary files a/cortex-m/bin/thumbv7m-none-eabi.a and /dev/null differ diff --git a/cortex-m/bin/thumbv8m.base-none-eabi-lto.a b/cortex-m/bin/thumbv8m.base-none-eabi-lto.a deleted file mode 100644 index 166e4eb9e..000000000 Binary files a/cortex-m/bin/thumbv8m.base-none-eabi-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv8m.base-none-eabi.a b/cortex-m/bin/thumbv8m.base-none-eabi.a deleted file mode 100644 index c489a8888..000000000 Binary files a/cortex-m/bin/thumbv8m.base-none-eabi.a and /dev/null differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabi-lto.a b/cortex-m/bin/thumbv8m.main-none-eabi-lto.a deleted file mode 100644 index 5ff66960f..000000000 Binary files a/cortex-m/bin/thumbv8m.main-none-eabi-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabi.a b/cortex-m/bin/thumbv8m.main-none-eabi.a deleted file mode 100644 index 997192328..000000000 Binary files a/cortex-m/bin/thumbv8m.main-none-eabi.a and /dev/null differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a b/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a deleted file mode 100644 index 6ee48896a..000000000 Binary files a/cortex-m/bin/thumbv8m.main-none-eabihf-lto.a and /dev/null differ diff --git a/cortex-m/bin/thumbv8m.main-none-eabihf.a b/cortex-m/bin/thumbv8m.main-none-eabihf.a deleted file mode 100644 index 9e07c40f7..000000000 Binary files a/cortex-m/bin/thumbv8m.main-none-eabihf.a and /dev/null differ diff --git a/cortex-m/build.rs b/cortex-m/build.rs index fa1a5a130..8e91e90fc 100644 --- a/cortex-m/build.rs +++ b/cortex-m/build.rs @@ -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()); - } - println!("cargo:rustc-check-cfg=cfg(armv6m)"); println!("cargo:rustc-check-cfg=cfg(armv7m)"); println!("cargo:rustc-check-cfg=cfg(armv7em)"); @@ -47,7 +27,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/src/asm.rs b/cortex-m/src/asm.rs index 3f8fd0e84..8b84a5b07 100644 --- a/cortex-m/src/asm.rs +++ b/cortex-m/src/asm.rs @@ -2,9 +2,15 @@ // 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; + +#![allow(missing_docs)] + +#[cfg_attr(any(armv6m, armv7m, armv7em, armv8m), path = "asm/inner.rs")] +#[cfg_attr( + all(not(armv6m), not(armv7m), not(armv7em), not(armv8m)), + path = "asm/inner_mock.rs" +)] +pub mod inner; /// Puts the processor in Debug state. Debuggers can pick this up as a "breakpoint". /// @@ -12,7 +18,7 @@ pub(crate) mod inline; /// exception. #[inline(always)] pub fn bkpt() { - call_asm!(__bkpt()); + unsafe { inner::__bkpt() }; } /// Blocks the program for *at least* `cycles` CPU cycles. @@ -31,13 +37,13 @@ pub fn bkpt() { /// please use a more accurate method to produce a delay. #[inline] pub fn delay(cycles: u32) { - call_asm!(__delay(cycles: u32)); + unsafe { inner::__delay(cycles) }; } /// A no-operation. Useful to prevent delay loops from being optimized away. #[inline] pub fn nop() { - call_asm!(__nop()); + unsafe { inner::__nop() }; } /// Generate an Undefined Instruction exception. @@ -45,25 +51,25 @@ pub fn nop() { /// Can be used as a stable alternative to `core::intrinsics::abort`. #[inline] pub fn udf() -> ! { - call_asm!(__udf() -> !) + unsafe { inner::__udf() } } /// Wait For Event #[inline] pub fn wfe() { - call_asm!(__wfe()) + unsafe { inner::__wfe() } } /// Wait For Interrupt #[inline] pub fn wfi() { - call_asm!(__wfi()) + unsafe { inner::__wfi() } } /// Send Event #[inline] pub fn sev() { - call_asm!(__sev()) + unsafe { inner::__sev() } } /// Instruction Synchronization Barrier @@ -72,7 +78,7 @@ pub fn sev() { /// from cache or memory, after the instruction has been completed. #[inline] pub fn isb() { - call_asm!(__isb()) + unsafe { inner::__isb() } } /// Data Synchronization Barrier @@ -84,7 +90,7 @@ pub fn isb() { /// * all cache and branch predictor maintenance operations before this instruction complete #[inline] pub fn dsb() { - call_asm!(__dsb()) + unsafe { inner::__dsb() } } /// Data Memory Barrier @@ -94,7 +100,7 @@ pub fn dsb() { /// after the `DMB` instruction. #[inline] pub fn dmb() { - call_asm!(__dmb()) + unsafe { inner::__dmb() } } /// Test Target @@ -108,7 +114,7 @@ pub fn dmb() { #[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) + unsafe { crate::asm::inner::__tt(addr) } } /// Test Target Unprivileged @@ -123,7 +129,7 @@ pub fn tt(addr: *mut u32) -> u32 { #[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) + unsafe { crate::asm::inner::__ttt(addr) } } /// Test Target Alternate Domain @@ -139,7 +145,7 @@ pub fn ttt(addr: *mut u32) -> u32 { #[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) + unsafe { crate::asm::inner::__tta(addr) } } /// Test Target Alternate Domain Unprivileged @@ -155,7 +161,7 @@ pub fn tta(addr: *mut u32) -> u32 { #[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) + unsafe { crate::asm::inner::__ttat(addr) } } /// Branch and Exchange Non-secure @@ -165,7 +171,7 @@ pub fn ttat(addr: *mut u32) -> u32 { #[inline] #[cfg(armv8m)] pub unsafe fn bx_ns(addr: u32) { - call_asm!(__bxns(addr: u32)); + unsafe { crate::asm::inner::__bxns(addr) }; } /// Semihosting syscall. @@ -173,7 +179,7 @@ pub unsafe fn bx_ns(addr: u32) { /// This method is used by cortex-m-semihosting to provide semihosting syscalls. #[inline] pub unsafe fn semihosting_syscall(nr: u32, arg: u32) -> u32 { - call_asm!(__sh_syscall(nr: u32, arg: u32) -> u32) + unsafe { inner::__sh_syscall(nr, arg) } } /// Switch to unprivileged mode using the Process Stack @@ -276,7 +282,7 @@ 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) -> !); + unsafe { inner::__bootstrap(msp, rv) } } /// Bootload. diff --git a/cortex-m/src/asm/inner.rs b/cortex-m/src/asm/inner.rs new file mode 100644 index 000000000..ef9059856 --- /dev/null +++ b/cortex-m/src/asm/inner.rs @@ -0,0 +1,498 @@ +use core::arch::asm; +use core::sync::atomic::{Ordering, compiler_fence}; + +#[inline(always)] +pub unsafe fn __bkpt() { + unsafe { asm!("bkpt", options(nomem, nostack, preserves_flags)) }; +} + +#[inline(always)] +pub unsafe fn __control_r() -> u32 { + let r; + unsafe { 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). + unsafe { + 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() { + 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); +} + +#[inline(always)] +pub unsafe fn __cpsie() { + // Ensure no preceeding memory accesses are reordered to after interrupts are enabled. + compiler_fence(Ordering::SeqCst); + + unsafe { 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; + unsafe { + asm!( + // The `bne` on some cores (eg Cortex-M4) will take a different number of instructions + // depending on the alignment of the branch target. Set the alignment of the top of the + // loop to prevent surprising timing changes when the alignment of the delay() changes. + ".p2align 3", + // Use local labels to avoid R_ARM_THM_JUMP8 relocations which fail on thumbv6m. + "2:", // not 1 or 0 because of https://github.com/llvm/llvm-project/issues/99547 + "subs {}, #1", + "bne 1b", + inout(reg) real_cyc => _, + options(nomem, nostack), + ) + }; +} + +#[inline(always)] +pub unsafe fn __dmb() { + compiler_fence(Ordering::SeqCst); + unsafe { asm!("dmb", options(nostack, preserves_flags)) }; + compiler_fence(Ordering::SeqCst); +} + +#[inline(always)] +pub unsafe fn __dsb() { + compiler_fence(Ordering::SeqCst); + unsafe { asm!("dsb", options(nostack, preserves_flags)) }; + compiler_fence(Ordering::SeqCst); +} + +#[inline(always)] +pub unsafe fn __isb() { + compiler_fence(Ordering::SeqCst); + unsafe { asm!("isb", options(nostack, preserves_flags)) }; + compiler_fence(Ordering::SeqCst); +} + +#[inline(always)] +pub unsafe fn __msp_r() -> u32 { + let r; + unsafe { 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. + unsafe { 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; + unsafe { 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. + unsafe { 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; + unsafe { 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) { + unsafe { 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; + unsafe { 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) { + unsafe { asm!("mov lr, {}", in(reg) val, options(nomem, nostack, preserves_flags)) }; +} + +#[inline(always)] +pub unsafe fn __primask_r() -> u32 { + let r; + unsafe { asm!("mrs {}, PRIMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r +} + +#[inline(always)] +pub unsafe fn __psp_r() -> u32 { + let r; + unsafe { 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. + unsafe { asm!("msr PSP, {}", in(reg) val, options(nomem, nostack, preserves_flags)) }; +} + +#[inline(always)] +pub unsafe fn __sev() { + unsafe { asm!("sev", options(nomem, nostack, preserves_flags)) }; +} + +#[inline(always)] +pub unsafe fn __udf() -> ! { + unsafe { asm!("udf #0", options(noreturn, nomem, nostack, preserves_flags)) }; +} + +#[inline(always)] +pub unsafe fn __wfe() { + unsafe { asm!("wfe", options(nomem, nostack, preserves_flags)) }; +} + +#[inline(always)] +pub unsafe fn __wfi() { + unsafe { asm!("wfi", options(nomem, nostack, preserves_flags)) }; +} + +/// Semihosting syscall. +#[inline(always)] +pub unsafe fn __sh_syscall(mut nr: u32, arg: u32) -> u32 { + unsafe { + 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) -> ! { + unsafe { + 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), + ) + }; +} + +#[cfg(not(armv6m))] +pub(crate) use v7m::*; + +#[cfg(not(armv6m))] +pub(crate) mod v7m { + use super::*; + + #[cfg(any(armv7m, armv8m_main))] + #[inline(always)] + pub unsafe fn __basepri_max(val: u8) { + unsafe { + asm!("msr BASEPRI_MAX, {}", in(reg) val, options(nomem, nostack, preserves_flags)) + }; + } + + #[cfg(any(armv7m, armv8m_main))] + #[inline(always)] + pub unsafe fn __basepri_r() -> u8 { + let r; + unsafe { asm!("mrs {}, BASEPRI", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r + } + + #[cfg(any(armv7m, armv8m_main))] + #[inline(always)] + pub unsafe fn __basepri_w(val: u8) { + unsafe { asm!("msr BASEPRI, {}", in(reg) val, options(nomem, nostack, preserves_flags)) }; + } + + #[cfg(any(armv7m, armv8m_main))] + #[inline(always)] + pub unsafe fn __faultmask_r() -> u32 { + let r; + unsafe { asm!("mrs {}, FAULTMASK", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r + } + + // Should this be safe? + #[inline(always)] + pub unsafe fn __enable_icache() { + unsafe { + 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); + } + + // Should this be safe? + #[inline(always)] + pub unsafe fn __enable_dcache() { + unsafe { + 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(feature = "cm7-r0p1")] +pub use self::v7em::*; + +#[cfg(any(armv7em, armv8m))] +mod v7em { + #[cfg(feature = "cm7-r0p1")] + use super::*; + + #[cfg(feature = "cm7-r0p1")] + #[inline(always)] + pub unsafe fn __basepri_max_cm7_r0p1(val: u8) { + unsafe { + 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), + ) + }; + } + + #[cfg(feature = "cm7-r0p1")] + #[inline(always)] + pub unsafe fn __basepri_w_cm7_r0p1(val: u8) { + unsafe { + 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 super::*; + + #[inline(always)] + pub unsafe fn __tt(mut target: u32) -> u32 { + unsafe { + asm!( + "tt {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target + } + + #[inline(always)] + pub unsafe fn __ttt(mut target: u32) -> u32 { + unsafe { + asm!( + "ttt {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target + } + + #[inline(always)] + pub unsafe fn __tta(mut target: u32) -> u32 { + unsafe { + asm!( + "tta {target}, {target}", + target = inout(reg) target, + options(nomem, nostack, preserves_flags), + ) + }; + target + } + + #[inline(always)] + pub unsafe fn __ttat(mut target: u32) -> u32 { + unsafe { + 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; + unsafe { asm!("mrs {}, MSP_NS", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r + } + + #[inline(always)] + pub unsafe fn __msp_ns_w(val: u32) { + unsafe { asm!("msr MSP_NS, {}", in(reg) val, options(nomem, nostack, preserves_flags)) }; + } + + #[inline(always)] + pub unsafe fn __bxns(val: u32) { + unsafe { 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 super::*; + + #[inline(always)] + pub unsafe fn __msplim_r() -> u32 { + let r; + unsafe { asm!("mrs {}, MSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r + } + + #[inline(always)] + pub unsafe fn __msplim_w(val: u32) { + unsafe { asm!("msr MSPLIM, {}", in(reg) val, options(nomem, nostack, preserves_flags)) }; + } + + #[inline(always)] + pub unsafe fn __psplim_r() -> u32 { + let r; + unsafe { asm!("mrs {}, PSPLIM", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r + } + + #[inline(always)] + pub unsafe fn __psplim_w(val: u32) { + unsafe { 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 super::*; + + #[inline(always)] + pub unsafe fn __fpscr_r() -> u32 { + let r; + unsafe { asm!("vmrs {}, fpscr", out(reg) r, options(nomem, nostack, preserves_flags)) }; + r + } + + #[inline(always)] + pub unsafe fn __fpscr_w(val: u32) { + unsafe { asm!("vmsr fpscr, {}", in(reg) val, options(nomem, nostack)) }; + } +} + +// /// 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] +// #[unsafe(link_section = ".text.asm_panic_handler")] +// fn panic(_: &core::panic::PanicInfo) -> ! { +// unsafe 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/cortex-m/src/asm/inner_mock.rs b/cortex-m/src/asm/inner_mock.rs new file mode 100644 index 000000000..3b36e6b8e --- /dev/null +++ b/cortex-m/src/asm/inner_mock.rs @@ -0,0 +1,129 @@ +#[inline(always)] +pub(crate) unsafe fn __bkpt() {} + +#[inline(always)] +pub(crate) unsafe fn __control_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __control_w(_w: u32) {} + +#[inline(always)] +pub(crate) unsafe fn __cpsid() {} + +#[inline(always)] +pub(crate) unsafe fn __cpsie() {} + +#[inline(always)] +pub(crate) unsafe fn __delay(_cyc: u32) {} + +#[inline(always)] +pub(crate) unsafe fn __dmb() {} + +#[inline(always)] +pub(crate) unsafe fn __dsb() {} + +#[inline(always)] +pub(crate) unsafe fn __isb() {} + +#[inline(always)] +pub(crate) unsafe fn __msp_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __msp_w(_val: u32) {} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub(crate) unsafe fn __apsr_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __nop() {} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub(crate) unsafe fn __pc_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __pc_w(_val: u32) {} + +#[inline(always)] +pub(crate) unsafe fn __lr_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __lr_w(_val: u32) {} + +#[inline(always)] +pub(crate) unsafe fn __primask_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __psp_r() -> u32 { + 0 +} + +#[inline(always)] +pub(crate) unsafe fn __psp_w(_val: u32) {} + +#[inline(always)] +pub(crate) unsafe fn __sev() {} + +#[inline(always)] +pub(crate) unsafe fn __udf() -> ! { + unimplemented!() +} + +#[inline(always)] +pub(crate) unsafe fn __wfe() {} + +#[inline(always)] +pub(crate) unsafe fn __wfi() {} + +/// Semihosting syscall. +#[inline(always)] +pub(crate) unsafe fn __sh_syscall(mut _nr: u32, _arg: u32) -> u32 { + 0 +} + +/// Set CONTROL.SPSEL to 0, write `msp` to MSP, branch to `rv`. +#[inline(always)] +pub(crate) unsafe fn __bootstrap(_msp: u32, _rv: u32) -> ! { + unimplemented!() +} + +pub(crate) use v7m::*; + +mod v7m { + #[inline(always)] + pub(crate) unsafe fn __basepri_max(_val: u8) {} + + #[inline(always)] + pub(crate) unsafe fn __basepri_r() -> u8 { + 0 + } + + #[inline(always)] + pub(crate) unsafe fn __basepri_w(_val: u8) {} + + #[inline(always)] + pub(crate) unsafe fn __faultmask_r() -> u32 { + 0 + } + + // Should this be safe? + #[inline(always)] + pub(crate) unsafe fn __enable_icache() {} + + // Should this be safe? + #[inline(always)] + pub(crate) unsafe fn __enable_dcache() {} +} diff --git a/cortex-m/src/call_asm.rs b/cortex-m/src/call_asm.rs deleted file mode 100644 index be5699945..000000000 --- a/cortex-m/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"))] - () => { - unsafe extern "C" { - fn $func($($args: $tys),*) $(-> $ret)?; - } - - $func($($args),*) - }, - } - } - }}; -} diff --git a/cortex-m/src/interrupt.rs b/cortex-m/src/interrupt.rs index a5c4b414c..939c669e8 100644 --- a/cortex-m/src/interrupt.rs +++ b/cortex-m/src/interrupt.rs @@ -35,7 +35,7 @@ unsafe impl InterruptNumber for T { /// Disables all interrupts #[inline] pub fn disable() { - call_asm!(__cpsid()); + unsafe { crate::asm::inner::__cpsid() }; } /// Enables all the interrupts @@ -45,7 +45,7 @@ pub fn disable() { /// - Do not call this function inside an `interrupt::free` critical section #[inline] pub unsafe fn enable() { - call_asm!(__cpsie()); + unsafe { crate::asm::inner::__cpsie() }; } /// Execute closure `f` in an interrupt-free context. diff --git a/cortex-m/src/lib.rs b/cortex-m/src/lib.rs index c103322b6..caa2ecfae 100644 --- a/cortex-m/src/lib.rs +++ b/cortex-m/src/lib.rs @@ -94,11 +94,12 @@ // Don't warn about feature(asm) being stable on Rust >= 1.59.0 #![allow(stable_features)] +#[cfg(all(feature = "cm7-r0p1", not(armv7em)))] +compile_error!("The feature \"cm7-r0p1\" is only compatible with the armv7em target"); + extern crate bare_metal; extern crate volatile_register; -#[macro_use] -mod call_asm; #[macro_use] mod macros; diff --git a/cortex-m/src/peripheral/scb.rs b/cortex-m/src/peripheral/scb.rs index 81e1b8b5d..6e13abf1d 100644 --- a/cortex-m/src/peripheral/scb.rs +++ b/cortex-m/src/peripheral/scb.rs @@ -316,17 +316,11 @@ impl SCB { // Invalidate I-cache cbp.iciallu(); - // Enable I-cache - unsafe extern "C" { - // see asm-v7m.s - fn __enable_icache(); - } - // NOTE(unsafe): The asm routine manages exclusive access to the SCB // registers and applies the proper barriers; it is technically safe on // its own, and is only `unsafe` here because it's `extern "C"`. unsafe { - __enable_icache(); + crate::asm::inner::__enable_icache(); } } @@ -391,17 +385,11 @@ impl SCB { // Invalidate anything currently in the D-cache unsafe { self.invalidate_dcache(cpuid) }; - // Now turn on the D-cache - unsafe extern "C" { - // see asm-v7m.s - fn __enable_dcache(); - } - // NOTE(unsafe): The asm routine manages exclusive access to the SCB // registers and applies the proper barriers; it is technically safe on // its own, and is only `unsafe` here because it's `extern "C"`. unsafe { - __enable_dcache(); + crate::asm::inner::__enable_dcache(); } } diff --git a/cortex-m/src/register/apsr.rs b/cortex-m/src/register/apsr.rs index e83435cec..a2b720cf3 100644 --- a/cortex-m/src/register/apsr.rs +++ b/cortex-m/src/register/apsr.rs @@ -49,6 +49,6 @@ impl Apsr { /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> Apsr { - let bits: u32 = call_asm!(__apsr_r() -> u32); + let bits = unsafe { crate::asm::inner::__apsr_r() }; Apsr { bits } } diff --git a/cortex-m/src/register/basepri.rs b/cortex-m/src/register/basepri.rs index 07084cd26..1a647e4c4 100644 --- a/cortex-m/src/register/basepri.rs +++ b/cortex-m/src/register/basepri.rs @@ -3,7 +3,7 @@ /// Reads the CPU register #[inline] pub fn read() -> u8 { - call_asm!(__basepri_r() -> u8) + unsafe { crate::asm::inner::__basepri_r() } } /// Writes to the CPU register @@ -14,11 +14,11 @@ pub fn read() -> u8 { pub unsafe fn write(basepri: u8) { #[cfg(feature = "cm7-r0p1")] { - call_asm!(__basepri_w_cm7_r0p1(basepri: u8)); + unsafe { crate::asm::inner::__basepri_w_cm7_r0p1(basepri) } } #[cfg(not(feature = "cm7-r0p1"))] { - call_asm!(__basepri_w(basepri: u8)); + unsafe { crate::asm::inner::__basepri_w(basepri) } } } diff --git a/cortex-m/src/register/basepri_max.rs b/cortex-m/src/register/basepri_max.rs index cea38383d..d75597c92 100644 --- a/cortex-m/src/register/basepri_max.rs +++ b/cortex-m/src/register/basepri_max.rs @@ -11,11 +11,11 @@ pub fn write(basepri: u8) { #[cfg(feature = "cm7-r0p1")] { - call_asm!(__basepri_max_cm7_r0p1(basepri: u8)); + unsafe { crate::asm::inner::__basepri_max_cm7_r0p1(basepri) } } #[cfg(not(feature = "cm7-r0p1"))] { - call_asm!(__basepri_max(basepri: u8)); + unsafe { crate::asm::inner::__basepri_max(basepri) } } } diff --git a/cortex-m/src/register/control.rs b/cortex-m/src/register/control.rs index ceca042e9..9e9d24084 100644 --- a/cortex-m/src/register/control.rs +++ b/cortex-m/src/register/control.rs @@ -174,7 +174,7 @@ impl Fpca { /// Reads the CPU register #[inline] pub fn read() -> Control { - let bits: u32 = call_asm!(__control_r() -> u32); + let bits = unsafe { crate::asm::inner::__control_r() }; Control { bits } } @@ -182,5 +182,5 @@ pub fn read() -> Control { #[inline] pub unsafe fn write(control: Control) { let control = control.bits(); - call_asm!(__control_w(control: u32)); + unsafe { crate::asm::inner::__control_w(control) }; } diff --git a/cortex-m/src/register/faultmask.rs b/cortex-m/src/register/faultmask.rs index e57fa28df..6460c6a3f 100644 --- a/cortex-m/src/register/faultmask.rs +++ b/cortex-m/src/register/faultmask.rs @@ -26,7 +26,7 @@ impl Faultmask { /// Reads the CPU register #[inline] pub fn read() -> Faultmask { - let r: u32 = call_asm!(__faultmask_r() -> u32); + let r = unsafe { crate::asm::inner::__faultmask_r() }; if r & (1 << 0) == (1 << 0) { Faultmask::Inactive } else { diff --git a/cortex-m/src/register/fpscr.rs b/cortex-m/src/register/fpscr.rs index 68692c732..f20673140 100644 --- a/cortex-m/src/register/fpscr.rs +++ b/cortex-m/src/register/fpscr.rs @@ -293,7 +293,7 @@ impl RMode { /// Read the FPSCR register #[inline] pub fn read() -> Fpscr { - let r: u32 = call_asm!(__fpscr_r() -> u32); + let r = unsafe { crate::asm::inner::__fpscr_r() }; Fpscr::from_bits(r) } @@ -301,5 +301,5 @@ pub fn read() -> Fpscr { #[inline] pub unsafe fn write(fpscr: Fpscr) { let fpscr = fpscr.bits(); - call_asm!(__fpscr_w(fpscr: u32)); + unsafe { crate::asm::inner::__fpscr_w(fpscr) }; } diff --git a/cortex-m/src/register/lr.rs b/cortex-m/src/register/lr.rs index 0da35d9ff..94a9a55da 100644 --- a/cortex-m/src/register/lr.rs +++ b/cortex-m/src/register/lr.rs @@ -5,7 +5,7 @@ /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> u32 { - call_asm!(__lr_r() -> u32) + unsafe { crate::asm::inner::__lr_r() } } /// Writes `bits` to the CPU register @@ -17,5 +17,5 @@ pub fn read() -> u32 { #[inline] #[deprecated = "This function can't be used soundly."] pub unsafe fn write(bits: u32) { - call_asm!(__lr_w(bits: u32)); + unsafe { crate::asm::inner::__lr_w(bits) } } diff --git a/cortex-m/src/register/msp.rs b/cortex-m/src/register/msp.rs index bccc2ae82..301e1bd63 100644 --- a/cortex-m/src/register/msp.rs +++ b/cortex-m/src/register/msp.rs @@ -3,14 +3,14 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__msp_r() -> u32) + unsafe { crate::asm::inner::__msp_r() } } /// Writes `bits` to the CPU register #[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)); + unsafe { crate::asm::inner::__msp_w(bits) } } /// Reads the Non-Secure CPU register from Secure state. @@ -19,7 +19,7 @@ pub unsafe fn write(bits: u32) { #[cfg(armv8m)] #[inline] pub fn read_ns() -> u32 { - call_asm!(__msp_ns_r() -> u32) + unsafe { crate::asm::inner::__msp_ns_r() } } /// Writes `bits` to the Non-Secure CPU register from Secure state. @@ -28,5 +28,5 @@ pub fn read_ns() -> u32 { #[cfg(armv8m)] #[inline] pub unsafe fn write_ns(bits: u32) { - call_asm!(__msp_ns_w(bits: u32)); + unsafe { crate::asm::inner::__msp_ns_w(bits) }; } diff --git a/cortex-m/src/register/msplim.rs b/cortex-m/src/register/msplim.rs index ac6f9ed66..bef85ccd5 100644 --- a/cortex-m/src/register/msplim.rs +++ b/cortex-m/src/register/msplim.rs @@ -3,11 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__msplim_r() -> u32) + unsafe { crate::asm::inner::__msplim_r() } } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__msplim_w(bits: u32)) + unsafe { crate::asm::inner::__msplim_w(bits) } } diff --git a/cortex-m/src/register/pc.rs b/cortex-m/src/register/pc.rs index 0b33629ac..e3a51ef14 100644 --- a/cortex-m/src/register/pc.rs +++ b/cortex-m/src/register/pc.rs @@ -5,7 +5,7 @@ /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> u32 { - call_asm!(__pc_r() -> u32) + unsafe { crate::asm::inner::__pc_r() } } /// Writes `bits` to the CPU register @@ -13,5 +13,5 @@ pub fn read() -> u32 { /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__pc_w(bits: u32)); + unsafe { crate::asm::inner::__pc_w(bits) } } diff --git a/cortex-m/src/register/psp.rs b/cortex-m/src/register/psp.rs index 0bca22c3a..e66a5be85 100644 --- a/cortex-m/src/register/psp.rs +++ b/cortex-m/src/register/psp.rs @@ -3,11 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__psp_r() -> u32) + unsafe { crate::asm::inner::__psp_r() } } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__psp_w(bits: u32)) + unsafe { crate::asm::inner::__psp_w(bits) } } diff --git a/cortex-m/src/register/psplim.rs b/cortex-m/src/register/psplim.rs index 8ee1e9455..40cc63423 100644 --- a/cortex-m/src/register/psplim.rs +++ b/cortex-m/src/register/psplim.rs @@ -3,11 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - call_asm!(__psplim_r() -> u32) + unsafe { crate::asm::inner::__psplim_r() } } /// Writes `bits` to the CPU register #[inline] pub unsafe fn write(bits: u32) { - call_asm!(__psplim_w(bits: u32)) + unsafe { crate::asm::inner::__psplim_w(bits) } } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index ffcabba44..23c4aecb0 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -4,123 +4,7 @@ //! //! Also see the docs in `asm.rs`. -use std::collections::BTreeMap; -use std::env::current_dir; -use std::fs::{self, File}; -use std::path::Path; -use std::process::{Command, Stdio}; - -fn toolchain() -> String { - fs::read_to_string("cortex-m/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() - .join("cortex-m") - .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"); - cmd.current_dir("cortex-m"); - - println!("{:?}", cmd); - let status = cmd.status().unwrap(); - assert!(status.success()); - - let full_obj_file_path = Path::new("cortex-m").join(&obj_file); - - // Archive `target.o` -> `bin/target.a`. - let mut builder = - ar::Builder::new(File::create(format!("cortex-m/bin/{}.a", file_stub)).unwrap()); - - // Use `append`, not `append_path`, to avoid adding any filesystem metadata (modification times, - // etc.). - let file = fs::read(&full_obj_file_path).unwrap(); - builder - .append( - &ar::Header::new(obj_file.as_bytes().to_vec(), file.len() as u64), - &*file, - ) - .unwrap(); - - fs::remove_file(&full_obj_file_path).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, toolchain: Option<&str>) { let mut rustup = Command::new("rustup"); @@ -134,90 +18,6 @@ pub fn install_targets(targets: &mut dyn Iterator, 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("cortex-m/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("cortex-m/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::>(); - let after = files_after.keys().collect::>(); - 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::scb::VectActive; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 01da649bd..224be3fe4 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,20 +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 "); 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" ); diff --git a/xtask/tests/ci.rs b/xtask/tests/ci.rs index 6e4023d1c..410e59b92 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. /// @@ -98,9 +98,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"); let is_msrv = str::from_utf8(&output.stdout).unwrap().contains("1.59");