diff --git a/tests/boot/nrf/Cargo.toml b/tests/boot/nrf/Cargo.toml index 62bc84c7..3f639b8f 100644 --- a/tests/boot/nrf/Cargo.toml +++ b/tests/boot/nrf/Cargo.toml @@ -12,9 +12,13 @@ embassy-sync = { version = "0.2.0", path = "../../../embassy-sync", features = [ embassy-executor = { version = "0.3.0", path = "../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.3", path = "../../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } +embassy-embedded-hal = { version = "0.1.0", path = "../../../embassy-embedded-hal", features = ["nightly"] } +embassy-boot-nrf = { version = "0.1.0", path = "../../../embassy-boot/nrf" } embedded-io-async = { version = "0.5.0" } embedded-hal-async = { version = "1.0.0-rc.1" } embedded-hal-bus = { version = "0.1.0-rc.1", features = ["async"] } +embedded-storage = "0.3.0" +embedded-storage-async = { version = "0.4.0" } static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" diff --git a/tests/boot/nrf/memory.x b/tests/boot/nrf/memory.x index 5ee9a523..aa6b110d 100644 --- a/tests/boot/nrf/memory.x +++ b/tests/boot/nrf/memory.x @@ -5,7 +5,7 @@ MEMORY BOOTLOADER_STATE : ORIGIN = 0x00006000, LENGTH = 4K FLASH : ORIGIN = 0x00007000, LENGTH = 64K DFU : ORIGIN = 0x00017000, LENGTH = 68K - TESTSTATE : ORIGIN = 0x00028000, LENGTH = 4K + TEST_STATE : ORIGIN = 0x00028000, LENGTH = 4K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K } @@ -15,6 +15,5 @@ __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); __bootloader_dfu_start = ORIGIN(DFU); __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); - -__teststate_start = ORIGIN(TESTSTATE); -__teststate_end = ORIGIN(TESTSTATE) + LENGTH(TESTSTATE); +__test_state_start = ORIGIN(TEST_STATE); +__test-state_end = ORIGIN(TEST_STATE) + LENGTH(TEST_STATE); diff --git a/tests/boot/nrf/src/bin/update_base.rs b/tests/boot/nrf/src/bin/update_base.rs new file mode 100644 index 00000000..ff16d1b8 --- /dev/null +++ b/tests/boot/nrf/src/bin/update_base.rs @@ -0,0 +1,68 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"nrf52840-dk"); + +#[path = "../common.rs"] +mod common; +use common::*; + +use embassy_boot_nrf::{BlockingFirmwareUpdater, FirmwareUpdaterConfig}; +use embassy_executor::Spawner; +use embassy_nrf::nvmc::Nvmc; +use embassy_time::{Timer, Duration}; +use embassy_sync::blocking_mutex::Mutex; +use core::cell::RefCell; +use embedded_storage::nor_flash::NorFlash; + +static APP_SUCCESS: &[u8] = include_bytes!("../../update_success.bin"); +static APP_FAILURE: &[u8] = include_bytes!("../../update_failure.bin"); + +#[embassy_executor::main] +async fn main(s: Spawner) { + let p = embassy_nrf::init(Default::default()); + + s.spawn(watchdog_task()).unwrap(); + + let nvmc = Nvmc::new(p.NVMC); + let nvmc = Mutex::new(RefCell::new(nvmc)); + + let state = TestState::init(&nvmc); + + // First time we boot the bad firmare + let app = if state.num_boots == 0 { + APP_FAILURE + // Second time we boot the good firmware + } else if state.num_boots == 1 { + APP_SUCCESS + } else { + defmt::panic!("Unexpected number of boots"); + }; + + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&nvmc); + let mut magic = [0; 4]; + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic); + + // Check state if we've attempted update yet + let dfu = updater.prepare_update().unwrap(); + + let mut offset = 0; + for chunk in app.chunks(4096) { + let mut buf: [u8; 4096] = [0; 4096]; + buf[..chunk.len()].copy_from_slice(chunk); + dfu.write(offset, &buf).unwrap(); + offset += chunk.len() as u32; + } + updater.mark_updated().unwrap(); + cortex_m::peripheral::SCB::sys_reset(); +} + +// Keeps our system alive +#[embassy_executor::task] +async fn watchdog_task() { + let mut handle = unsafe { embassy_nrf::wdt::WatchdogHandle::steal(0) }; + loop { + handle.pet(); + Timer::after(Duration::from_secs(2)).await; + } +} diff --git a/tests/boot/nrf/src/bin/update_failure.rs b/tests/boot/nrf/src/bin/update_failure.rs new file mode 100644 index 00000000..bbba90f0 --- /dev/null +++ b/tests/boot/nrf/src/bin/update_failure.rs @@ -0,0 +1,40 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +teleprobe_meta::target!(b"nrf52840-dk"); + +#[path = "../common.rs"] +mod common; +use common::*; + +use embassy_executor::Spawner; +use embassy_time::{Timer, Duration}; +use embassy_nrf::nvmc::Nvmc; +use embassy_sync::blocking_mutex::Mutex; +use core::cell::RefCell; + +#[embassy_executor::main] +async fn main(s: Spawner) { + let p = embassy_nrf::init(Default::default()); + + s.spawn(watchdog_task()).unwrap(); + + let nvmc = Nvmc::new(p.NVMC); + let nvmc = Mutex::new(RefCell::new(nvmc)); + + let mut state = TestState::init(&nvmc); + state.inc(&nvmc); + + // Simulate hang + loop {} +} + +// Keeps our system alive +#[embassy_executor::task] +async fn watchdog_task() { + let mut handle = unsafe { embassy_nrf::wdt::WatchdogHandle::steal(0) }; + loop { + handle.pet(); + Timer::after(Duration::from_secs(2)).await; + } +} diff --git a/tests/boot/nrf/src/bin/update_success.rs b/tests/boot/nrf/src/bin/update_success.rs index 60e11452..4da0c6b7 100644 --- a/tests/boot/nrf/src/bin/update_success.rs +++ b/tests/boot/nrf/src/bin/update_success.rs @@ -7,49 +7,46 @@ teleprobe_meta::target!(b"nrf52840-dk"); mod common; use common::*; -use embassy_boot_nrf::{FirmwareUpdater, FirmwareUpdaterConfig}; -use embassy_embedded_hal::adapter::BlockingAsync; +use embassy_boot_nrf::{BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; -use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; +use embassy_time::{Timer, Duration}; use embassy_nrf::nvmc::Nvmc; -use embassy_nrf::wdt::{self, Watchdog}; -use embassy_sync::mutex::Mutex; -use panic_reset as _; +use embassy_sync::blocking_mutex::Mutex; +use core::cell::RefCell; #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(s: Spawner) { let p = embassy_nrf::init(Default::default()); - let wdt_config = wdt::Config::try_new(&p.WDT).unwrap(); - let (_wdt, [_wdt_handle]) = match Watchdog::try_new(p.WDT, wdt_config) { - Ok(x) => x, - Err(_) => { - // Watchdog already active with the wrong number of handles, waiting for it to timeout... - loop { - cortex_m::asm::wfe(); - } - } - }; + s.spawn(watchdog_task()).unwrap(); let nvmc = Nvmc::new(p.NVMC); - let nvmc = Mutex::new(BlockingAsync::new(nvmc)); + let nvmc = Mutex::new(RefCell::new(nvmc)); - let test_state = Partition::new(&mut ) - let config = FirmwareUpdaterConfig::from_linkerfile(&nvmc); - let mut magic = [0; 4]; - let mut updater = FirmwareUpdater::new(config, &mut magic); + let mut state = TestState::init(&nvmc); + if state.num_boots == 1 { + let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&nvmc); - // Check state if we've attempted update yet - let dfu = updater.prepare().await.unwrap(); + let mut magic = [0; 4]; + let mut updater = BlockingFirmwareUpdater::new(config, &mut magic); - let mut offset = 0; - for chunk in APP_B.chunks(4096) { - let mut buf: [u8; 4096] = [0; 4096]; - buf[..chunk.len()].copy_from_slice(chunk); - updater.write_firmware(offset, &buf).await.unwrap(); - offset += chunk.len(); + updater.mark_booted().unwrap(); + state.inc(&nvmc); + cortex_m::peripheral::SCB::sys_reset(); + } else if state.num_boots == 2 { + // Test success! + cortex_m::asm::bkpt(); + } else { + defmt::panic!("Unexpected number of boots"); + } +} + +// Keeps our system alive +#[embassy_executor::task] +async fn watchdog_task() { + let mut handle = unsafe { embassy_nrf::wdt::WatchdogHandle::steal(0) }; + loop { + handle.pet(); + Timer::after(Duration::from_secs(2)).await; } - updater.mark_updated().await.unwrap(); - led.set_high(); - cortex_m::peripheral::SCB::sys_reset(); } diff --git a/tests/boot/nrf/src/common.rs b/tests/boot/nrf/src/common.rs index 2f01f57d..22bc1bd1 100644 --- a/tests/boot/nrf/src/common.rs +++ b/tests/boot/nrf/src/common.rs @@ -1,12 +1,58 @@ #![macro_use] +use core::cell::RefCell; + #[allow(unused)] pub use defmt::*; -use embassy_embedded_hal::flash::partition::asynch::Partition; +use embassy_embedded_hal::flash::partition::BlockingPartition; use embassy_nrf::nvmc::Nvmc; use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use {defmt_rtt as _, panic_probe as _}; -pub struct TestState<'a> { - flash: Partition<'a, NoopRawMutex, Nvmc<'a>>, +pub struct TestState { + pub num_boots: u32, +} + +impl TestState { + pub fn init(flash: &Mutex>>) -> Self { + extern "C" { + static __test_state_start: u32; + static __test_state_end: u32; + } + + let mut flash = unsafe { + let start = &__test_state_start as *const u32 as u32; + let end = &__test_state_end as *const u32 as u32; + BlockingPartition::new(flash, start, end - start) + }; + + let mut buf = [0; 4]; + let _ = flash.read(0, &mut buf).unwrap(); + if &buf[..] == &[0xff, 0xff, 0xff, 0xff] { + TestState { num_boots: 0 } + } else { + TestState { + num_boots: u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]), + } + } + } + + pub fn inc(&mut self, flash: &Mutex>>) { + extern "C" { + static __test_state_start: u32; + static __test_state_end: u32; + } + + let mut flash = unsafe { + let start = &__test_state_start as *const u32 as u32; + let end = &__test_state_end as *const u32 as u32; + BlockingPartition::new(flash, start, end - start) + }; + self.num_boots += 1; + + flash.erase(0, 4096).unwrap(); + flash.write(0, &self.num_boots.to_be_bytes()).unwrap(); + } }