Skip to content

Commit 88e80a2

Browse files
committed
bootutil: swap-move: Avoid rewriting the secondary trailer on revert
When using the swap-move strategy, at the very beginning of the revert process, the secondary trailer was rewritten to make the revert look like a permanent upgrade in case an unfortunate reset occurs when rewriting the primary trailer. This was possible because the assumption was that no sector contained both part of the firmware and part of the trailer. To relax this assumption, it is necessary to avoid having to rewrite the secondary trailer at the start of the revert process, since that could also erase firmware data. The solution chosen is to write, during the upgrade process, a "fallback trailer" at the end of the last sector that is not containing part of the slot's trailer. The fallback trailer only contains the magic field. Depending on the size of the trailer, the maximum firmware image size has to be reduced a bit (by at most 8 bytes) to ensure the fallback trailer can be written. During a revert, if a reboot occurs while the primary trailer is rewritten, the presence of the fallback trailer will make possible to determine that a revert was in progress and has to be resumed. Signed-off-by: Thomas Altenbach <[email protected]>
1 parent b428292 commit 88e80a2

File tree

4 files changed

+198
-59
lines changed

4 files changed

+198
-59
lines changed

boot/bootutil/src/bootutil_misc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include "bootutil/enc_key.h"
1717
#endif
1818

19-
static int
19+
static inline int
2020
boot_magic_decode(const uint8_t *magic)
2121
{
2222
if (memcmp(magic, BOOT_IMG_MAGIC, BOOT_MAGIC_SZ) == 0) {

boot/bootutil/src/bootutil_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ uint32_t boot_status_off(const struct flash_area *fap);
317317
int boot_read_swap_state(const struct flash_area *fap,
318318
struct boot_swap_state *state);
319319
int boot_write_magic(const struct flash_area *fap);
320+
int boot_write_magic_at_off(const struct flash_area *fap, uint32_t off);
320321
int boot_write_status(const struct boot_loader_state *state, struct boot_status *bs);
321322
int boot_write_copy_done(const struct flash_area *fap);
322323
int boot_write_image_ok(const struct flash_area *fap);

boot/bootutil/src/bootutil_public.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,23 @@ boot_read_swap_state_by_id(int flash_area_id, struct boot_swap_state *state)
302302
int
303303
boot_write_magic(const struct flash_area *fap)
304304
{
305+
int rc = 0;
305306
uint32_t off;
307+
308+
off = boot_magic_off(fap);
309+
rc = boot_write_magic_at_off(fap, off);
310+
311+
return rc;
312+
}
313+
314+
int
315+
boot_write_magic_at_off(const struct flash_area *fap, uint32_t off)
316+
{
306317
uint32_t pad_off;
307318
int rc;
308319
uint8_t magic[BOOT_MAGIC_ALIGN_SIZE];
309320
uint8_t erased_val;
310321

311-
off = boot_magic_off(fap);
312-
313322
/* image_trailer structure was modified with additional padding such that
314323
* the pad+magic ends up in a flash minimum write region. The address
315324
* returned by boot_magic_off() is the start of magic which is not the

boot/bootutil/src/swap_move.c

Lines changed: 185 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <string.h>
2424
#include "bootutil/bootutil.h"
2525
#include "bootutil_priv.h"
26+
#include "bootutil_misc.h"
2627
#include "swap_priv.h"
2728
#include "bootutil/bootutil_log.h"
2829

@@ -68,6 +69,123 @@ find_last_idx(struct boot_loader_state *state, uint32_t swap_size)
6869
return last_idx;
6970
}
7071

72+
/**
73+
* Returns the index of the sector containing the fallback trailer in the primary slot.
74+
*
75+
* The fallback trailer is needed during a revert process, in case a reboot occurs between the time
76+
* the primary slot's trailer is erased and the time it is fully rewritten. When this happens, the
77+
* bootloader will be able to detect that a revert process was in progress by the looking at the
78+
* fallback trailer. The fallback trailer is located in the last sector of the primary slot that
79+
* doesn't contain part of the slot's trailer, so in other words, in the sector right before the
80+
* first sector holding part of the slot's trailer. It is written at the end of the upgrade process.
81+
*
82+
* Example:
83+
* PRIMARY
84+
* | ... |
85+
* +-----------------------+
86+
* | Firmware (3968 bytes) | Sector N-2
87+
* | Padding (128 bytes) | <---- Fallback trailer stored here
88+
* +-----------------------+
89+
* | Padding (3968 bytes) | Sector N-1
90+
* | Trailer (128 bytes) |
91+
* +-----------------------+
92+
* | Trailer (4096 bytes) | Sector N
93+
* | . |
94+
* +-----------------------+
95+
*
96+
* @param state Current bootloader's state.
97+
*
98+
* @return The index of the sector containg the fallback trailer in the primary slot.
99+
*/
100+
static size_t
101+
get_fallback_trailer_sector(struct boot_loader_state *state)
102+
{
103+
size_t first_trailer_sector = boot_get_first_trailer_sector(state, BOOT_PRIMARY_SLOT);
104+
105+
return first_trailer_sector - 1;
106+
}
107+
108+
/**
109+
* Returns the offset of the fallback trailer in the primary slot.
110+
*
111+
* The fallback trailer is composed only of the magic field. When computing the maximum image size,
112+
* it is ensured at least BOOT_MAGIC_ALIGN_SIZE bytes are available at the end of the firmware image
113+
* to write the fallback trailer.
114+
*
115+
* @param state Current bootloader's state.
116+
*
117+
* @return The offset of the fallback trailer in the primary slot.
118+
*/
119+
static uint32_t
120+
get_fallback_trailer_off(struct boot_loader_state *state)
121+
{
122+
size_t fallback_trailer_sector = get_fallback_trailer_sector(state);
123+
size_t end_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, fallback_trailer_sector + 1);
124+
125+
return end_off - BOOT_MAGIC_ALIGN_SIZE;
126+
}
127+
128+
/**
129+
* Writes the fallback trailer in the primary slot.
130+
*
131+
* @param state Current bootloader's state.
132+
* @param need_erase True if the sector has to be erased before writing, false otherwise.
133+
*/
134+
static void
135+
write_fallback_trailer(struct boot_loader_state *state, bool need_erase)
136+
{
137+
int rc;
138+
uint32_t fallback_trailer_off;
139+
const struct flash_area *fap_pri;
140+
141+
fap_pri = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT);
142+
assert(fap_pri != NULL);
143+
144+
if (need_erase) {
145+
size_t fallback_trailer_sector = get_fallback_trailer_sector(state);
146+
uint32_t sector_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, fallback_trailer_sector);
147+
uint32_t sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
148+
149+
rc = boot_erase_region(fap_pri, sector_off, sector_sz);
150+
assert(rc == 0);
151+
}
152+
153+
fallback_trailer_off = get_fallback_trailer_off(state);
154+
155+
rc = boot_write_magic_at_off(fap_pri, fallback_trailer_off);
156+
assert(rc == 0);
157+
}
158+
159+
/**
160+
* Reads the magic field of the fallback trailer.
161+
*
162+
* @param state Current bootloader's state.
163+
*
164+
* @return BOOT_MAGIC_GOOD if the magic is valid, BOOT_MAGIC_BAD otherwise.
165+
*/
166+
static int
167+
read_fallback_trailer_magic(struct boot_loader_state *state)
168+
{
169+
int rc;
170+
const struct flash_area *fap_pri;
171+
uint32_t fallback_trailer_off;
172+
uint32_t magic_off;
173+
uint8_t magic[BOOT_MAGIC_SZ];
174+
175+
fap_pri = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT);
176+
assert(fap_pri != NULL);
177+
178+
fallback_trailer_off = get_fallback_trailer_off(state);
179+
180+
/* The magic has to be read without including the padding */
181+
magic_off = fallback_trailer_off + BOOT_MAGIC_ALIGN_SIZE - BOOT_MAGIC_SZ;
182+
183+
rc = flash_area_read(fap_pri, magic_off, magic, BOOT_MAGIC_SZ);
184+
assert(rc == 0);
185+
186+
return boot_magic_decode(magic);
187+
}
188+
71189
int
72190
boot_read_image_header(struct boot_loader_state *state, int slot,
73191
struct image_header *out_hdr, struct boot_status *bs)
@@ -337,6 +455,8 @@ boot_slots_compatible(struct boot_loader_state *state)
337455
int
338456
swap_status_source(struct boot_loader_state *state)
339457
{
458+
const struct flash_area *fap_pri;
459+
const struct flash_area *fap_sec;
340460
struct boot_swap_state state_primary_slot;
341461
struct boot_swap_state state_secondary_slot;
342462
int rc;
@@ -349,14 +469,18 @@ swap_status_source(struct boot_loader_state *state)
349469

350470
image_index = BOOT_CURR_IMG(state);
351471

352-
rc = boot_read_swap_state(state->imgs[image_index][BOOT_PRIMARY_SLOT].area,
353-
&state_primary_slot);
472+
fap_pri = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT);
473+
assert(fap_pri != NULL);
474+
475+
fap_sec = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
476+
assert(fap_sec != NULL);
477+
478+
rc = boot_read_swap_state(fap_pri, &state_primary_slot);
354479
assert(rc == 0);
355480

356481
BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
357482

358-
rc = boot_read_swap_state(state->imgs[image_index][BOOT_SECONDARY_SLOT].area,
359-
&state_secondary_slot);
483+
rc = boot_read_swap_state(fap_sec, &state_secondary_slot);
360484
assert(rc == 0);
361485

362486
BOOT_LOG_SWAP_STATE("Secondary image", &state_secondary_slot);
@@ -376,6 +500,31 @@ swap_status_source(struct boot_loader_state *state)
376500
return source;
377501
}
378502

503+
/* If both primary and secondary trailers are absent and the fallback trailer is present, it
504+
* means a revert process was interrupted in the middle of rewriting of the primary trailer.
505+
* This can only happen at the very beggining of the revert process.
506+
*/
507+
if (state_primary_slot.magic != BOOT_MAGIC_GOOD &&
508+
state_secondary_slot.magic != BOOT_MAGIC_GOOD &&
509+
read_fallback_trailer_magic(state) == BOOT_MAGIC_GOOD) {
510+
/* In that case, the primary trailer is rewritten with just enough data to allow to the
511+
* revert process to be resumed. Only the swap-type and the magic are needed since we are at
512+
* the beginning of the revert process so the process has to be restarted from scratch,
513+
* which will rewrite the primary trailer with all required data (swap size, encryption
514+
* keys, ...).
515+
*/
516+
rc = swap_scramble_trailer_sectors(state, fap_pri);
517+
assert(rc == 0);
518+
519+
rc = boot_write_swap_info(fap_pri, BOOT_SWAP_TYPE_REVERT, image_index);
520+
assert(rc == 0);
521+
522+
rc = boot_write_magic(fap_pri);
523+
assert(rc == 0);
524+
525+
return BOOT_STATUS_SOURCE_PRIMARY_SLOT;
526+
}
527+
379528
BOOT_LOG_INF("Boot source: none");
380529
return BOOT_STATUS_SOURCE_NONE;
381530
}
@@ -460,10 +609,28 @@ boot_swap_sectors(size_t idx, size_t last_idx, uint32_t sz, struct boot_loader_s
460609
sec_off = boot_img_sector_off(state, BOOT_SECONDARY_SLOT, idx - 1);
461610

462611
if (bs->state == BOOT_STATUS_STATE_0) {
612+
uint32_t copy_sz = sz;
613+
size_t fallback_trailer_sector = get_fallback_trailer_sector(state);
614+
463615
rc = boot_erase_region(fap_pri, pri_off, sz);
464616
assert(rc == 0);
465617

466-
rc = boot_copy_region(state, fap_sec, fap_pri, sec_off, pri_off, sz);
618+
/* The last sector containing part of the firmware image is about to be written to the
619+
* primary slot. If we are not reverting, a fallback trailer must be written in case a
620+
* revert is performed later and is interrupted. The fallback trailer is located in the last
621+
* sector not containing part of the slot's trailer. This sector can be the one being
622+
* written at this step, so in that case write the fallback trailer now. Otherwise, it will
623+
* be written at the very end of the swap.
624+
*/
625+
if (fallback_trailer_sector == idx - 1 && bs->swap_type != BOOT_SWAP_TYPE_REVERT) {
626+
/* The sector has already been erased, no need to erase twice */
627+
write_fallback_trailer(state, false);
628+
629+
/* Adjust the copy size to ensure the fallback trailer won't be rewritten. */
630+
copy_sz = bs->swap_size % sz;
631+
}
632+
633+
rc = boot_copy_region(state, fap_sec, fap_pri, sec_off, pri_off, copy_sz);
467634
assert(rc == 0);
468635

469636
rc = boot_write_status(state, bs);
@@ -518,55 +685,6 @@ boot_swap_sectors(size_t idx, size_t last_idx, uint32_t sz, struct boot_loader_s
518685
}
519686
}
520687

521-
/*
522-
* When starting a revert the swap status exists in the primary slot, and
523-
* the status in the secondary slot is erased. To start the swap, the status
524-
* area in the primary slot must be re-initialized; if during the small
525-
* window of time between re-initializing it and writing the first metadata
526-
* a reset happens, the swap process is broken and cannot be resumed.
527-
*
528-
* This function handles the issue by making the revert look like a permanent
529-
* upgrade (by initializing the secondary slot).
530-
*/
531-
void
532-
fixup_revert(const struct boot_loader_state *state, struct boot_status *bs,
533-
const struct flash_area *fap_sec)
534-
{
535-
struct boot_swap_state swap_state;
536-
int rc;
537-
538-
#if (BOOT_IMAGE_NUMBER == 1)
539-
(void)state;
540-
#endif
541-
542-
/* No fixup required */
543-
if (bs->swap_type != BOOT_SWAP_TYPE_REVERT ||
544-
bs->op != BOOT_STATUS_OP_MOVE ||
545-
bs->idx != BOOT_STATUS_IDX_0) {
546-
return;
547-
}
548-
549-
rc = boot_read_swap_state(fap_sec, &swap_state);
550-
assert(rc == 0);
551-
552-
BOOT_LOG_SWAP_STATE("Secondary image", &swap_state);
553-
554-
if (swap_state.magic == BOOT_MAGIC_UNSET) {
555-
/* Remove trailer and prepare area for write on devices requiring erase */
556-
rc = swap_scramble_trailer_sectors(state, fap_sec);
557-
assert(rc == 0);
558-
559-
rc = boot_write_image_ok(fap_sec);
560-
assert(rc == 0);
561-
562-
rc = boot_write_swap_size(fap_sec, bs->swap_size);
563-
assert(rc == 0);
564-
565-
rc = boot_write_magic(fap_sec);
566-
assert(rc == 0);
567-
}
568-
}
569-
570688
void
571689
swap_run(struct boot_loader_state *state, struct boot_status *bs,
572690
uint32_t copy_size)
@@ -577,6 +695,7 @@ swap_run(struct boot_loader_state *state, struct boot_status *bs,
577695
uint32_t trailer_sz;
578696
uint32_t first_trailer_idx;
579697
uint32_t last_idx;
698+
size_t fallback_trailer_sector;
580699
const struct flash_area *fap_pri;
581700
const struct flash_area *fap_sec;
582701

@@ -617,8 +736,6 @@ swap_run(struct boot_loader_state *state, struct boot_status *bs,
617736
fap_sec = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
618737
assert(fap_sec != NULL);
619738

620-
fixup_revert(state, bs, fap_sec);
621-
622739
if (bs->op == BOOT_STATUS_OP_MOVE) {
623740
idx = last_idx;
624741
while (idx > 0) {
@@ -639,6 +756,18 @@ swap_run(struct boot_loader_state *state, struct boot_status *bs,
639756
}
640757
idx++;
641758
}
759+
760+
/* The swap is done, if we are upgrading and the fallback trailer has not been written during
761+
* the swap, write it now. This is necessary if the fallback trailer sector is not part of the
762+
* sectors that have been swapped, either because the images are not large enough to include
763+
* that sector, or because the trailer is sector-aligned and the fallback trailer is therefore
764+
* stored at the end of the padding sector.
765+
*/
766+
fallback_trailer_sector = get_fallback_trailer_sector(state);
767+
768+
if (last_idx <= fallback_trailer_sector && bs->swap_type != BOOT_SWAP_TYPE_REVERT) {
769+
write_fallback_trailer(state, true);
770+
}
642771
}
643772

644773
int app_max_size(struct boot_loader_state *state)

0 commit comments

Comments
 (0)