Skip to content

Commit 1b6abae

Browse files
committed
boot: bootutil: Ensure the whole trailer is erased when it is large
When using swap-scratch and the trailer is too large to fit in a single sector, might not be fully erased in both the primary and the secondary slot if the last sector containing firmware data also contains part of the trailer. Indeed, in that case, it might happen that only the first part of the trailer was erased, causing issues e.g. when attempting to rewrite the trailer in the primary slot during the upgrade. Signed-off-by: Thomas Altenbach <[email protected]>
1 parent 1bf5c9f commit 1b6abae

File tree

1 file changed

+91
-35
lines changed

1 file changed

+91
-35
lines changed

boot/bootutil/src/swap_scratch.c

Lines changed: 91 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,33 @@ find_swap_count(const struct boot_loader_state *state, uint32_t copy_size)
531531
return swap_count;
532532
}
533533

534+
/**
535+
* Finds the first sector of a given slot that holds image trailer data.
536+
*
537+
* @param state Current bootloader's state.
538+
* @param slot The index of the slot to consider.
539+
* @param trailer_sz The size of the trailer, in bytes.
540+
*
541+
* @return The index of the first sector of the slot that holds image trailer data.
542+
*/
543+
static size_t
544+
get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz)
545+
{
546+
size_t first_trailer_sector = boot_img_num_sectors(state, slot) - 1;
547+
size_t sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);
548+
size_t trailer_sector_sz = sector_sz;
549+
550+
while (trailer_sector_sz < trailer_sz) {
551+
/* Consider that the image trailer may span across sectors of different sizes */
552+
--first_trailer_sector;
553+
sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);
554+
555+
trailer_sector_sz += sector_sz;
556+
}
557+
558+
return first_trailer_sector;
559+
}
560+
534561
/**
535562
* Swaps the contents of two flash regions within the two image slots.
536563
*
@@ -551,11 +578,10 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
551578
const struct flash_area *fap_scratch;
552579
uint32_t copy_sz;
553580
uint32_t trailer_sz;
554-
uint32_t sector_sz;
555581
uint32_t img_off;
556582
uint32_t scratch_trailer_off;
557583
struct boot_swap_state swap_state;
558-
size_t last_sector;
584+
size_t first_trailer_sector_primary;
559585
bool erase_scratch;
560586
uint8_t image_index;
561587
int rc;
@@ -578,33 +604,23 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
578604
trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
579605

580606
/* sz in this function is always sized on a multiple of the sector size.
581-
* The check against the start offset of the last sector
582-
* is to determine if we're swapping the last sector. The last sector
583-
* needs special handling because it's where the trailer lives. If we're
584-
* copying it, we need to use scratch to write the trailer temporarily.
607+
* The check against the start offset of the first trailer sector is to determine if we're
608+
* swapping that sector, which might contains both part of the firmware image and part of the
609+
* trailer (or the whole trailer if the latter is small enough). Therefore, that sector needs
610+
* special handling: if we're copying it, we need to use scratch to write the trailer
611+
* temporarily.
612+
*
613+
* Since the primary and secondary slots don't necessarily have the same layout, the index of
614+
* the first trailer sector may be different for each slot.
585615
*
586616
* NOTE: `use_scratch` is a temporary flag (never written to flash) which
587-
* controls if special handling is needed (swapping last sector).
617+
* controls if special handling is needed (swapping the first trailer sector).
588618
*/
589-
last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
590-
sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, last_sector);
591-
592-
if (sector_sz < trailer_sz) {
593-
uint32_t trailer_sector_sz = sector_sz;
594-
595-
while (trailer_sector_sz < trailer_sz) {
596-
/* Consider that the image trailer may span across sectors of
597-
* different sizes.
598-
*/
599-
sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, --last_sector);
600-
601-
trailer_sector_sz += sector_sz;
602-
}
603-
}
619+
first_trailer_sector_primary = get_first_trailer_sector(state, BOOT_PRIMARY_SLOT, trailer_sz);
604620

605621
/* Check if the currently swapped sector(s) contain the trailer or part of it */
606622
if ((img_off + sz) >
607-
boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
623+
boot_img_sector_off(state, BOOT_PRIMARY_SLOT, first_trailer_sector_primary)) {
608624
copy_sz = flash_area_get_size(fap_primary_slot) - img_off - trailer_sz;
609625

610626
/* Check if the computed copy size would cause the beginning of the trailer in the scratch
@@ -663,29 +679,69 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
663679
}
664680

665681
if (bs->state == BOOT_STATUS_STATE_1) {
666-
rc = boot_erase_region(fap_secondary_slot, img_off, sz);
667-
assert(rc == 0);
668-
669-
rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
670-
img_off, img_off, copy_sz);
671-
assert(rc == 0);
682+
uint32_t erase_sz = sz;
672683

673-
if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
674-
/* If not all sectors of the slot are being swapped,
675-
* guarantee here that only the primary slot will have the state.
676-
*/
684+
if (bs->idx == BOOT_STATUS_IDX_0) {
685+
/* Guarantee here that only the primary slot will have the state.
686+
*
687+
* This is necessary even though the current area being swapped contains part of the
688+
* trailer since in case the trailer spreads over multiple sector erasing the [img_off,
689+
* img_off + sz) might not erase the entire trailer.
690+
*/
677691
rc = swap_scramble_trailer_sectors(state, fap_secondary_slot);
678692
assert(rc == 0);
693+
694+
if (bs->use_scratch) {
695+
/* If the area being swapped contains the trailer or part of it, ensure the
696+
* sector(s) containing the beginning of the trailer won't be erased again.
697+
*/
698+
size_t trailer_sector_secondary =
699+
get_first_trailer_sector(state, BOOT_SECONDARY_SLOT, trailer_sz);
700+
701+
uint32_t trailer_sector_offset =
702+
boot_img_sector_off(state, BOOT_SECONDARY_SLOT, trailer_sector_secondary);
703+
704+
erase_sz = trailer_sector_offset - img_off;
705+
}
706+
}
707+
708+
if (erase_sz > 0) {
709+
rc = boot_erase_region(fap_secondary_slot, img_off, erase_sz);
710+
assert(rc == 0);
679711
}
680712

713+
rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
714+
img_off, img_off, copy_sz);
715+
assert(rc == 0);
716+
681717
rc = boot_write_status(state, bs);
682718
bs->state = BOOT_STATUS_STATE_2;
683719
BOOT_STATUS_ASSERT(rc == 0);
684720
}
685721

686722
if (bs->state == BOOT_STATUS_STATE_2) {
687-
rc = boot_erase_region(fap_primary_slot, img_off, sz);
688-
assert(rc == 0);
723+
uint32_t erase_sz = sz;
724+
725+
if (bs->use_scratch) {
726+
/* The current area that is being swapped contains the trailer or part of it. In that
727+
* case, make sure to erase all sectors containing the trailer in the primary slot to be
728+
* able to write the new trailer. This is not always equivalent to erasing the [img_off,
729+
* img_off + sz) range when the trailer spreads across multiple sectors.
730+
*/
731+
rc = swap_erase_trailer_sectors(state, fap_primary_slot);
732+
assert(rc == 0);
733+
734+
/* Ensure the sector(s) containing the beginning of the trailer won't be erased twice */
735+
uint32_t trailer_sector_off =
736+
boot_img_sector_off(state, BOOT_PRIMARY_SLOT, first_trailer_sector_primary);
737+
738+
erase_sz = trailer_sector_off - img_off;
739+
}
740+
741+
if (erase_sz > 0) {
742+
rc = boot_erase_region(fap_primary_slot, img_off, erase_sz);
743+
assert(rc == 0);
744+
}
689745

690746
/* NOTE: If this is the final sector, we exclude the image trailer from
691747
* this copy (copy_sz was truncated earlier).

0 commit comments

Comments
 (0)