@@ -531,6 +531,33 @@ find_swap_count(const struct boot_loader_state *state, uint32_t copy_size)
531
531
return swap_count ;
532
532
}
533
533
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
+
534
561
/**
535
562
* Swaps the contents of two flash regions within the two image slots.
536
563
*
@@ -551,11 +578,10 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
551
578
const struct flash_area * fap_scratch ;
552
579
uint32_t copy_sz ;
553
580
uint32_t trailer_sz ;
554
- uint32_t sector_sz ;
555
581
uint32_t img_off ;
556
582
uint32_t scratch_trailer_off ;
557
583
struct boot_swap_state swap_state ;
558
- size_t last_sector ;
584
+ size_t first_trailer_sector_primary ;
559
585
bool erase_scratch ;
560
586
uint8_t image_index ;
561
587
int rc ;
@@ -578,33 +604,23 @@ boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
578
604
trailer_sz = boot_trailer_sz (BOOT_WRITE_SZ (state ));
579
605
580
606
/* 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.
585
615
*
586
616
* 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).
588
618
*/
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 );
604
620
605
621
/* Check if the currently swapped sector(s) contain the trailer or part of it */
606
622
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 )) {
608
624
copy_sz = flash_area_get_size (fap_primary_slot ) - img_off - trailer_sz ;
609
625
610
626
/* 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,
663
679
}
664
680
665
681
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 ;
672
683
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
+ */
677
691
rc = swap_scramble_trailer_sectors (state , fap_secondary_slot );
678
692
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 );
679
711
}
680
712
713
+ rc = boot_copy_region (state , fap_primary_slot , fap_secondary_slot ,
714
+ img_off , img_off , copy_sz );
715
+ assert (rc == 0 );
716
+
681
717
rc = boot_write_status (state , bs );
682
718
bs -> state = BOOT_STATUS_STATE_2 ;
683
719
BOOT_STATUS_ASSERT (rc == 0 );
684
720
}
685
721
686
722
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
+ }
689
745
690
746
/* NOTE: If this is the final sector, we exclude the image trailer from
691
747
* this copy (copy_sz was truncated earlier).
0 commit comments