@@ -534,6 +534,33 @@ find_swap_count(const struct boot_loader_state *state, uint32_t copy_size)
534
534
return swap_count ;
535
535
}
536
536
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
+
537
564
/**
538
565
* Swaps the contents of two flash regions within the two image slots.
539
566
*
@@ -554,11 +581,10 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
554
581
const struct flash_area * fap_scratch ;
555
582
uint32_t copy_sz ;
556
583
uint32_t trailer_sz ;
557
- uint32_t sector_sz ;
558
584
uint32_t img_off ;
559
585
uint32_t scratch_trailer_off ;
560
586
struct boot_swap_state swap_state ;
561
- size_t last_sector ;
587
+ size_t first_trailer_sector_primary ;
562
588
bool erase_scratch ;
563
589
uint8_t image_index ;
564
590
int rc ;
@@ -581,33 +607,23 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
581
607
trailer_sz = boot_trailer_sz (BOOT_WRITE_SZ (state ));
582
608
583
609
/* 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.
588
618
*
589
619
* 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).
591
621
*/
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 );
607
623
608
624
/* Check if the currently swapped sector(s) contain the trailer or part of it */
609
625
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 )) {
611
627
copy_sz = flash_area_get_size (fap_primary_slot ) - img_off - trailer_sz ;
612
628
613
629
/* 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,
666
682
}
667
683
668
684
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 ;
675
686
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
+ */
680
694
rc = swap_erase_trailer_sectors (state , fap_secondary_slot );
681
695
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 );
682
714
}
683
715
716
+ rc = boot_copy_region (state , fap_primary_slot , fap_secondary_slot ,
717
+ img_off , img_off , copy_sz );
718
+ assert (rc == 0 );
719
+
684
720
rc = boot_write_status (state , bs );
685
721
bs -> state = BOOT_STATUS_STATE_2 ;
686
722
BOOT_STATUS_ASSERT (rc == 0 );
687
723
}
688
724
689
725
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
+ }
692
748
693
749
/* NOTE: If this is the final sector, we exclude the image trailer from
694
750
* this copy (copy_sz was truncated earlier).
0 commit comments