aboutsummaryrefslogtreecommitdiff
path: root/cortex-m-rt/src
diff options
context:
space:
mode:
Diffstat (limited to 'cortex-m-rt/src')
-rw-r--r--cortex-m-rt/src/lib.rs213
1 files changed, 169 insertions, 44 deletions
diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs
index 752d3d7..6e6bf7e 100644
--- a/cortex-m-rt/src/lib.rs
+++ b/cortex-m-rt/src/lib.rs
@@ -34,14 +34,14 @@
//!
//! This crate expects the user, or some other crate, to provide the memory layout of the target
//! device via a linker script named `memory.x`. This section covers the contents of `memory.x`
-//! The `memory.x` file is used by during linking by the `link.x` script provided by this crate.
+//! The `memory.x` file is used during linking by the `link.x` script provided by this crate.
//!
//! ### `MEMORY`
//!
//! The linker script must specify the memory available in the device as, at least, two `MEMORY`
//! regions: one named `FLASH` and one named `RAM`. The `.text` and `.rodata` sections of the
//! program will be placed in the `FLASH` region, whereas the `.bss` and `.data` sections, as well
-//! as the heap,will be placed in the `RAM` region.
+//! as the heap, will be placed in the `RAM` region.
//!
//! ```text
//! /* Linker script for the STM32F103C8T6 */
@@ -158,6 +158,19 @@
//! conjunction with crates generated using `svd2rust`. Those *device crates* will populate the
//! missing part of the vector table when their `"rt"` feature is enabled.
//!
+//! ## `set-sp`
+//!
+//! If this feature is enabled, the stack pointer (SP) is initialised in the reset handler to the
+//! `_stack_start` value from the linker script. This is not usually required, but some debuggers
+//! do not initialise SP when performing a soft reset, which can lead to stack corruption.
+//!
+//! ## `set-vtor`
+//!
+//! If this feature is enabled, the vector table offset register (VTOR) is initialised in the reset
+//! handler to the start of the vector table defined in the linker script. This is not usually
+//! required, but some bootloaders do not set VTOR before jumping to application code, leading to
+//! your main function executing but interrupt handlers not being used.
+//!
//! # Inspection
//!
//! This section covers how to inspect a binary that builds on top of `cortex-m-rt`.
@@ -309,14 +322,10 @@
//!
//! We want to provide a default handler for all the interrupts while still letting the user
//! individually override each interrupt handler. In C projects, this is usually accomplished using
-//! weak aliases declared in external assembly files. In Rust, we could achieve something similar
-//! using `global_asm!`, but that's an unstable feature.
-//!
-//! A solution that doesn't require `global_asm!` or external assembly files is to use the `PROVIDE`
-//! command in a linker script to create the weak aliases. This is the approach that `cortex-m-rt`
-//! uses; when the `"device"` feature is enabled `cortex-m-rt`'s linker script (`link.x`) depends on
-//! a linker script named `device.x`. The crate that provides `__INTERRUPTS` must also provide this
-//! file.
+//! weak aliases declared in external assembly files. We use a similar solution via the `PROVIDE`
+//! command in the linker script: when the `"device"` feature is enabled, `cortex-m-rt`'s linker
+//! script (`link.x`) includes a linker script named `device.x`, which must be provided by
+//! whichever crate provides `__INTERRUPTS`.
//!
//! For our running example the `device.x` linker script looks like this:
//!
@@ -330,8 +339,8 @@
//! that the core exceptions use unless overridden.
//!
//! Because this linker script is provided by a dependency of the final application the dependency
-//! must contain build script that puts `device.x` somewhere the linker can find. An example of such
-//! build script is shown below:
+//! must contain a build script that puts `device.x` somewhere the linker can find. An example of
+//! such build script is shown below:
//!
//! ```ignore
//! use std::env;
@@ -418,7 +427,7 @@
//!
//! # Minimum Supported Rust Version (MSRV)
//!
-//! The MSRV of this release is Rust 1.42.0.
+//! The MSRV of this release is Rust 1.59.0.
// # Developer notes
//
@@ -430,16 +439,152 @@
extern crate cortex_m_rt_macros as macros;
+#[cfg(cortex_m)]
+use core::arch::global_asm;
use core::fmt;
-use core::sync::atomic::{self, Ordering};
+
+// 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.
+// Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP.
+#[cfg(cortex_m)]
+global_asm!(
+ ".cfi_sections .debug_frame
+ .section .HardFaultTrampoline, \"ax\"
+ .global HardFaultTrampline
+ .type HardFaultTrampline,%function
+ .thumb_func
+ .cfi_startproc
+ HardFaultTrampoline:",
+ "mov r0, lr
+ movs r1, #4
+ tst r0, r1
+ bne 0f
+ mrs r0, MSP
+ b HardFault
+ 0:
+ mrs r0, PSP
+ b HardFault",
+ ".cfi_endproc
+ .size HardFaultTrampoline, . - HardFaultTrampoline",
+);
+
+/// Parse cfg attributes inside a global_asm call.
+#[cfg(cortex_m)]
+macro_rules! cfg_global_asm {
+ {@inner, [$($x:tt)*], } => {
+ global_asm!{$($x)*}
+ };
+ (@inner, [$($x:tt)*], #[cfg($meta:meta)] $asm:literal, $($rest:tt)*) => {
+ #[cfg($meta)]
+ cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
+ #[cfg(not($meta))]
+ cfg_global_asm!{@inner, [$($x)*], $($rest)*}
+ };
+ {@inner, [$($x:tt)*], $asm:literal, $($rest:tt)*} => {
+ cfg_global_asm!{@inner, [$($x)* $asm,], $($rest)*}
+ };
+ {$($asms:tt)*} => {
+ cfg_global_asm!{@inner, [], $($asms)*}
+ };
+}
+
+// This reset vector is the initial entry point after a system reset.
+// Calls an optional user-provided __pre_init and then initialises RAM.
+// If the target has an FPU, it is enabled.
+// Finally jumps to the user main function.
+#[cfg(cortex_m)]
+cfg_global_asm! {
+ ".cfi_sections .debug_frame
+ .section .Reset, \"ax\"
+ .global Reset
+ .type Reset,%function
+ .thumb_func",
+ ".cfi_startproc
+ Reset:",
+
+ // Ensure LR is loaded with 0xFFFF_FFFF at startup to help debuggers find the first call frame.
+ // On ARMv6-M LR is not initialised at all, while other platforms should initialise it.
+ "movs r4, #0
+ mvns r4, r4
+ mov lr, r4",
+
+ // If enabled, initialise the SP. This is normally initialised by the CPU itself or by a
+ // bootloader, but some debuggers fail to set it when resetting the target, leading to
+ // stack corruptions.
+ #[cfg(feature = "set-sp")]
+ "ldr r0, =_stack_start
+ msr msp, r0",
+
+ // If enabled, initialise VTOR to the start of the vector table. This is normally initialised
+ // by a bootloader when the non-reset value is required, but some bootloaders do not set it,
+ // leading to frustrating issues where everything seems to work but interrupts are never
+ // handled. The VTOR register is optional on ARMv6-M, but when not present is RAZ,WI and
+ // therefore safe to write to.
+ #[cfg(feature = "set-vtor")]
+ "ldr r0, =0xe000ed08
+ ldr r1, =__vector_table
+ str r1, [r0]",
+
+ // 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.
+ // Reload LR after returning from pre-init (r4 is preserved by subroutines).
+ "bl __pre_init
+ mov lr, r4",
+
+ // Initialise .bss memory. `__sbss` and `__ebss` come from the linker script.
+ "ldr r0, =__sbss
+ ldr r1, =__ebss
+ movs 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.
+ "ldr r0, =__sdata
+ ldr r1, =__edata
+ ldr r2, =__sidata
+ 2:
+ cmp r1, r0
+ beq 3f
+ ldm r2!, {{r3}}
+ stm r0!, {{r3}}
+ b 2b
+ 3:",
+
+ // Potentially enable an FPU.
+ // SCB.CPACR is 0xE000_ED88.
+ // We enable access to CP10 and CP11 from priviliged and unprivileged mode.
+ #[cfg(has_fpu)]
+ "ldr r0, =0xE000ED88
+ ldr r1, =(0b1111 << 20)
+ ldr r2, [r0]
+ orr r2, r2, r1
+ str r2, [r0]
+ dsb
+ isb",
+
+ // Push `lr` to the stack for debuggers, to prevent them unwinding past Reset.
+ // See https://sourceware.org/binutils/docs/as/CFI-directives.html.
+ ".cfi_def_cfa sp, 0
+ push {{lr}}
+ .cfi_offset lr, 0",
+
+ // Jump to user main function.
+ // `bl` is used for the extended range, but the user main function should not return,
+ // so trap on any unexpected return.
+ "bl main
+ udf #0",
+
+ ".cfi_endproc
+ .size Reset, . - Reset",
+}
/// Attribute to declare an interrupt (AKA device-specific exception) handler
///
-/// **IMPORTANT**: If you are using Rust 1.30 this attribute must be used on reachable items (i.e.
-/// there must be no private modules between the item and the root of the crate); if the item is in
-/// the root of the crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31
-/// and newer releases.
-///
/// **NOTE**: This attribute is exposed by `cortex-m-rt` only when the `device` feature is enabled.
/// However, that export is not meant to be used directly -- using it will result in a compilation
/// error. You should instead use the device crate (usually generated using `svd2rust`) re-export of
@@ -506,11 +651,6 @@ pub use macros::interrupt;
/// Attribute to declare the entry point of the program
///
-/// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you
-/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
-/// private modules between the item and the root of the crate); if the item is in the root of the
-/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases.
-///
/// The specified function will be called by the reset handler *after* RAM has been initialized. In
/// the case of the `thumbv7em-none-eabihf` target the FPU will also be enabled before the function
/// is called.
@@ -565,11 +705,6 @@ pub use macros::entry;
/// Attribute to declare an exception handler
///
-/// **IMPORTANT**: If you are using Rust 1.30 this attribute must be used on reachable items (i.e.
-/// there must be no private modules between the item and the root of the crate); if the item is in
-/// the root of the crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31
-/// and newer releases.
-///
/// # Syntax
///
/// ```
@@ -681,11 +816,7 @@ pub use macros::exception;
/// Attribute to mark which function will be called at the beginning of the reset handler.
///
-/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you
-/// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no
-/// private modules between the item and the root of the crate); if the item is in the root of the
-/// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer
-/// releases.
+/// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph.
///
/// The function must have the signature of `unsafe fn()`.
///
@@ -920,21 +1051,15 @@ pub static __RESET_VECTOR: unsafe extern "C" fn() -> ! = Reset;
#[cfg_attr(cortex_m, link_section = ".HardFault.default")]
#[no_mangle]
pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! {
- loop {
- // add some side effect to prevent this from turning into a UDF instruction
- // see rust-lang/rust#28728 for details
- atomic::compiler_fence(Ordering::SeqCst);
- }
+ #[allow(clippy::empty_loop)]
+ loop {}
}
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn DefaultHandler_() -> ! {
- loop {
- // add some side effect to prevent this from turning into a UDF instruction
- // see rust-lang/rust#28728 for details
- atomic::compiler_fence(Ordering::SeqCst);
- }
+ #[allow(clippy::empty_loop)]
+ loop {}
}
#[doc(hidden)]