Skip to content

Commit 97a87d3

Browse files
committed
Custom trailer: record the rollback direction in the UPDATE state
1 parent 1b9cb03 commit 97a87d3

2 files changed

Lines changed: 99 additions & 30 deletions

File tree

‎src/update_flash.c‎

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -753,39 +753,48 @@ static int RAMFUNCTION wolfBoot_swap_and_final_erase(int resume)
753753
#endif /* !DISABLE_BACKUP && !CUSTOM_PARTITION_TRAILER */
754754

755755
#if !defined(DISABLE_BACKUP) && defined(CUSTOM_PARTITION_TRAILER)
756-
/**
757-
* @brief Final step of the swap with a custom trailer, power-fail safe.
758-
*
759-
* Resetting a custom trailer to NEW rewrites it, once per sector flag, so
760-
* UPDATE leaves UPDATING first (FINAL_FLAGS only clears bits, written in
761-
* place): a resumed swap would otherwise swap again the sectors already
762-
* reset to NEW. BOOT is set before the UPDATE trailer is reset: once that
763-
* reset starts, a cut can only leave an erased trailer, which reads as NEW.
764-
*
765-
* @param resume If 1, only completes a final step interrupted by a cut
766-
* @param bootState State to set in BOOT (TESTING, or SUCCESS on rollback)
767-
* @return 0 if the final step ran, -1 if there was nothing to resume
768-
*/
769-
static int RAMFUNCTION wolfBoot_custom_trailer_final(int resume,
770-
uint8_t bootState)
756+
/* UPDATE states of a rollback: header versions are mixed during the swap, so
757+
* the direction is recorded when it starts. ROLLBACK, FINAL_FLAGS and
758+
* FINAL_ROLLBACK are reached from the previous state by programming only, so
759+
* they are written in place: a trailer rewrite could be cut half-way. */
760+
#ifndef WOLFBOOT_FLAGS_INVERT
761+
#define IMG_STATE_ROLLBACK 0x60
762+
#define IMG_STATE_FINAL_ROLLBACK 0x20
763+
#else
764+
#define IMG_STATE_ROLLBACK 0x9F
765+
#define IMG_STATE_FINAL_ROLLBACK 0xDF
766+
#endif
767+
768+
/* Resetting the sector flags rewrites the custom trailer, once per flag:
769+
* UPDATE leaves UPDATING first, or a resumed swap would swap again the
770+
* sectors already reset. BOOT is set before: a cut in the reset then leaves
771+
* at worst an erased trailer, which reads as NEW. */
772+
static int RAMFUNCTION wolfBoot_custom_trailer_final(int resume, int rollback)
771773
{
772774
uint8_t updateState;
773775
uint32_t sector;
774776

775-
if (resume && ((wolfBoot_get_partition_state(PART_UPDATE,
776-
&updateState) != 0) || (updateState != IMG_STATE_FINAL_FLAGS)))
777-
return -1;
777+
if (resume) {
778+
if (wolfBoot_get_partition_state(PART_UPDATE, &updateState) != 0)
779+
return -1;
780+
if (updateState == IMG_STATE_FINAL_FLAGS)
781+
rollback = 0;
782+
else if (updateState == IMG_STATE_FINAL_ROLLBACK)
783+
rollback = 1;
784+
else
785+
return -1;
786+
}
778787

779788
hal_flash_unlock();
780789
#ifdef EXT_FLASH
781790
ext_flash_unlock();
782791
#endif
783-
if (!resume)
784-
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_FINAL_FLAGS);
785-
/* Mark boot partition as TESTING - this tells bootloader to fallback if update fails */
786-
wolfBoot_set_partition_state(PART_BOOT, bootState);
787-
/* Reset the sector flags, so a fallback swaps every sector again, then
788-
* consume the update partition */
792+
if (!resume) {
793+
wolfBoot_set_partition_state(PART_UPDATE,
794+
rollback ? IMG_STATE_FINAL_ROLLBACK : IMG_STATE_FINAL_FLAGS);
795+
}
796+
wolfBoot_set_partition_state(PART_BOOT,
797+
rollback ? IMG_STATE_SUCCESS : IMG_STATE_TESTING);
789798
for (sector = 0; (sector * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE;
790799
sector++)
791800
wolfBoot_set_update_sector_flag(sector, SECT_FLAG_NEW);
@@ -1302,6 +1311,13 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
13021311
#endif
13031312

13041313
wolfBoot_get_update_sector_flag(0, &flag);
1314+
#if !defined(DISABLE_BACKUP) && defined(CUSTOM_PARTITION_TRAILER)
1315+
if (flag != SECT_FLAG_NEW) {
1316+
uint8_t updateState;
1317+
rollback_needed = (wolfBoot_get_partition_state(PART_UPDATE,
1318+
&updateState) == 0) && (updateState == IMG_STATE_ROLLBACK);
1319+
}
1320+
#endif
13051321
/* Check the first sector to detect interrupted update */
13061322
if (flag == SECT_FLAG_NEW) {
13071323
if (((update_type & HDR_IMG_TYPE_PART_MASK) != HDR_IMG_TYPE_APP) ||
@@ -1425,6 +1441,10 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
14251441
#ifdef EXT_FLASH
14261442
ext_flash_unlock();
14271443
#endif
1444+
#ifdef CUSTOM_PARTITION_TRAILER
1445+
if (rollback_needed)
1446+
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_ROLLBACK);
1447+
#endif
14281448
/* Interruptible swap
14291449
* The status is saved in the sector flags of the update partition.
14301450
* If something goes wrong, the operation will be resumed upon reboot.
@@ -1575,9 +1595,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
15751595
}
15761596
#endif
15771597
#else
1578-
/* A custom trailer is not erased with the partitions */
1579-
(void)wolfBoot_custom_trailer_final(0,
1580-
rollback_needed ? IMG_STATE_SUCCESS : IMG_STATE_TESTING);
1598+
(void)wolfBoot_custom_trailer_final(0, rollback_needed);
15811599
#endif
15821600

15831601
#else /* DISABLE_BACKUP */
@@ -1880,9 +1898,8 @@ void RAMFUNCTION wolfBoot_start(void)
18801898
if ((resumedFinalErase != 0) ||
18811899
((bootRet == 0) && (bootState == IMG_STATE_TESTING)))
18821900
#elif !defined(DISABLE_BACKUP)
1883-
/* resume the final step in case the power failed before it finished:
1884-
* the new image boots in TESTING */
1885-
if (wolfBoot_custom_trailer_final(1, IMG_STATE_TESTING) != 0)
1901+
/* resume the final step in case the power failed before it finished */
1902+
if (wolfBoot_custom_trailer_final(1, 0) != 0)
18861903
#endif
18871904
{
18881905
/* Check if the BOOT partition is still in TESTING,

‎tools/unit-tests/unit-update-flash.c‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,49 @@ START_TEST (test_custom_trailer_power_cut)
12011201
ck_assert_int_gt(cut, 2);
12021202
}
12031203
END_TEST
1204+
1205+
/* Cut the power at every custom trailer write of a rollback: the image that
1206+
* failed to confirm must never come back */
1207+
START_TEST (test_custom_trailer_rollback_power_cut)
1208+
{
1209+
int cut;
1210+
int done = 0;
1211+
uint8_t st;
1212+
1213+
for (cut = 1; !done; cut++) {
1214+
reset_mock_stats();
1215+
prepare_flash();
1216+
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
1217+
add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL);
1218+
wolfBoot_update_trigger();
1219+
wolfBoot_start();
1220+
ck_assert_uint_eq(wolfBoot_current_firmware_version(), 2);
1221+
/* v2 not confirmed: this boot rolls back */
1222+
trailer_cut_countdown = cut;
1223+
if (setjmp(trailer_cut_jmp) == 0) {
1224+
wolfBoot_start();
1225+
done = 1;
1226+
}
1227+
else {
1228+
locked = ext_locked = 1;
1229+
}
1230+
trailer_cut_countdown = 0;
1231+
if (!done)
1232+
wolfBoot_start();
1233+
ck_assert_msg(!wolfBoot_panicked, "cut %d: panic", cut);
1234+
ck_assert_msg(wolfBoot_current_firmware_version() == 1,
1235+
"cut %d: version %u", cut, wolfBoot_current_firmware_version());
1236+
ck_assert_int_eq(wolfBoot_get_partition_state(PART_BOOT, &st), 0);
1237+
ck_assert_msg(st == IMG_STATE_SUCCESS, "cut %d: BOOT state 0x%x",
1238+
cut, st);
1239+
wolfBoot_start();
1240+
ck_assert_msg(wolfBoot_current_firmware_version() == 1,
1241+
"cut %d: rejected image reinstalled", cut);
1242+
cleanup_flash();
1243+
}
1244+
ck_assert_int_gt(cut, 2);
1245+
}
1246+
END_TEST
12041247
#endif
12051248

12061249
START_TEST (test_sunnyday_noupdate)
@@ -1412,7 +1455,14 @@ START_TEST(test_rollback_resume_from_swapping_flag)
14121455
wolfBoot_start();
14131456
ck_assert_uint_eq(wolfBoot_current_firmware_version(), 2);
14141457
ext_flash_unlock();
1458+
#ifdef CUSTOM_PARTITION_TRAILER
1459+
/* written by the rollback before its first sector */
1460+
hal_flash_unlock();
1461+
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_ROLLBACK);
1462+
hal_flash_lock();
1463+
#else
14151464
wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING);
1465+
#endif
14161466
ext_flash_lock();
14171467
cut_after_sector0_backup_erase();
14181468
wolfBoot_start();
@@ -2517,6 +2567,8 @@ Suite *wolfboot_suite(void)
25172567
#endif
25182568
#if !defined(DISABLE_BACKUP) && defined(CUSTOM_PARTITION_TRAILER)
25192569
tcase_add_test(forward_update_samesize, test_custom_trailer_power_cut);
2570+
tcase_add_test(forward_update_samesize,
2571+
test_custom_trailer_rollback_power_cut);
25202572
#endif
25212573
#if defined(PART_SWAP_EXT) && !defined(DISABLE_BACKUP)
25222574
tcase_add_test(forward_update_samesize, test_update_resume_from_backup_flag);

0 commit comments

Comments
 (0)