From 23c9d23d66ada5cf713dac8ff9610da1bd248090 Mon Sep 17 00:00:00 2001 From: Geo Palakunnel Date: Mon, 26 Oct 2026 17:27:19 -0700 Subject: [PATCH] Retry target after an unusable backup When the journal selects an unusable backup, try the distinct target rather than aborting without checking it during this boot. Record Attempting before handoff so the app tracks its running and update slots correctly. --- README.md | 2 ++ libs/ec-slimloader/src/lib.rs | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 78ac862d..8ba154a4 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ The `ec-slimloader` crate will handle for you: * it will read from the state journal what image slot will be booted. * on subsequent reboots, it will fall back to your defined backup slot if you do not mark your current application image as `confirmed`. +If that backup fails validation and differs from the target, the loader also tries the target before giving up. It records `Attempting` before this reverse fallback, so the application identifies the correct running slot and inactive update slot. Each distinct candidate is checked at most once per boot. + However, some aspects are handled by the platform support crate (and can differ from project-to-project): * how application images are loaded. For `ec-slimloader-imxrt` images are copied to RAM in a quite chip-specific way. Typically for other platforms you might want to swap images between on-chip NOR flash and external NOR flash. The latter method is not implemented in this repository (yet). * how application images are verified. By default the images themselves are not checked at all. `ec-slimloader-imxrt` leverages the native NXP authentication routines to check image integrity. diff --git a/libs/ec-slimloader/src/lib.rs b/libs/ec-slimloader/src/lib.rs index 14cac4c1..32de9c9e 100644 --- a/libs/ec-slimloader/src/lib.rs +++ b/libs/ec-slimloader/src/lib.rs @@ -164,6 +164,16 @@ pub async fn start(config: B::Config info!("Attempting to boot backup in {:?}", slot); let error = board.check_and_boot(&state.backup()).await; // If this function returns, it implies that the boot has failed. warn!("Failed to boot backup in {:?} because {:?}", slot, error); + } else if slot != state.target() { + // We started in [Attempting] or [Failed], so the backup was tried first and could not boot. + // The target has not been tried this boot; an interrupted earlier attempt may have left it usable. + // Try the target as a last resort, recording [Attempting] in the journal so the app + // identifies the target as running and writes future updates to the backup. + + set_status::<_, JOURNAL_BUFFER_SIZE>(&mut board, &mut state, Status::Attempting).await; + info!("Backup unusable, retrying target {:?}", state.target()); + let error = board.check_and_boot(&state.target()).await; + warn!("Failed to boot target {:?} because {:?}", state.target(), error); } error!("No candidates booted successfully, giving up...");