diff --git a/cortex-m-rt/CHANGELOG.md b/cortex-m-rt/CHANGELOG.md index fe639fb8..4d2b2222 100644 --- a/cortex-m-rt/CHANGELOG.md +++ b/cortex-m-rt/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Mark `pre_init` as deprecated - Add `set_msplim` feature to conditionally set the MSPLIM register at device reset ([#580]). +- Add `skip-data-init` feature to optionally skip copying the `.data` section. ## [v0.7.5] diff --git a/cortex-m-rt/Cargo.toml b/cortex-m-rt/Cargo.toml index 377a8d5d..a03f7cf7 100644 --- a/cortex-m-rt/Cargo.toml +++ b/cortex-m-rt/Cargo.toml @@ -48,6 +48,7 @@ set-vtor = [] set-msplim = [] zero-init-ram = [] paint-stack = [] +skip-data-init = [] [package.metadata.docs.rs] features = ["device"] diff --git a/cortex-m-rt/examples/skip-data-init.rs b/cortex-m-rt/examples/skip-data-init.rs new file mode 100644 index 00000000..1143f455 --- /dev/null +++ b/cortex-m-rt/examples/skip-data-init.rs @@ -0,0 +1,56 @@ +//! Example demonstrating the skip-data-init feature +//! +//! This feature is useful when using bootloaders (like RP2040's boot2) that: +//! 1. Copy all data from Flash to RAM +//! 2. Unmap the Flash from memory space +//! 3. Jump to the Reset handler +//! +//! In such scenarios, the default cortex-m-rt data initialization would fail +//! because it tries to copy from Flash which is no longer accessible. +//! +//! To use this feature, enable it in your Cargo.toml: +//! ```toml +//! [dependencies] +//! cortex-m-rt = { version = "0.7", features = ["skip-data-init"] } +//! ``` +//! +//! And ensure your bootloader or linker script properly initializes .data before +//! jumping to Reset. + +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use cortex_m_semihosting::hprintln; +use panic_halt as _; + +// Minimal dummy critical-section implementation for single-core boards +#[no_mangle] +extern "C" fn _critical_section_1_0_acquire() -> u8 { + 0 +} +#[no_mangle] +extern "C" fn _critical_section_1_0_release(_token: u8) {} + +static mut COUNTER: u32 = 42; + +#[entry] +fn main() -> ! { + let (prev, new) = unsafe { + COUNTER += 1; + (COUNTER - 1, COUNTER) + }; + + // Print via semihosting; ignore errors + hprintln!("Previous counter value: {}", prev); + hprintln!("New counter value: {}", new); + + // Panic if data section not initialized properly + if prev != 42 || new != 43 { + panic!("Unexpected COUNTER value! Data section may not be initialized."); + } + + loop { + cortex_m::asm::nop(); + } +} diff --git a/cortex-m-rt/src/lib.rs b/cortex-m-rt/src/lib.rs index 2aaeadde..0aca2070 100644 --- a/cortex-m-rt/src/lib.rs +++ b/cortex-m-rt/src/lib.rs @@ -208,6 +208,30 @@ //! where the stack has been used the 'paint' will have been 'scrubbed off' and the memory will //! have a value other than `STACK_PAINT_VALUE`. //! +//! ## `skip-data-init` +//! +//! If this feature is enabled, the `.data` section initialization is skipped during startup. +//! By default, cortex-m-rt copies the `.data` section from its load address (LMA) in Flash +//! to its virtual address (VMA) in RAM. However, in some scenarios this copy is unnecessary +//! or undesirable: +//! +//! - When using bootloaders like RP2040's boot2 that copy all data from Flash to RAM and then +//! unmap the Flash, the cortex-m-rt data initialization would fail because Flash is no longer +//! accessible. +//! - When the `.data` section is already placed in RAM at the correct address (LMA equals VMA). +//! +//! When this feature is enabled, it is the user's responsibility to ensure that the `.data` +//! section is properly initialized before the program's main function is called. This can be +//! done by: +//! +//! - Using a bootloader that copies the data before jumping to the Reset handler +//! - Setting `__sidata = ADDR(.data)` in the linker script to make LMA equal to VMA (though this +//! wastes Flash space) +//! - Other custom initialization mechanisms +//! +//! **WARNING:** Using this feature without ensuring proper `.data` initialization will result +//! in undefined behavior if your program uses initialized static variables. +//! //! # Inspection //! //! This section covers how to inspect a binary that builds on top of `cortex-m-rt`. @@ -489,6 +513,11 @@ #![deny(missing_docs)] #![no_std] +#[cfg(all(feature = "skip-data-init", feature = "zero-init-ram"))] +compile_error!( + "features `skip-data-init` and `zero-init-ram` cannot be enabled at the same time" +); + extern crate cortex_m_rt_macros as macros; /// The 32-bit value the stack is painted with before the program runs. @@ -603,6 +632,7 @@ cfg_global_asm! { 1:", // Initialise .data memory. `__sdata`, `__sidata`, and `__edata` come from the linker script. + #[cfg(not(feature = "skip-data-init"))] "ldr r0, =__sdata ldr r1, =__edata ldr r2, =__sidata