diff --git a/Cargo.toml b/Cargo.toml index fa5e775c..bbb6254b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ autoexamples = true links = "cortex-m-rt" # Prevent multiple versions of cortex-m-rt being linked [dependencies] -r0 = "1.0" cortex-m-rt-macros = { path = "macros", version = "=0.6.11" } # Note: Do not depend on `cortex-m` here. This crate is used for testing `cortex-m`, so we need to # avoid pulling in multiple versions of `cortex-m`. diff --git a/asm.S b/asm.S new file mode 100644 index 00000000..62541298 --- /dev/null +++ b/asm.S @@ -0,0 +1,105 @@ + .cfi_sections .debug_frame + + # Notes for function attributes: + # .type and .thumb_func are _both_ required, otherwise the Thumb mode bit + # will not be set and an invalid vector table is generated. + # LLD requires that section flags are set explicitly. + + .section .HardFaultTrampoline, "ax" + .global HardFaultTrampoline + .type HardFaultTrampoline,%function + .thumb_func + .cfi_startproc + # HardFault exceptions are bounced through this trampoline which grabs the + # stack pointer at the time of the exception and passes it to the user's + # HardFault handler in r0. +HardFaultTrampoline: + # Depending on the stack mode in EXC_RETURN, fetch stack pointer from + # PSP or MSP. + mov r0, lr + mov r1, #4 + tst r0, r1 + bne 0f + mrs r0, MSP + b HardFault +0: + mrs r0, PSP + b HardFault + .cfi_endproc + .size HardFaultTrampoline, . - HardFaultTrampoline + + .section .Reset, "ax" + .global Reset + .type Reset,%function + .thumb_func + .cfi_startproc + # Main entry point after reset. This jumps to the user __pre_init function, + # which cannot be called from Rust code without invoking UB, then + # initialises RAM. If the target has an FPU, it is enabled. Finally, jumps + # to the user main function. +Reset: + # ARMv6-M does not initialise LR, but many tools expect it to be 0xFFFF_FFFF + # when reaching the first call frame, so we set it at startup. + # ARMv7-M and above initialise LR to 0xFFFF_FFFF at reset. + ldr r4,=0xffffffff + mov lr,r4 + + # Run user pre-init code, which must be executed immediately after startup, + # before the potentially time-consuming memory initialisation takes place. + # Example use cases include disabling default watchdogs or enabling RAM. + bl __pre_init + + # Restore LR after calling __pre_init (r4 is preserved by subroutines). + mov lr,r4 + + # Initialise .bss memory. `__sbss` and `__ebss` come from the linker script. + ldr r0,=__sbss + ldr r1,=__ebss + mov r2,#0 +0: + cmp r1, r0 + beq 1f + stm r0!, {r2} + b 0b +1: + + # Initialise .data memory. `__sdata`, `__sidata`, and `__edata` come from the + # linker script. Copy from r2 into r0 until r0 reaches r1. + ldr r0,=__sdata + ldr r1,=__edata + ldr r2,=__sidata +2: + cmp r1, r0 + beq 3f + # load 1 word from r2 to r3, inc r2 + ldm r2!, {r3} + # store 1 word from r3 to r0, inc r0 + stm r0!, {r3} + b 2b +3: + +#ifdef HAS_FPU + # Conditionally enable the FPU. + # Address of SCB.CPACR. + ldr r0, =0xE000ED88 + # Enable access to CP10 and CP11 from both privileged and unprivileged mode. + ldr r1, =(0b1111 << 20) + # RMW. + ldr r2, [r0] + orr r2, r2, r1 + str r2, [r0] + # Barrier is required on some processors. + dsb + isb +#endif + +4: + # Jump to user main function. We use bl for the extended range, but the + # user main function may not return. + bl main + + # Trap on return. + udf + + .cfi_endproc + .size Reset, . - Reset diff --git a/asm.s b/asm.s deleted file mode 100644 index 58ed2741..00000000 --- a/asm.s +++ /dev/null @@ -1,67 +0,0 @@ - .cfi_sections .debug_frame - - # LLD requires that the section flags are explicitly set here - .section .HardFaultTrampoline, "ax" - .global HardFaultTrampoline - # .type and .thumb_func are both required; otherwise its Thumb bit does not - # get set and an invalid vector table is generated - .type HardFaultTrampoline,%function - .thumb_func - .cfi_startproc -HardFaultTrampoline: - # depending on the stack mode in EXC_RETURN, fetch stack pointer from - # PSP or MSP - mov r0, lr - mov r1, #4 - tst r0, r1 - bne 0f - mrs r0, MSP - b HardFault -0: - mrs r0, PSP - b HardFault - .cfi_endproc - .size HardFaultTrampoline, . - HardFaultTrampoline - - .section .text.FpuTrampoline, "ax" - .global FpuTrampoline - # .type and .thumb_func are both required; otherwise its Thumb bit does not - # get set and an invalid vector table is generated - .type FpuTrampoline,%function - .thumb_func - .cfi_startproc - # This enables the FPU and jumps to the main function. -FpuTrampoline: - # Address of SCB.CPACR. - ldr r0, =0xE000ED88 - # Enable access to CP10 and CP11 from both privileged and unprivileged mode. - ldr r1, =(0b1111 << 20) - # RMW. - ldr r2, [r0] - orr r2, r2, r1 - str r2, [r0] - # Barrier is required on some processors. - dsb - isb - # Hand execution over to `main`. - bl main - # Note: `main` must not return. `bl` is used only because it has a wider range than `b`. - .cfi_endproc - .size FpuTrampoline, . - FpuTrampoline - - # ARMv6-M leaves LR in an unknown state on Reset - # this trampoline sets LR before it's pushed onto the stack by Reset - .section .PreResetTrampoline, "ax" - .global PreResetTrampoline - # .type and .thumb_func are both required; otherwise its Thumb bit does not - # get set and an invalid vector table is generated - .type PreResetTrampoline,%function - .thumb_func - .cfi_startproc -PreResetTrampoline: - # set LR to the initial value used by the ARMv7-M (0xFFFF_FFFF) - ldr r0,=0xffffffff - mov lr,r0 - b Reset - .cfi_endproc - .size PreResetTrampoline, . - PreResetTrampoline diff --git a/assemble.sh b/assemble.sh index cdb32054..b914fedd 100755 --- a/assemble.sh +++ b/assemble.sh @@ -9,21 +9,25 @@ crate=cortex-m-rt # remove existing blobs because otherwise this will append object files to the old blobs rm -f bin/*.a -arm-none-eabi-as -march=armv6s-m asm.s -o bin/$crate.o +arm-none-eabi-gcc -c -march=armv6s-m asm.S -o bin/$crate.o ar crs bin/thumbv6m-none-eabi.a bin/$crate.o -arm-none-eabi-as -march=armv7-m asm.s -o bin/$crate.o +arm-none-eabi-gcc -c -march=armv7-m asm.S -o bin/$crate.o ar crs bin/thumbv7m-none-eabi.a bin/$crate.o -arm-none-eabi-as -march=armv7e-m asm.s -o bin/$crate.o +arm-none-eabi-gcc -c -march=armv7e-m asm.S -o bin/$crate.o ar crs bin/thumbv7em-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -c -march=armv7e-m asm.S -DHAS_FPU -o bin/$crate.o ar crs bin/thumbv7em-none-eabihf.a bin/$crate.o -arm-none-eabi-as -march=armv8-m.base asm.s -o bin/$crate.o +arm-none-eabi-gcc -c -march=armv8-m.base asm.S -o bin/$crate.o ar crs bin/thumbv8m.base-none-eabi.a bin/$crate.o -arm-none-eabi-as -march=armv8-m.main asm.s -o bin/$crate.o +arm-none-eabi-gcc -c -march=armv8-m.main asm.S -o bin/$crate.o ar crs bin/thumbv8m.main-none-eabi.a bin/$crate.o + +arm-none-eabi-gcc -c -march=armv8-m.main -DHAS_FPU asm.S -o bin/$crate.o ar crs bin/thumbv8m.main-none-eabihf.a bin/$crate.o rm bin/$crate.o diff --git a/bin/thumbv6m-none-eabi.a b/bin/thumbv6m-none-eabi.a index 65684dad..3ac07773 100644 Binary files a/bin/thumbv6m-none-eabi.a and b/bin/thumbv6m-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabi.a b/bin/thumbv7em-none-eabi.a index c4e1f47a..d38ee469 100644 Binary files a/bin/thumbv7em-none-eabi.a and b/bin/thumbv7em-none-eabi.a differ diff --git a/bin/thumbv7em-none-eabihf.a b/bin/thumbv7em-none-eabihf.a index c4e1f47a..a782dce7 100644 Binary files a/bin/thumbv7em-none-eabihf.a and b/bin/thumbv7em-none-eabihf.a differ diff --git a/bin/thumbv7m-none-eabi.a b/bin/thumbv7m-none-eabi.a index ed96942a..038af9fe 100644 Binary files a/bin/thumbv7m-none-eabi.a and b/bin/thumbv7m-none-eabi.a differ diff --git a/bin/thumbv8m.base-none-eabi.a b/bin/thumbv8m.base-none-eabi.a index f1c7734a..ad383aa9 100644 Binary files a/bin/thumbv8m.base-none-eabi.a and b/bin/thumbv8m.base-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabi.a b/bin/thumbv8m.main-none-eabi.a index cb216dcf..ef6e77a8 100644 Binary files a/bin/thumbv8m.main-none-eabi.a and b/bin/thumbv8m.main-none-eabi.a differ diff --git a/bin/thumbv8m.main-none-eabihf.a b/bin/thumbv8m.main-none-eabihf.a index cb216dcf..44380fb3 100644 Binary files a/bin/thumbv8m.main-none-eabihf.a and b/bin/thumbv8m.main-none-eabihf.a differ diff --git a/build.rs b/build.rs index 20f32b88..6758d9ef 100644 --- a/build.rs +++ b/build.rs @@ -18,8 +18,6 @@ fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - has_fpu(&target); - if target.starts_with("thumbv") { fs::copy( format!("bin/{}.a", target), @@ -92,9 +90,3 @@ handlers."); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=link.x.in"); } - -fn has_fpu(target: &str) { - if target.ends_with("eabihf") { - println!("cargo:rustc-cfg=has_fpu"); - } -} diff --git a/link.x.in b/link.x.in index f1a921d0..78fa8255 100644 --- a/link.x.in +++ b/link.x.in @@ -23,8 +23,9 @@ INCLUDE memory.x /* # Entry point = reset vector */ +EXTERN(__RESET_VECTOR); +EXTERN(Reset); ENTRY(Reset); -EXTERN(__RESET_VECTOR); /* depends on the `Reset` symbol */ /* # Exception vectors */ /* This is effectively weak aliasing at the linker level */ @@ -85,13 +86,15 @@ SECTIONS /* ### .text */ .text _stext : { - /* place these 2 close to each other or the `b` instruction will fail to link */ - *(.PreResetTrampoline); *(.Reset); *(.text .text.*); + + /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`, + so must be placed close to it. */ *(.HardFaultTrampoline); *(.HardFault.*); + . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ } > FLASH . = ALIGN(4); /* Ensure __etext is aligned if something unaligned is inserted after .text */ diff --git a/src/lib.rs b/src/lib.rs index ab4bc3f2..e5e290d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,11 +192,9 @@ //! //! One will always find the following (unmangled) symbols in `cortex-m-rt` applications: //! -//! - `Reset`. This is the reset handler. The microcontroller will executed this function upon +//! - `Reset`. This is the reset handler. The microcontroller will execute this function upon //! booting. This function will call the user program entry point (cf. [`#[entry]`][attr-entry]) -//! using the `main` symbol so you may also find that symbol in your program; if you do, `main` -//! will contain your application code. Some other times `main` gets inlined into `Reset` so you -//! won't find it. +//! using the `main` symbol so you will also find that symbol in your program. //! //! - `DefaultHandler`. This is the default handler. If not overridden using `#[exception] fn //! DefaultHandler(..` this will be an infinite loop. @@ -212,8 +210,8 @@ //! the initial value of the stack pointer; this is where the stack will be located -- the stack //! grows downwards towards smaller addresses. //! -//! - `__RESET_VECTOR`. This is the reset vector, a pointer into the `Reset` handler. This vector is -//! located in the `.vector_table` section after `__STACK_START`. +//! - `__RESET_VECTOR`. This is the reset vector, a pointer to the `Reset` function. This vector +//! is located in the `.vector_table` section after `__STACK_START`. //! //! - `__EXCEPTIONS`. This is the core exceptions portion of the vector table; it's an array of 14 //! exception vectors, which includes exceptions like `HardFault` and `SysTick`. This array is @@ -226,19 +224,11 @@ //! //! - `__pre_init`. This is a function to be run before RAM is initialized. It defaults to an empty //! function. The function called can be changed by applying the [`#[pre_init]`][attr-pre_init] -//! attribute to a function. The empty function is not optimized out by default, but if an empty -//! function is passed to [`#[pre_init]`][attr-pre_init] the function call will be optimized out. +//! attribute to a function. //! //! If you override any exception handler you'll find it as an unmangled symbol, e.g. `SysTick` or //! `SVCall`, in the output of `objdump`, //! -//! If you are targeting the `thumbv7em-none-eabihf` target you'll also see a `ResetTrampoline` -//! symbol in the output. To avoid the compiler placing FPU instructions before the FPU has been -//! enabled (cf. `vpush`) `Reset` calls the function `ResetTrampoline` which is marked as -//! `#[inline(never)]` and `ResetTrampoline` calls `main`. The compiler is free to inline `main` -//! into `ResetTrampoline` but it can't inline `ResetTrampoline` into `Reset` -- the FPU is enabled -//! in `Reset`. -//! //! # Advanced usage //! //! ## Setting the program entry point @@ -248,9 +238,9 @@ //! guarantees. //! //! The `Reset` handler will call a symbol named `main` (unmangled) *after* initializing `.bss` and -//! `.data`, and enabling the FPU (if the target is `thumbv7em-none-eabihf`). A function with the -//! `entry` attribute will be set to have the export name "`main`"; in addition, its mutable -//! statics are turned into safe mutable references (see [`#[entry]`][attr-entry] for details). +//! `.data`, and enabling the FPU (if the target has an FPU). A function with the `entry` attribute +//! will be set to have the export name "`main`"; in addition, its mutable statics are turned into +//! safe mutable references (see [`#[entry]`][attr-entry] for details). //! //! The unmangled `main` symbol must have signature `extern "C" fn() -> !` or its invocation from //! `Reset` will result in undefined behavior. @@ -411,7 +401,7 @@ //! *(.ccmram .ccmram.*); //! . = ALIGN(4); //! } > CCMRAM -//! } INSERT AFTER .bss; +//! } //! ``` //! //! You can then use something like this to place a variable into this specific section of memory: @@ -438,7 +428,6 @@ #![no_std] extern crate cortex_m_rt_macros as macros; -extern crate r0; use core::fmt; use core::sync::atomic::{self, Ordering}; @@ -701,8 +690,8 @@ pub use macros::exception; /// /// # Safety /// -/// The function will be called before static variables are initialized. Any access of static -/// variables will result in undefined behavior. +/// The function will be called before memory is initialized, as soon as possible after reset. Any +/// access of memory, including any static variables, will result in undefined behavior. /// /// **Warning**: Due to [rvalue static promotion][rfc1414] static variables may be accessed whenever /// taking a reference to a constant. This means that even trivial expressions such as `&1` in the @@ -919,66 +908,12 @@ pub fn heap_start() -> *mut u32 { unsafe { &mut __sheap } } -/* Entry point */ +// Entry point is Reset. #[doc(hidden)] #[link_section = ".vector_table.reset_vector"] #[no_mangle] -#[cfg(not(armv6m))] pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset; -#[doc(hidden)] -#[link_section = ".vector_table.reset_vector"] -#[no_mangle] -#[cfg(armv6m)] -pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = PreResetTrampoline; - -#[doc(hidden)] -#[link_section = ".Reset"] -#[no_mangle] -pub unsafe extern "C" fn Reset() -> ! { - extern "C" { - - // These symbols come from `link.x` - static mut __sbss: u32; - static mut __ebss: u32; - - static mut __sdata: u32; - static mut __edata: u32; - static __sidata: u32; - } - - extern "Rust" { - // This symbol will be provided by the user via `#[pre_init]` - fn __pre_init(); - } - - __pre_init(); - - // Initialize RAM - r0::zero_bss(&mut __sbss, &mut __ebss); - r0::init_data(&mut __sdata, &mut __edata, &__sidata); - - #[allow(clippy::match_single_binding)] - match () { - #[cfg(not(has_fpu))] - () => { - extern "C" { - // This symbol will be provided by the user via `#[entry]` - fn main() -> !; - } - main() - } - #[cfg(has_fpu)] - () => { - extern "C" { - fn FpuTrampoline() -> !; - } - - FpuTrampoline() - } - } -} - #[allow(unused_variables)] #[doc(hidden)] #[link_section = ".HardFault.default"] @@ -1038,8 +973,7 @@ pub enum Exception { pub use self::Exception as exception; extern "C" { - #[cfg(armv6m)] - fn PreResetTrampoline() -> !; + fn Reset() -> !; fn NonMaskableInt();