diff --git a/.travis.yml b/.travis.yml index 14e5602c..5547b782 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ branches: - trying rust: - - 1.35.0 + - 1.38.0 - stable - nightly diff --git a/README.md b/README.md index a098d987..6bd8aedd 100644 --- a/README.md +++ b/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.31 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.38 and up. It might compile with older versions but that may change in any new patch release. ## License diff --git a/asm.rs b/asm.rs deleted file mode 100644 index f5b0179b..00000000 --- a/asm.rs +++ /dev/null @@ -1,346 +0,0 @@ -//! Assembly stubs for the `cortex-m` crate. -//! -//! 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 Rust -//! file `asm.rs` 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)] - -#[no_mangle] -pub unsafe extern "C" fn __bkpt() { - asm!("bkpt"); -} - -#[no_mangle] -pub unsafe extern "C" fn __control_r() -> u32 { - let r; - asm!("mrs {}, CONTROL", out(reg) r); - r -} - -#[no_mangle] -pub unsafe extern "C" fn __control_w(w: u32) { - asm!("msr CONTROL, {}", in(reg) w); -} - -#[no_mangle] -pub unsafe extern "C" fn __cpsid() { - asm!("cpsid i"); -} - -#[no_mangle] -pub unsafe extern "C" fn __cpsie() { - asm!("cpsie i"); -} - -#[no_mangle] -pub unsafe extern "C" fn __delay(cyc: u32) { - asm!(" - 1: - nop - subs {}, #1 - bne 1b - // Branch to 1 instead of __delay does not generate R_ARM_THM_JUMP8 relocation, which breaks - // linking on the thumbv6m-none-eabi target - ", in(reg) cyc); -} - -// FIXME do we need compiler fences here or should we expect them in the caller? -#[no_mangle] -pub unsafe extern "C" fn __dmb() { - asm!("dmb 0xF"); -} - -#[no_mangle] -pub unsafe extern "C" fn __dsb() { - asm!("dsb 0xF"); -} - -#[no_mangle] -pub unsafe extern "C" fn __isb() { - asm!("isb 0xF"); -} - -#[no_mangle] -pub unsafe extern "C" fn __msp_r() -> u32 { - let r; - asm!("mrs {}, MSP", out(reg) r); - r -} - -#[no_mangle] -pub unsafe extern "C" fn __msp_w(val: u32) { - asm!("msr MSP, {}", in(reg) val); -} - -#[no_mangle] -pub unsafe extern "C" 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"); -} - -#[no_mangle] -pub unsafe extern "C" fn __primask() -> u32 { - // FIXME: rename to __primask_r - let r; - asm!("mrs {}, PRIMASK", out(reg) r); - r -} - -#[no_mangle] -pub unsafe extern "C" fn __psp_r() -> u32 { - let r; - asm!("mrs {}, PSP", out(reg) r); - r -} - -#[no_mangle] -pub unsafe extern "C" fn __psp_w(val: u32) { - asm!("msr PSP, {}", in(reg) val); -} - -#[no_mangle] -pub unsafe extern "C" fn __sev() { - asm!("sev"); -} - -#[no_mangle] -pub unsafe extern "C" fn __udf() { - asm!("udf #0"); -} - -#[no_mangle] -pub unsafe extern "C" fn __wfe() { - asm!("wfe"); -} - -#[no_mangle] -pub unsafe extern "C" fn __wfi() { - asm!("wfi"); -} - -#[cfg(armv7m)] -pub mod v7m { - #[no_mangle] - pub unsafe extern "C" fn __basepri_max(val: u8) { - asm!("msr BASEPRI_MAX, {}", in(reg) val); - } - - #[no_mangle] - pub unsafe extern "C" fn __basepri_r() -> u8 { - let r; - asm!("mrs {}, BASEPRI", out(reg) r); - r - } - - #[no_mangle] - pub unsafe extern "C" fn __basepri_w(val: u8) { - asm!("msr BASEPRI, {}", in(reg) val); - } - - #[no_mangle] - pub unsafe extern "C" fn __faultmask() -> u32 { - let r; - asm!("mrs {}, FAULTMASK", out(reg) r); - r - } - - // FIXME: compiler_fences necessary? - #[no_mangle] - pub unsafe extern "C" fn __enable_icache() { - asm!( - " - ldr r0, =0xE000ED14 @ CCR - mrs r2, PRIMASK @ save critical nesting info - cpsid i @ mask interrupts - ldr r1, [r0] @ read CCR - orr.w r1, r1, #(1 << 17) @ Set bit 17, IC - str r1, [r0] @ write it back - dsb @ ensure store completes - isb @ synchronize pipeline - msr PRIMASK, r2 @ unnest critical section - ", - out("r0") _, - out("r1") _, - out("r2") _, - ); - } - - #[no_mangle] - pub unsafe extern "C" fn __enable_dcache() { - asm!( - " - ldr r0, =0xE000ED14 @ CCR - mrs r2, PRIMASK @ save critical nesting info - cpsid i @ mask interrupts - ldr r1, [r0] @ read CCR - orr.w r1, r1, #(1 << 16) @ Set bit 16, DC - str r1, [r0] @ write it back - dsb @ ensure store completes - isb @ synchronize pipeline - msr PRIMASK, r2 @ unnest critical section - ", - out("r0") _, - out("r1") _, - out("r2") _, - ); - } -} - -#[cfg(armv7em)] -mod v7em { - #[no_mangle] - pub unsafe extern "C" fn __basepri_max_cm7_r0p1(val: u8) { - asm!( - " - mrs r1, PRIMASK - cpsid i - tst.w r1, #1 - msr BASEPRI_MAX, {} - it ne - bxne lr - cpsie i - ", - in(reg) val, - out("r1") _, - ); - } - - #[no_mangle] - pub unsafe extern "C" fn __basepri_w_cm7_r0p1(val: u8) { - asm!( - " - mrs r1, PRIMASK - cpsid i - tst.w r1, #1 - msr BASEPRI, {} - it ne - bxne lr - cpsie i - ", - in(reg) val, - out("r1") _, - ); - } -} - -/// Baseline and Mainline. -#[cfg(armv8m)] -pub mod v8m { - #[no_mangle] - pub unsafe extern "C" fn __tt(mut target: u32) -> u32 { - asm!("tt {target}, {target}", target = inout(reg) target); - target - } - - #[no_mangle] - pub unsafe extern "C" fn __ttt(mut target: u32) -> u32 { - asm!("ttt {target}, {target}", target = inout(reg) target); - target - } - - #[no_mangle] - pub unsafe extern "C" fn __tta(mut target: u32) -> u32 { - asm!("tta {target}, {target}", target = inout(reg) target); - target - } - - #[no_mangle] - pub unsafe extern "C" fn __ttat(mut target: u32) -> u32 { - asm!("ttat {target}, {target}", target = inout(reg) target); - target - } -} - -/// Mainline only. -#[cfg(armv8m_main)] -pub mod v8m_main { - #[no_mangle] - pub unsafe extern "C" fn __msplim_r() -> u32 { - let r; - asm!("mrs {}, MSPLIM", out(reg) r); - r - } - - #[no_mangle] - pub unsafe extern "C" fn __msplim_w(val: u32) { - asm!("msr MSPLIM, {}", in(reg) val); - } - - #[no_mangle] - pub unsafe extern "C" fn __psplim_r() -> u32 { - let r; - asm!("mrs {}, PSPLIM", out(reg) r); - r - } - - #[no_mangle] - pub unsafe extern "C" fn __psplim_w(val: u32) { - asm!("msr PSPLIM, {}", in(reg) val); - } -} - -/// All targets with FPU. -#[cfg(has_fpu)] -pub mod fpu { - #[no_mangle] - pub unsafe extern "C" fn __get_FPSCR() -> u32 { - let r; - asm!("vmrs {}, fpscr", out(reg) r); - r - } - - #[no_mangle] - pub unsafe extern "C" fn __set_FPSCR(val: u32) { - asm!("vmsr fpscr, {}", in(reg) val); - } -} - -/// 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] -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/asm/inline.rs b/asm/inline.rs new file mode 100644 index 00000000..67e4925f --- /dev/null +++ b/asm/inline.rs @@ -0,0 +1,345 @@ +//! 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. + +#[inline(always)] +pub unsafe fn __bkpt() { + asm!("bkpt"); +} + +#[inline(always)] +pub unsafe fn __control_r() -> u32 { + let r; + asm!("mrs {}, CONTROL", out(reg) r); + r +} + +#[inline(always)] +pub unsafe fn __control_w(w: u32) { + asm!("msr CONTROL, {}", in(reg) w); +} + +#[inline(always)] +pub unsafe fn __cpsid() { + asm!("cpsid i"); +} + +#[inline(always)] +pub unsafe fn __cpsie() { + asm!("cpsie i"); +} + +#[inline(always)] +pub unsafe fn __delay(cyc: u32) { + asm!(" + 1: + nop + subs {}, #1 + bne 1b + // Branch to 1 instead of delay does not generate R_ARM_THM_JUMP8 relocation, which breaks + // linking on the thumbv6m-none-eabi target + ", in(reg) cyc); +} + +// FIXME do we need compiler fences here or should we expect them in the caller? +#[inline(always)] +pub unsafe fn __dmb() { + asm!("dmb 0xF"); +} + +#[inline(always)] +pub unsafe fn __dsb() { + asm!("dsb 0xF"); +} + +#[inline(always)] +pub unsafe fn __isb() { + asm!("isb 0xF"); +} + +#[inline(always)] +pub unsafe fn __msp_r() -> u32 { + let r; + asm!("mrs {}, MSP", out(reg) r); + r +} + +#[inline(always)] +pub unsafe fn __msp_w(val: u32) { + asm!("msr MSP, {}", in(reg) val); +} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub unsafe fn __apsr_r() -> u32 { + let r; + asm!("mrs {}, APSR", out(reg) r); + 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"); +} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub unsafe fn __pc_r() -> u32 { + let r; + asm!("mov {}, R15", out(reg) r); + r +} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub unsafe fn __pc_w(val: u32) { + asm!("mov R15, {}", in(reg) val); +} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub unsafe fn __lr_r() -> u32 { + let r; + asm!("mov {}, R14", out(reg) r); + r +} + +// NOTE: No FFI shim, this requires inline asm. +#[inline(always)] +pub unsafe fn __lr_w(val: u32) { + asm!("mov R14, {}", in(reg) val); +} + +#[inline(always)] +pub unsafe fn __primask_r() -> u32 { + let r; + asm!("mrs {}, PRIMASK", out(reg) r); + r +} + +#[inline(always)] +pub unsafe fn __psp_r() -> u32 { + let r; + asm!("mrs {}, PSP", out(reg) r); + r +} + +#[inline(always)] +pub unsafe fn __psp_w(val: u32) { + asm!("msr PSP, {}", in(reg) val); +} + +#[inline(always)] +pub unsafe fn __sev() { + asm!("sev"); +} + +#[inline(always)] +pub unsafe fn __udf() -> ! { + asm!("udf #0", options(noreturn)); +} + +#[inline(always)] +pub unsafe fn __wfe() { + asm!("wfe"); +} + +#[inline(always)] +pub unsafe fn __wfi() { + asm!("wfi"); +} + +// v7m *AND* v8m.main, but *NOT* v8m.base +#[cfg(any(armv7m, armv8m_main))] +pub use self::v7m::*; +#[cfg(any(armv7m, armv8m_main))] +mod v7m { + #[inline(always)] + pub unsafe fn __basepri_max(val: u8) { + asm!("msr BASEPRI_MAX, {}", in(reg) val); + } + + #[inline(always)] + pub unsafe fn __basepri_r() -> u8 { + let r; + asm!("mrs {}, BASEPRI", out(reg) r); + r + } + + #[inline(always)] + pub unsafe fn __basepri_w(val: u8) { + asm!("msr BASEPRI, {}", in(reg) val); + } + + #[inline(always)] + pub unsafe fn __faultmask_r() -> u32 { + let r; + asm!("mrs {}, FAULTMASK", out(reg) r); + r + } + + // FIXME: compiler_fences necessary? + #[inline(always)] + pub unsafe fn __enable_icache() { + asm!( + " + ldr r0, =0xE000ED14 @ CCR + mrs r2, PRIMASK @ save critical nesting info + cpsid i @ mask interrupts + ldr r1, [r0] @ read CCR + orr.w r1, r1, #(1 << 17) @ Set bit 17, IC + str r1, [r0] @ write it back + dsb @ ensure store completes + isb @ synchronize pipeline + msr PRIMASK, r2 @ unnest critical section + ", + out("r0") _, + out("r1") _, + out("r2") _, + ); + } + + #[inline(always)] + pub unsafe fn __enable_dcache() { + asm!( + " + ldr r0, =0xE000ED14 @ CCR + mrs r2, PRIMASK @ save critical nesting info + cpsid i @ mask interrupts + ldr r1, [r0] @ read CCR + orr.w r1, r1, #(1 << 16) @ Set bit 16, DC + str r1, [r0] @ write it back + dsb @ ensure store completes + isb @ synchronize pipeline + msr PRIMASK, r2 @ unnest critical section + ", + out("r0") _, + out("r1") _, + out("r2") _, + ); + } +} + +#[cfg(armv7em)] +pub use self::v7em::*; +#[cfg(armv7em)] +mod v7em { + #[inline(always)] + pub unsafe fn __basepri_max_cm7_r0p1(val: u8) { + asm!( + " + mrs r1, PRIMASK + cpsid i + tst.w r1, #1 + msr BASEPRI_MAX, {} + it ne + bxne lr + cpsie i + ", + in(reg) val, + out("r1") _, + ); + } + + #[inline(always)] + pub unsafe fn __basepri_w_cm7_r0p1(val: u8) { + asm!( + " + mrs r1, PRIMASK + cpsid i + tst.w r1, #1 + msr BASEPRI, {} + it ne + bxne lr + cpsie i + ", + in(reg) val, + out("r1") _, + ); + } +} + +#[cfg(armv8m)] +pub use self::v8m::*; +/// Baseline and Mainline. +#[cfg(armv8m)] +mod v8m { + #[inline(always)] + pub unsafe fn __tt(mut target: u32) -> u32 { + asm!("tt {target}, {target}", target = inout(reg) target); + target + } + + #[inline(always)] + pub unsafe fn __ttt(mut target: u32) -> u32 { + asm!("ttt {target}, {target}", target = inout(reg) target); + target + } + + #[inline(always)] + pub unsafe fn __tta(mut target: u32) -> u32 { + asm!("tta {target}, {target}", target = inout(reg) target); + target + } + + #[inline(always)] + pub unsafe fn __ttat(mut target: u32) -> u32 { + asm!("ttat {target}, {target}", target = inout(reg) target); + target + } +} + +#[cfg(armv8m_main)] +pub use self::v8m_main::*; +/// Mainline only. +#[cfg(armv8m_main)] +mod v8m_main { + #[inline(always)] + pub unsafe fn __msplim_r() -> u32 { + let r; + asm!("mrs {}, MSPLIM", out(reg) r); + r + } + + #[inline(always)] + pub unsafe fn __msplim_w(val: u32) { + asm!("msr MSPLIM, {}", in(reg) val); + } + + #[inline(always)] + pub unsafe fn __psplim_r() -> u32 { + let r; + asm!("mrs {}, PSPLIM", out(reg) r); + r + } + + #[inline(always)] + pub unsafe fn __psplim_w(val: u32) { + asm!("msr PSPLIM, {}", in(reg) val); + } +} + +#[cfg(has_fpu)] +pub use self::fpu::*; +/// All targets with FPU. +#[cfg(has_fpu)] +mod fpu { + #[inline(always)] + pub unsafe fn __fpscr_r() -> u32 { + let r; + asm!("vmrs {}, fpscr", out(reg) r); + r + } + + #[inline(always)] + pub unsafe fn __fpscr_w(val: u32) { + asm!("vmsr fpscr, {}", in(reg) val); + } +} diff --git a/asm/lib.rs b/asm/lib.rs new file mode 100644 index 00000000..50e1db68 --- /dev/null +++ b/asm/lib.rs @@ -0,0 +1,134 @@ +//! 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)] + +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 __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(); +} + +// 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; +} + +// 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] +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 index 4d2f02cf..32667b18 100644 Binary files a/bin/thumbv6m-none-eabi-lto.a and b/bin/thumbv6m-none-eabi-lto.a differ diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a index beedd73b..5c8c7587 100644 Binary files a/bin/thumbv6m-none-eabi.a and b/bin/thumbv6m-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabi-lto.a b/bin/thumbv7em-none-eabi-lto.a index 7b8a4f87..c405e9ef 100644 Binary files a/bin/thumbv7em-none-eabi-lto.a and b/bin/thumbv7em-none-eabi-lto.a differ diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a index 588e5cdb..ec56934c 100644 Binary files a/bin/thumbv7em-none-eabi.a and b/bin/thumbv7em-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabihf-lto.a b/bin/thumbv7em-none-eabihf-lto.a index 4efadd80..bd5ad837 100644 Binary files a/bin/thumbv7em-none-eabihf-lto.a and b/bin/thumbv7em-none-eabihf-lto.a differ diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a index fd08ba59..75a7e5dc 100644 Binary files a/bin/thumbv7em-none-eabihf.a and b/bin/thumbv7em-none-eabihf.a differ diff --git a/bin/thumbv7m-none-eabi-lto.a b/bin/thumbv7m-none-eabi-lto.a index 28e5860a..a95b0699 100644 Binary files a/bin/thumbv7m-none-eabi-lto.a and b/bin/thumbv7m-none-eabi-lto.a differ diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a index 4559ad4a..ae9db609 100644 Binary files a/bin/thumbv7m-none-eabi.a and b/bin/thumbv7m-none-eabi.a differ diff --git a/bin/thumbv8m.base-none-eabi-lto.a b/bin/thumbv8m.base-none-eabi-lto.a index ee467925..dd7553ed 100644 Binary files a/bin/thumbv8m.base-none-eabi-lto.a and b/bin/thumbv8m.base-none-eabi-lto.a differ diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a index 26d25bd3..e8bb7a6a 100644 Binary files a/bin/thumbv8m.base-none-eabi.a and b/bin/thumbv8m.base-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabi-lto.a b/bin/thumbv8m.main-none-eabi-lto.a index e3aa0cf1..1f5b29dd 100644 Binary files a/bin/thumbv8m.main-none-eabi-lto.a and b/bin/thumbv8m.main-none-eabi-lto.a differ diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a index 10bbf53f..af9179ab 100644 Binary files a/bin/thumbv8m.main-none-eabi.a and b/bin/thumbv8m.main-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabihf-lto.a b/bin/thumbv8m.main-none-eabihf-lto.a index 1c25494b..c243d5d4 100644 Binary files a/bin/thumbv8m.main-none-eabihf-lto.a and b/bin/thumbv8m.main-none-eabihf-lto.a differ diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a index 06517880..3189a29c 100644 Binary files a/bin/thumbv8m.main-none-eabihf.a and b/bin/thumbv8m.main-none-eabihf.a differ diff --git a/src/asm.rs b/src/asm.rs index 39cd8efd..3165acab 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -1,27 +1,18 @@ //! 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; + /// 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. #[inline(always)] pub fn bkpt() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("bkpt" :::: "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __bkpt(); - } - - __bkpt(); - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__bkpt()); } /// Blocks the program for *at least* `n` instruction cycles @@ -29,151 +20,47 @@ pub fn bkpt() { /// This is implemented in assembly so its execution time is independent of the optimization /// level, however it is dependent on the specific architecture and core configuration. /// -/// NOTE that the delay can take much longer if interrupts are serviced during its execution +/// NOTE that the delay can take much longer if interrupts are serviced during its execution /// 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. #[inline] -pub fn delay(_n: u32) { +pub fn delay(n: u32) { // NOTE(divide by 4) is easier to compute than `/ 3` because it's just a shift (`>> 2`). - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { - llvm_asm!("1: - nop - subs $0, $$1 - bne.n 1b" - : "+r"(_n / 4 + 1) - : - : "cpsr" - : "volatile"); - }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __delay(n: u32); - } - - __delay(_n / 4 + 1); - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let real_cyc = n / 4 + 1; + call_asm!(__delay(real_cyc: u32)); } /// A no-operation. Useful to prevent delay loops from being optimized away. #[inline] pub fn nop() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("nop" :::: "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __nop(); - } - - __nop() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__nop()); } - /// Generate an Undefined Instruction exception. /// /// Can be used as a stable alternative to `core::intrinsics::abort`. #[inline] pub fn udf() -> ! { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { - llvm_asm!("udf" :::: "volatile"); - core::hint::unreachable_unchecked(); - }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __udf(); - } - - __udf(); - - core::hint::unreachable_unchecked(); - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__udf() -> !) } /// Wait For Event #[inline] pub fn wfe() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("wfe" :::: "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __wfe(); - } - - __wfe() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__wfe()) } /// Wait For Interrupt #[inline] pub fn wfi() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("wfi" :::: "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __wfi(); - } - - __wfi() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__wfi()) } /// Send Event #[inline] pub fn sev() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("sev" :::: "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __sev(); - } - - __sev() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__sev()) } /// Instruction Synchronization Barrier @@ -182,23 +69,7 @@ pub fn sev() { /// from cache or memory, after the instruction has been completed. #[inline] pub fn isb() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("isb 0xF" ::: "memory" : "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __isb(); - } - - __isb() - // XXX do we need a explicit compiler barrier here? - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__isb()) } /// Data Synchronization Barrier @@ -210,23 +81,7 @@ pub fn isb() { /// * all cache and branch predictor maintenance operations before this instruction complete #[inline] pub fn dsb() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("dsb 0xF" ::: "memory" : "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __dsb(); - } - - __dsb() - // XXX do we need a explicit compiler barrier here? - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__dsb()) } /// Data Memory Barrier @@ -236,23 +91,7 @@ pub fn dsb() { /// after the `DMB` instruction. #[inline] pub fn dmb() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { llvm_asm!("dmb 0xF" ::: "memory" : "volatile") }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __dmb(); - } - - __dmb() - // XXX do we need a explicit compiler barrier here? - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__dmb()) } /// Test Target @@ -265,28 +104,8 @@ 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 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let tt_resp: u32; - unsafe { - llvm_asm!("tt $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile"); - } - tt_resp - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __tt(_: *mut u32) -> u32; - } - - __tt(addr) - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let addr = addr as u32; + call_asm!(__tt(addr: u32) -> u32) } /// Test Target Unprivileged @@ -300,28 +119,8 @@ 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 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let tt_resp: u32; - unsafe { - llvm_asm!("ttt $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile"); - } - tt_resp - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __ttt(_: *mut u32) -> u32; - } - - __ttt(addr) - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let addr = addr as u32; + call_asm!(__ttt(addr: u32) -> u32) } /// Test Target Alternate Domain @@ -336,28 +135,8 @@ 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 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let tt_resp: u32; - unsafe { - llvm_asm!("tta $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile"); - } - tt_resp - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __tta(_: *mut u32) -> u32; - } - - __tta(addr) - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let addr = addr as u32; + call_asm!(__tta(addr: u32) -> u32) } /// Test Target Alternate Domain Unprivileged @@ -372,26 +151,6 @@ 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 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let tt_resp: u32; - unsafe { - llvm_asm!("ttat $0, $1" : "=r"(tt_resp) : "r"(addr) :: "volatile"); - } - tt_resp - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __ttat(_: *mut u32) -> u32; - } - - __ttat(addr) - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let addr = addr as u32; + call_asm!(__ttat(addr: u32) -> u32) } diff --git a/src/call_asm.rs b/src/call_asm.rs new file mode 100644 index 00000000..295277f3 --- /dev/null +++ b/src/call_asm.rs @@ -0,0 +1,24 @@ +/// 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 c5da48dd..68719ecf 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -26,25 +26,7 @@ pub unsafe trait InterruptNumber: Copy { /// Disables all interrupts #[inline] pub fn disable() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { - llvm_asm!("cpsid i" ::: "memory" : "volatile"); - }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __cpsid(); - } - - // XXX do we need a explicit compiler barrier here? - __cpsid(); - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__cpsid()); } /// Enables all the interrupts @@ -54,23 +36,7 @@ pub fn disable() { /// - Do not call this function inside an `interrupt::free` critical section #[inline] pub unsafe fn enable() { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => llvm_asm!("cpsie i" ::: "memory" : "volatile"), - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __cpsie(); - } - - // XXX do we need a explicit compiler barrier here? - __cpsie(); - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__cpsie()); } /// Execute closure `f` in an interrupt-free context. diff --git a/src/lib.rs b/src/lib.rs index 723816aa..f30408e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ //! ## `inline-asm` //! //! When this feature is enabled the implementation of all the functions inside the `asm` and -//! `register` modules use inline assembly (`llvm_asm!`) instead of external assembly (FFI into separate +//! `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: //! @@ -52,17 +52,16 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.31 and up. It *might* +//! This crate is guaranteed to compile on stable Rust 1.38 and up. It *might* //! compile with older versions but that may change in any new patch release. -#![cfg_attr(feature = "inline-asm", feature(llvm_asm))] +#![cfg_attr(feature = "inline-asm", feature(asm))] #![deny(missing_docs)] #![no_std] #![allow(clippy::identity_op)] #![allow(clippy::missing_safety_doc)] // Prevent clippy from complaining about empty match expression that are used for cfg gating. #![allow(clippy::match_single_binding)] - // This makes clippy warn about public functions which are not #[inline]. // // Almost all functions in this crate result in trivial or even no assembly. @@ -81,6 +80,8 @@ extern crate bare_metal; extern crate volatile_register; +#[macro_use] +mod call_asm; #[macro_use] mod macros; diff --git a/src/register/apsr.rs b/src/register/apsr.rs index 3db8aebe..b81d8925 100644 --- a/src/register/apsr.rs +++ b/src/register/apsr.rs @@ -50,17 +50,6 @@ impl Apsr { /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> Apsr { - match () { - #[cfg(cortex_m)] - () => { - let r: u32; - unsafe { - llvm_asm!("mrs $0, APSR" : "=r"(r) ::: "volatile"); - } - Apsr { bits: r } - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let bits: u32 = call_asm!(__apsr_r() -> u32); + Apsr { bits } } diff --git a/src/register/basepri.rs b/src/register/basepri.rs index 6caf9386..07084cd2 100644 --- a/src/register/basepri.rs +++ b/src/register/basepri.rs @@ -3,28 +3,7 @@ /// Reads the CPU register #[inline] pub fn read() -> u8 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let r: u32; - unsafe { - llvm_asm!("mrs $0, BASEPRI" : "=r"(r) ::: "volatile"); - } - r as u8 - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __basepri_r() -> u8; - } - - __basepri_r() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__basepri_r() -> u8) } /// Writes to the CPU register @@ -32,39 +11,14 @@ pub fn read() -> u8 { /// **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. #[inline] -pub unsafe fn write(_basepri: u8) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => match () { - #[cfg(not(feature = "cm7-r0p1"))] - () => llvm_asm!("msr BASEPRI, $0" :: "r"(_basepri) : "memory" : "volatile"), - #[cfg(feature = "cm7-r0p1")] - () => crate::interrupt::free( - |_| llvm_asm!("msr BASEPRI, $0" :: "r"(_basepri) : "memory" : "volatile"), - ), - }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => match () { - #[cfg(not(feature = "cm7-r0p1"))] - () => { - extern "C" { - fn __basepri_w(_: u8); - } - - __basepri_w(_basepri); - } - #[cfg(feature = "cm7-r0p1")] - () => { - extern "C" { - fn __basepri_w_cm7_r0p1(_: u8); - } - - __basepri_w_cm7_r0p1(_basepri); - } - }, +pub unsafe fn write(basepri: u8) { + #[cfg(feature = "cm7-r0p1")] + { + call_asm!(__basepri_w_cm7_r0p1(basepri: u8)); + } - #[cfg(not(cortex_m))] - () => unimplemented!(), + #[cfg(not(feature = "cm7-r0p1"))] + { + call_asm!(__basepri_w(basepri: u8)); } } diff --git a/src/register/basepri_max.rs b/src/register/basepri_max.rs index 0e66f691..cea38383 100644 --- a/src/register/basepri_max.rs +++ b/src/register/basepri_max.rs @@ -8,43 +8,14 @@ /// **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. #[inline] -pub fn write(_basepri: u8) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => unsafe { - match () { - #[cfg(not(feature = "cm7-r0p1"))] - () => llvm_asm!("msr BASEPRI_MAX, $0" :: "r"(_basepri) : "memory" : "volatile"), - #[cfg(feature = "cm7-r0p1")] - () => crate::interrupt::free( - |_| llvm_asm!("msr BASEPRI_MAX, $0" :: "r"(_basepri) : "memory" : "volatile"), - ), - } - }, - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - match () { - #[cfg(not(feature = "cm7-r0p1"))] - () => { - extern "C" { - fn __basepri_max(_: u8); - } - - __basepri_max(_basepri) - } - #[cfg(feature = "cm7-r0p1")] - () => { - extern "C" { - fn __basepri_max_cm7_r0p1(_: u8); - } - - __basepri_max_cm7_r0p1(_basepri) - } - } - }, +pub fn write(basepri: u8) { + #[cfg(feature = "cm7-r0p1")] + { + call_asm!(__basepri_max_cm7_r0p1(basepri: u8)); + } - #[cfg(not(cortex_m))] - () => unimplemented!(), + #[cfg(not(feature = "cm7-r0p1"))] + { + call_asm!(__basepri_max(basepri: u8)); } } diff --git a/src/register/control.rs b/src/register/control.rs index 211b5327..938b10ff 100644 --- a/src/register/control.rs +++ b/src/register/control.rs @@ -156,58 +156,13 @@ impl Fpca { /// Reads the CPU register #[inline] pub fn read() -> Control { - match () { - #[cfg(cortex_m)] - () => { - let r = match () { - #[cfg(feature = "inline-asm")] - () => { - let r: u32; - unsafe { llvm_asm!("mrs $0, CONTROL" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(not(feature = "inline-asm"))] - () => unsafe { - extern "C" { - fn __control_r() -> u32; - } - - __control_r() - }, - }; - - Control { bits: r } - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let bits: u32 = call_asm!(__control_r() -> u32); + Control { bits } } /// Writes to the CPU register. #[inline] -pub unsafe fn write(_control: Control) { - match () { - #[cfg(cortex_m)] - () => match () { - #[cfg(feature = "inline-asm")] - () => { - let control = _control.bits(); - llvm_asm!("msr CONTROL, $0" :: "r"(control) : "memory" : "volatile"); - } - - #[cfg(not(feature = "inline-asm"))] - () => { - extern "C" { - fn __control_w(bits: u32); - } - - __control_w(_control.bits()); - } - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(control: Control) { + let control = control.bits(); + call_asm!(__control_w(control: u32)); } diff --git a/src/register/faultmask.rs b/src/register/faultmask.rs index 06f60fe7..1f19d97c 100644 --- a/src/register/faultmask.rs +++ b/src/register/faultmask.rs @@ -27,36 +27,10 @@ impl Faultmask { /// Reads the CPU register #[inline] pub fn read() -> Faultmask { - match () { - #[cfg(cortex_m)] - () => { - let r = match () { - #[cfg(feature = "inline-asm")] - () => { - let r: u32; - unsafe { llvm_asm!("mrs $0, FAULTMASK" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(not(feature = "inline-asm"))] - () => unsafe { - extern "C" { - fn __faultmask() -> u32; - - } - - __faultmask() - }, - }; - - if r & (1 << 0) == (1 << 0) { - Faultmask::Inactive - } else { - Faultmask::Active - } - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), + let r: u32 = call_asm!(__faultmask_r() -> u32); + if r & (1 << 0) == (1 << 0) { + Faultmask::Inactive + } else { + Faultmask::Active } } diff --git a/src/register/fpscr.rs b/src/register/fpscr.rs index 2ca00e1a..dd538e96 100644 --- a/src/register/fpscr.rs +++ b/src/register/fpscr.rs @@ -295,49 +295,13 @@ impl RMode { /// Read the FPSCR register #[inline] pub fn read() -> Fpscr { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let r: u32; - unsafe { - llvm_asm!("vmrs $0, fpscr" : "=r"(r) ::: "volatile"); - } - Fpscr::from_bits(r) - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __get_FPSCR() -> u32; - } - Fpscr::from_bits(__get_FPSCR()) - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + let r: u32 = call_asm!(__fpscr_r() -> u32); + Fpscr::from_bits(r) } /// Set the value of the FPSCR register #[inline] -pub unsafe fn write(_fspcr: Fpscr) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let bits = _fspcr.bits(); - llvm_asm!("vmsr fpscr, $0" :: "r"(bits) :: "volatile"); - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __set_FPSCR(bits: u32); - } - - __set_FPSCR(_fspcr.bits()); - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(fpscr: Fpscr) { + let fpscr = fpscr.bits(); + call_asm!(__fpscr_w(fpscr: u32)); } diff --git a/src/register/lr.rs b/src/register/lr.rs index 6919e107..1aa546c8 100644 --- a/src/register/lr.rs +++ b/src/register/lr.rs @@ -5,29 +5,13 @@ /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> u32 { - match () { - #[cfg(cortex_m)] - () => { - let r: u32; - unsafe { llvm_asm!("mov $0,R14" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__lr_r() -> u32) } /// Writes `bits` to the CPU register /// /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] -pub unsafe fn write(_bits: u32) { - match () { - #[cfg(cortex_m)] - () => llvm_asm!("mov R14,$0" :: "r"(_bits) :: "volatile"), - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(bits: u32) { + call_asm!(__lr_w(bits: u32)); } diff --git a/src/register/mod.rs b/src/register/mod.rs index efbe6ef5..48d157a5 100644 --- a/src/register/mod.rs +++ b/src/register/mod.rs @@ -58,11 +58,11 @@ pub mod psplim; // Accessing these registers requires inline assembly because their contents are tied to the current // stack frame -#[cfg(any(feature = "inline-asm", target_arch = "x86_64"))] +#[cfg(feature = "inline-asm")] pub mod apsr; -#[cfg(any(feature = "inline-asm", target_arch = "x86_64"))] +#[cfg(feature = "inline-asm")] pub mod lr; -#[cfg(any(feature = "inline-asm", target_arch = "x86_64"))] +#[cfg(feature = "inline-asm")] pub mod pc; diff --git a/src/register/msp.rs b/src/register/msp.rs index b5460ed0..275023d3 100644 --- a/src/register/msp.rs +++ b/src/register/msp.rs @@ -3,45 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let r; - unsafe { llvm_asm!("mrs $0,MSP" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __msp_r() -> u32; - } - - __msp_r() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__msp_r() -> u32) } /// Writes `bits` to the CPU register #[inline] -pub unsafe fn write(_bits: u32) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => llvm_asm!("msr MSP,$0" :: "r"(_bits) :: "volatile"), - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __msp_w(_: u32); - } - - __msp_w(_bits); - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(bits: u32) { + call_asm!(__msp_w(bits: u32)); } diff --git a/src/register/msplim.rs b/src/register/msplim.rs index 68915c4a..ac6f9ed6 100644 --- a/src/register/msplim.rs +++ b/src/register/msplim.rs @@ -3,45 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let r; - unsafe { llvm_asm!("mrs $0,MSPLIM" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __msplim_r() -> u32; - } - - __msplim_r() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__msplim_r() -> u32) } /// Writes `bits` to the CPU register #[inline] -pub unsafe fn write(_bits: u32) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => llvm_asm!("msr MSPLIM,$0" :: "r"(_bits) :: "volatile"), - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __msplim_w(_: u32); - } - - __msplim_w(_bits); - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(bits: u32) { + call_asm!(__msplim_w(bits: u32)) } diff --git a/src/register/pc.rs b/src/register/pc.rs index f4486c49..0b33629a 100644 --- a/src/register/pc.rs +++ b/src/register/pc.rs @@ -5,29 +5,13 @@ /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] pub fn read() -> u32 { - match () { - #[cfg(cortex_m)] - () => { - let r; - unsafe { llvm_asm!("mov $0,R15" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__pc_r() -> u32) } /// Writes `bits` to the CPU register /// /// **NOTE** This function is available if `cortex-m` is built with the `"inline-asm"` feature. #[inline] -pub unsafe fn write(_bits: u32) { - match () { - #[cfg(cortex_m)] - () => llvm_asm!("mov R15,$0" :: "r"(_bits) :: "volatile"), - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(bits: u32) { + call_asm!(__pc_w(bits: u32)); } diff --git a/src/register/primask.rs b/src/register/primask.rs index 4b6df3c8..20692a2f 100644 --- a/src/register/primask.rs +++ b/src/register/primask.rs @@ -27,35 +27,14 @@ impl Primask { /// Reads the CPU register #[inline] pub fn read() -> Primask { - match () { - #[cfg(cortex_m)] - () => { - let r = match () { - #[cfg(feature = "inline-asm")] - () => { - let r: u32; - unsafe { llvm_asm!("mrs $0, PRIMASK" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(not(feature = "inline-asm"))] - () => { - extern "C" { - fn __primask() -> u32; - } - - unsafe { __primask() } - } - }; - - if r & (1 << 0) == (1 << 0) { - Primask::Inactive - } else { - Primask::Active - } - } + fn read_raw() -> u32 { + call_asm!(__primask_r() -> u32) + } - #[cfg(not(cortex_m))] - () => unimplemented!(), + let r = read_raw(); + if r & (1 << 0) == (1 << 0) { + Primask::Inactive + } else { + Primask::Active } } diff --git a/src/register/psp.rs b/src/register/psp.rs index c020e4f9..0bca22c3 100644 --- a/src/register/psp.rs +++ b/src/register/psp.rs @@ -3,45 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let r; - unsafe { llvm_asm!("mrs $0,PSP" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __psp_r() -> u32; - } - - __psp_r() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__psp_r() -> u32) } /// Writes `bits` to the CPU register #[inline] -pub unsafe fn write(_bits: u32) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => llvm_asm!("msr PSP,$0" :: "r"(_bits) :: "volatile"), - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __psp_w(_: u32); - } - - __psp_w(_bits); - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(bits: u32) { + call_asm!(__psp_w(bits: u32)) } diff --git a/src/register/psplim.rs b/src/register/psplim.rs index 8cb8f1c7..8ee1e945 100644 --- a/src/register/psplim.rs +++ b/src/register/psplim.rs @@ -3,45 +3,11 @@ /// Reads the CPU register #[inline] pub fn read() -> u32 { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => { - let r; - unsafe { llvm_asm!("mrs $0,PSPLIM" : "=r"(r) ::: "volatile") } - r - } - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => unsafe { - extern "C" { - fn __psplim_r() -> u32; - } - - __psplim_r() - }, - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } + call_asm!(__psplim_r() -> u32) } /// Writes `bits` to the CPU register #[inline] -pub unsafe fn write(_bits: u32) { - match () { - #[cfg(all(cortex_m, feature = "inline-asm"))] - () => llvm_asm!("msr PSPLIM,$0" :: "r"(_bits) :: "volatile"), - - #[cfg(all(cortex_m, not(feature = "inline-asm")))] - () => { - extern "C" { - fn __psplim_w(_: u32); - } - - __psplim_w(_bits); - } - - #[cfg(not(cortex_m))] - () => unimplemented!(), - } +pub unsafe fn write(bits: u32) { + call_asm!(__psplim_w(bits: u32)) } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 06c51435..e23d1a71 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -11,3 +11,7 @@ harness = false [dependencies] ar = "0.8.0" + +[dependencies.object] +version = "0.21.1" +features = ["write"] diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 4575f65e..3ff66e8f 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -4,12 +4,16 @@ //! //! Also see the docs in `asm.rs`. -use process::Stdio; +use object::{ + read::{Object as _, ObjectSection as _}, + write::{Object, Symbol, SymbolSection}, + SymbolFlags, +}; use std::env::current_dir; use std::{ collections::BTreeMap, fs::{self, File}, - process::{self, Command}, + process::{Command, Stdio}, }; fn toolchain() -> String { @@ -25,6 +29,88 @@ fn rustc() -> Command { cmd } +/// Patches an object file so that it doesn't contain a panic handler. +/// +/// The panic handler defined in `asm/lib.rs` should never get linked to the final program. +/// Unfortunately, Rust uses the same symbol for all panic handlers, and doesn't really like it if +/// that ends up with multiple ones. It also demands that we define a panic handler for the inline +/// assembly shim, even though none of that code should ever be able to panic. The result of this is +/// that the supposedly unreachable panic handler does end up getting linked into the final program, +/// unless it is built with optimizations enabled. +/// +/// To fix that, we put the never-to-be-used panic handler into its own section via +/// `#[link_section]`, and then use this function to delete that section. +fn trim_panic_handler(obj_file: &str) { + let objdata = fs::read(&obj_file).unwrap(); + let obj = object::File::parse(&objdata).unwrap(); + + let mut writer = Object::new(obj.format(), obj.architecture(), obj.endianness()); + for (sec_index, section) in obj.sections().enumerate() { + assert_eq!(section.index().0, sec_index); + + let name = section.name().unwrap(); + if name.starts_with(".ARM") + || name.starts_with(".rel.ARM") + || name.contains("cortex_m_asm_panic") + || name == ".strtab" + || name == ".symtab" + { + // We drop the ARM exception handling tables since they refer back to the panic handler + // symbol. They aren't used either way. We also drop `.strtab` and `.symtab` since they + // otherwise end up having the wrong section type. The object crate should rebuild any + // index tables when writing the file. + continue; + } + + let segment = section + .segment_name() + .unwrap() + .map(|s| s.as_bytes()) + .unwrap_or(&[]); + let sec_id = writer.add_section(segment.to_vec(), name.as_bytes().to_vec(), section.kind()); + + let align = if section.align() == 0 { + // Not sure why but `section.align()` can return 0. + 1 + } else { + section.align() + }; + writer.append_section_data(sec_id, section.data().unwrap(), align); + + // Import all symbols from the section. + for (_sym_idx, symbol) in obj.symbols() { + if symbol.section_index() == Some(section.index()) { + writer.add_symbol(Symbol { + name: symbol.name().unwrap_or("").as_bytes().to_vec(), + value: symbol.address(), + size: symbol.size(), + kind: symbol.kind(), + scope: symbol.scope(), + weak: symbol.is_weak(), + section: match symbol.section() { + object::SymbolSection::Unknown => unimplemented!(), + object::SymbolSection::None => SymbolSection::None, + object::SymbolSection::Undefined => SymbolSection::Undefined, + object::SymbolSection::Absolute => SymbolSection::Absolute, + object::SymbolSection::Common => SymbolSection::Common, + object::SymbolSection::Section(_) => SymbolSection::Section(sec_id), + }, + flags: match symbol.flags() { + SymbolFlags::None => SymbolFlags::None, + SymbolFlags::Elf { st_info, st_other } => { + SymbolFlags::Elf { st_info, st_other } + } + _ => unimplemented!(), + }, + }); + } + } + } + + let obj = writer.write().unwrap(); + fs::write(&obj_file, obj).unwrap(); +} + fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { let mut cmd = rustc(); @@ -68,12 +154,17 @@ fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) { // Pass output and input file. cmd.arg("-o").arg(&obj_file); - cmd.arg("asm.rs"); + cmd.arg("asm/lib.rs"); println!("{:?}", cmd); let status = cmd.status().unwrap(); assert!(status.success()); + if !plugin_lto { + // Post-process the object file. + trim_panic_handler(&obj_file); + } + // Archive `target.o` -> `bin/target.a`. let mut builder = ar::Builder::new(File::create(format!("bin/{}.a", file_stub)).unwrap()); @@ -198,7 +289,10 @@ pub fn check_blobs() { for ((file, before), (_, after)) in files_before.iter().zip(files_after.iter()) { if before != after { - panic!("{} differs between rebuilds", file); + panic!( + "{} is not up-to-date, please run `cargo xtask assemble`", + file + ); } }