rust-embedded/cortex-m

Add feature to disable .data copy when using a copying bootloader

Opened this issue · 5 comments

This is an issue that I found when getting boot2's copy-to-RAM setup working on RP2040. The boot2 bootloader sets up the SPI flash, copies all of it (including the .data region) into memory, and then de-initializes the SPI flash so it's no longer mapped into the memory space. If the LMA and VMAs are set up correctly with __sidata = LOADADDR(.data) cortex-m-rt's apploader will then come along and try to copy the .data region out of flash again, but since the SPI flash is no longer mapped it gets nonsense bytes.

I was able to proximally solve the issue by setting __sidata = ADDR(.data) in my linker script, which causes the app loader to copy the .data region to itself in RAM after boot2 is done. A better solution would be to add a feature flag to the library that lets me disable the copy entirely (which would then let me use a correct definition for __sidata), but I wanted to open an issue to see if there's a better way to solve it before opening a PR.

I think a skip-data-copy feature would be a reasonable thing to add.

When exactly is this a problem? I wrote Rust firmware for RP2040 in the past, but never had this issue. I used rp2040-hal with the rt feature and I seem to remember that it used boot2, too, but I can't find it right now.

@9names: You are right, I looked it up. I was using rp2040_boot2 indirectly through rp-pico which uses rp2040_boot2::BOOT_LOADER_W25Q080. That is why this was never a problem for me.

Yes, by default rp-pico uses XIP while running which keeps the RAM mapped, so the boot script keeps working.

This project https://github.com/BenChung/RP2040_boot2_run_from_ram (which is based on @9names's https://github.com/9names/rp2040-hal-run-from-ram/tree/main) should be set up to run from RAM, though the Cargo.toml needs to be updated to point at your local copy of embassy and defmt. It has the fix applied in the link.ram.x linker script:

  /* LMA of .data */
  /* should actually be __sidata = LOADADDR(.data) - but by setting it to ADDR(.data) it works */
  __sidata = ADDR(.data);

Replace this line with

  __sidata = LOADADDR(.data);

to reproduce the problem (tested on a RP2040 in a pico).

It should be possible to reproduce the problem in any RP2040 project by enabling memcpy on boot2 (e.g. add the feature boot2-ram-memcpy to embassy-rp) and adding the RAM linker script by replacing

    println!("cargo:rustc-link-arg-bins=-Tlink.x");

with

    println!("cargo:rustc-link-arg-bins=-Tlink.ram.x");

in the build.rs file, while updating it to also copy the link.ram.x file to the target directory (see the example for how I do this, which is probably not optimal).

If you run this with the LOADADDR version then it'll crash when defmtt tries to attach because the RTT config block is in the static region and gets messed up by the copy from uninitialized XIP memory.