Skip to content

Commit d1a3b95

Browse files
taltenbachnordicjm
authored andcommitted
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 66d41e7 commit d1a3b95

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
@@ -534,6 +534,33 @@ find_swap_count(const struct boot_loader_state *state, uint32_t copy_size)
534534
return swap_count;
535535
}
536536

537+
/**
538+
* Finds the first sector of a given slot that holds image trailer data.
539+
*
540+
* @param state Current bootloader's state.
541+
* @param slot The index of the slot to consider.
542+
* @param trailer_sz The size of the trailer, in bytes.
543+
*
544+
* @return The index of the first sector of the slot that holds image trailer data.
545+
*/
546+
static size_t
547+
get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz)
548+
{
549+
size_t first_trailer_sector = boot_img_num_sectors(state, slot) - 1;
550+
size_t sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);
551+
size_t trailer_sector_sz = sector_sz;
552+
553+
while (trailer_sector_sz < trailer_sz) {
554+
/* Consider that the image trailer may span across sectors of different sizes */
555+
--first_trailer_sector;
556+
sector_sz = boot_img_sector_size(state, slot, first_trailer_sector);
557+
558+
trailer_sector_sz += sector_sz;
559+
}
560+
561+
return first_trailer_sector;
562+
}
563+
537564
/**
538565
* Swaps the contents of two flash regions within the two image slots.
539566
*
@@ -554,11 +581,10 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
554581
const struct flash_area *fap_scratch;
555582
uint32_t copy_sz;
556583
uint32_t trailer_sz;
557-
uint32_t sector_sz;
558584
uint32_t img_off;
559585
uint32_t scratch_trailer_off;
560586
struct boot_swap_state swap_state;
561-
size_t last_sector;
587+
size_t first_trailer_sector_primary;
562588
bool erase_scratch;
563589
uint8_t image_index;
564590
int rc;
@@ -581,33 +607,23 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
581607
trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
582608

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

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

613629
/* Check if the computed copy size would cause the beginning of the trailer in the scratch
@@ -666,29 +682,69 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
666682
}
667683

668684
if (bs->state == BOOT_STATUS_STATE_1) {
669-
rc = boot_erase_region(fap_secondary_slot, img_off, sz);
670-
assert(rc == 0);
671-
672-
rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
673-
img_off, img_off, copy_sz);
674-
assert(rc == 0);
685+
uint32_t erase_sz = sz;
675686

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

716+
rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
717+
img_off, img_off, copy_sz);
718+
assert(rc == 0);
719+
684720
rc = boot_write_status(state, bs);
685721
bs->state = BOOT_STATUS_STATE_2;
686722
BOOT_STATUS_ASSERT(rc == 0);
687723
}
688724

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

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

0 commit comments

Comments
 (0)