diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-02-09 15:07:25 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-02-09 15:07:25 +0000 |
| commit | 3d6b8bd9832d5a29cab4aa21434663e6ea6f4488 (patch) | |
| tree | 5e457a46c63f9565e82b9dd401701cbd7aba20b7 /examples/boot | |
| parent | d91bd0b9a69b8411f2a1d58bfad5d4dce51e7110 (diff) | |
| parent | e990021b9a9d3acc309c21bd4ddf3ff090bb7999 (diff) | |
Merge #604
604: Add embassy-boot r=lulf a=lulf
Continuation of https://github.com/embassy-rs/embassy/pull/588
Embassy-boot is a simple bootloader that works together with an application to provide firmware update capabilities with a minimal risk.
The bootloader consists of a platform-independent part, which implements the swap algorithm, and a platform-dependent part (currently only for nRF) that provides addition functionality such as watchdog timers softdevice support.
The bootloader is intended to be configurable for different flash sizes and architectures, and only requires that embedded-storage flash traits are implemented.
The nRF version can be configured programatically as a library, or using linker scripts to set the partition locations for DFU, ACTIVE and STATE
* DFU: Where the next firmware version should be written. This is used by the FirmwareUpdater
* ACTIVE: Where the current firmware version resides. Written by bootloader when swap magic is set
* STATE: Contains the bootloader magic and the copy progress. Can be 1-N pages long (depending on how much flash you have which will determine the copy progress index size
Co-authored-by: Ulf Lilleengen <[email protected]>
Co-authored-by: Ulf Lilleengen <[email protected]>
Diffstat (limited to 'examples/boot')
| -rw-r--r-- | examples/boot/.cargo/config.toml | 7 | ||||
| -rw-r--r-- | examples/boot/Cargo.toml | 19 | ||||
| -rw-r--r-- | examples/boot/README.md | 31 | ||||
| -rw-r--r-- | examples/boot/build.rs | 34 | ||||
| -rw-r--r-- | examples/boot/memory.x | 14 | ||||
| -rw-r--r-- | examples/boot/src/bin/a.rs | 49 | ||||
| -rw-r--r-- | examples/boot/src/bin/b.rs | 26 |
7 files changed, 180 insertions, 0 deletions
diff --git a/examples/boot/.cargo/config.toml b/examples/boot/.cargo/config.toml new file mode 100644 index 000000000..d044e9b4c --- /dev/null +++ b/examples/boot/.cargo/config.toml | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | [unstable] | ||
| 2 | namespaced-features = true | ||
| 3 | build-std = ["core"] | ||
| 4 | build-std-features = ["panic_immediate_abort"] | ||
| 5 | |||
| 6 | [build] | ||
| 7 | target = "thumbv7em-none-eabi" | ||
diff --git a/examples/boot/Cargo.toml b/examples/boot/Cargo.toml new file mode 100644 index 000000000..36e2e169d --- /dev/null +++ b/examples/boot/Cargo.toml | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | [package] | ||
| 2 | authors = ["Ulf Lilleengen <[email protected]>"] | ||
| 3 | edition = "2018" | ||
| 4 | name = "embassy-boot-examples" | ||
| 5 | version = "0.1.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy = { version = "0.1.0", path = "../../embassy" } | ||
| 9 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["time-driver-rtc1", "gpiote"] } | ||
| 10 | embassy-boot-nrf = { version = "0.1.0", path = "../../embassy-boot/nrf" } | ||
| 11 | embassy-traits = { version = "0.1.0", path = "../../embassy-traits" } | ||
| 12 | |||
| 13 | defmt = { version = "0.3", optional = true } | ||
| 14 | defmt-rtt = { version = "0.3", optional = true } | ||
| 15 | panic-reset = { version = "0.1.1" } | ||
| 16 | embedded-hal = { version = "0.2.6" } | ||
| 17 | |||
| 18 | cortex-m = "0.7.3" | ||
| 19 | cortex-m-rt = "0.7.0" | ||
diff --git a/examples/boot/README.md b/examples/boot/README.md new file mode 100644 index 000000000..b97513a9d --- /dev/null +++ b/examples/boot/README.md | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | # Examples using bootloader | ||
| 2 | |||
| 3 | Example for nRF52 demonstrating the bootloader. The example consists of application binaries, 'a' | ||
| 4 | which allows you to press a button to start the DFU process, and 'b' which is the updated | ||
| 5 | application. | ||
| 6 | |||
| 7 | |||
| 8 | ## Prerequisites | ||
| 9 | |||
| 10 | * `cargo-binutils` | ||
| 11 | * `cargo-flash` | ||
| 12 | * `embassy-boot-nrf` | ||
| 13 | |||
| 14 | ## Usage | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | ``` | ||
| 19 | # Flash bootloader | ||
| 20 | cargo flash --manifest-path ../../embassy-boot/nrf/Cargo.toml --release --features embassy-nrf/nrf52840 --chip nRF52840_xxAA | ||
| 21 | # Build 'b' | ||
| 22 | cargo build --release --features embassy-nrf/nrf52840 --bin b | ||
| 23 | # Generate binary for 'b' | ||
| 24 | cargo objcopy --release --features embassy-nrf/nrf52840 --bin b -- -O binary b.bin | ||
| 25 | ``` | ||
| 26 | |||
| 27 | # Flash `a` (which includes b.bin) | ||
| 28 | |||
| 29 | ``` | ||
| 30 | cargo flash --release --features embassy-nrf/nrf52840 --bin a --chip nRF52840_xxAA | ||
| 31 | ``` | ||
diff --git a/examples/boot/build.rs b/examples/boot/build.rs new file mode 100644 index 000000000..cd1a264c4 --- /dev/null +++ b/examples/boot/build.rs | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | } | ||
diff --git a/examples/boot/memory.x b/examples/boot/memory.x new file mode 100644 index 000000000..dfb72103f --- /dev/null +++ b/examples/boot/memory.x | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K | ||
| 5 | FLASH : ORIGIN = 0x00007000, LENGTH = 64K | ||
| 6 | DFU : ORIGIN = 0x00017000, LENGTH = 68K | ||
| 7 | RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K | ||
| 8 | } | ||
| 9 | |||
| 10 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | ||
| 11 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); | ||
| 12 | |||
| 13 | __bootloader_dfu_start = ORIGIN(DFU); | ||
| 14 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); | ||
diff --git a/examples/boot/src/bin/a.rs b/examples/boot/src/bin/a.rs new file mode 100644 index 000000000..88880e688 --- /dev/null +++ b/examples/boot/src/bin/a.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![feature(generic_associated_types)] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use embassy_boot_nrf::updater; | ||
| 8 | use embassy_nrf::{ | ||
| 9 | gpio::{Input, Pull}, | ||
| 10 | gpio::{Level, Output, OutputDrive}, | ||
| 11 | nvmc::Nvmc, | ||
| 12 | Peripherals, | ||
| 13 | }; | ||
| 14 | use embassy_traits::adapter::BlockingAsync; | ||
| 15 | use embedded_hal::digital::v2::InputPin; | ||
| 16 | use panic_reset as _; | ||
| 17 | |||
| 18 | static APP_B: &[u8] = include_bytes!("../../b.bin"); | ||
| 19 | |||
| 20 | #[embassy::main] | ||
| 21 | async fn main(_s: embassy::executor::Spawner, p: Peripherals) { | ||
| 22 | let mut button = Input::new(p.P0_11, Pull::Up); | ||
| 23 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | ||
| 24 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | ||
| 25 | //let mut button = Input::new(p.P1_02, Pull::Up); | ||
| 26 | |||
| 27 | let nvmc = Nvmc::new(p.NVMC); | ||
| 28 | let mut nvmc = BlockingAsync::new(nvmc); | ||
| 29 | |||
| 30 | loop { | ||
| 31 | button.wait_for_any_edge().await; | ||
| 32 | if button.is_low().unwrap() { | ||
| 33 | let mut updater = updater::new(); | ||
| 34 | let mut offset = 0; | ||
| 35 | for chunk in APP_B.chunks(4096) { | ||
| 36 | let mut buf: [u8; 4096] = [0; 4096]; | ||
| 37 | buf[..chunk.len()].copy_from_slice(chunk); | ||
| 38 | updater | ||
| 39 | .write_firmware(offset, &buf, &mut nvmc) | ||
| 40 | .await | ||
| 41 | .unwrap(); | ||
| 42 | offset += chunk.len(); | ||
| 43 | } | ||
| 44 | updater.mark_update(&mut nvmc).await.unwrap(); | ||
| 45 | led.set_high(); | ||
| 46 | cortex_m::peripheral::SCB::sys_reset(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/examples/boot/src/bin/b.rs b/examples/boot/src/bin/b.rs new file mode 100644 index 000000000..18bb6330c --- /dev/null +++ b/examples/boot/src/bin/b.rs | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![macro_use] | ||
| 4 | #![feature(generic_associated_types)] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use embassy::time::{Duration, Timer}; | ||
| 8 | use embassy_nrf::{ | ||
| 9 | gpio::{Level, Output, OutputDrive}, | ||
| 10 | Peripherals, | ||
| 11 | }; | ||
| 12 | |||
| 13 | use panic_reset as _; | ||
| 14 | |||
| 15 | #[embassy::main] | ||
| 16 | async fn main(_s: embassy::executor::Spawner, p: Peripherals) { | ||
| 17 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | ||
| 18 | //let mut led = Output::new(p.P1_10, Level::Low, OutputDrive::Standard); | ||
| 19 | |||
| 20 | loop { | ||
| 21 | led.set_high(); | ||
| 22 | Timer::after(Duration::from_millis(300)).await; | ||
| 23 | led.set_low(); | ||
| 24 | Timer::after(Duration::from_millis(300)).await; | ||
| 25 | } | ||
| 26 | } | ||
