1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2019 JUUL Labs
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include <stddef.h>
20 #include <stdbool.h>
21 #include <inttypes.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "bootutil/bootutil.h"
25 #include "bootutil_priv.h"
26 #include "swap_priv.h"
27 #include "bootutil/bootutil_log.h"
28
29 #include "mcuboot_config/mcuboot_config.h"
30
31 BOOT_LOG_MODULE_DECLARE(mcuboot);
32
33 #if !defined(MCUBOOT_SWAP_USING_MOVE)
34
35 #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
36 /*
37 * FIXME: this might have to be updated for threaded sim
38 */
39 int boot_status_fails = 0;
40 #define BOOT_STATUS_ASSERT(x) \
41 do { \
42 if (!(x)) { \
43 boot_status_fails++; \
44 } \
45 } while (0)
46 #else
47 #define BOOT_STATUS_ASSERT(x) ASSERT(x)
48 #endif
49
50 #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
51 /**
52 * Reads the status of a partially-completed swap, if any. This is necessary
53 * to recover in case the boot lodaer was reset in the middle of a swap
54 * operation.
55 */
56 int
swap_read_status_bytes(const struct flash_area * fap,struct boot_loader_state * state,struct boot_status * bs)57 swap_read_status_bytes(const struct flash_area *fap,
58 struct boot_loader_state *state, struct boot_status *bs)
59 {
60 uint32_t off;
61 uint8_t status;
62 int max_entries;
63 int found;
64 int found_idx;
65 int invalid;
66 int rc;
67 int i;
68
69 off = boot_status_off(fap);
70 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
71 if (max_entries < 0) {
72 return BOOT_EBADARGS;
73 }
74
75 found = 0;
76 found_idx = 0;
77 invalid = 0;
78 for (i = 0; i < max_entries; i++) {
79 rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
80 &status, 1);
81 if (rc < 0) {
82 return BOOT_EFLASH;
83 }
84
85 if (bootutil_buffer_is_erased(fap, &status, 1)) {
86 if (found && !found_idx) {
87 found_idx = i;
88 }
89 } else if (!found) {
90 found = 1;
91 } else if (found_idx) {
92 invalid = 1;
93 break;
94 }
95 }
96
97 if (invalid) {
98 /* This means there was an error writing status on the last
99 * swap. Tell user and move on to validation!
100 */
101 #if !defined(__BOOTSIM__)
102 BOOT_LOG_ERR("Detected inconsistent status!");
103 #endif
104
105 #if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
106 /* With validation of the primary slot disabled, there is no way
107 * to be sure the swapped primary slot is OK, so abort!
108 */
109 assert(0);
110 #endif
111 }
112
113 if (found) {
114 if (!found_idx) {
115 found_idx = i;
116 }
117 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
118 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
119 }
120
121 return 0;
122 }
123
124 uint32_t
boot_status_internal_off(const struct boot_status * bs,int elem_sz)125 boot_status_internal_off(const struct boot_status *bs, int elem_sz)
126 {
127 int idx_sz;
128
129 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
130
131 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
132 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
133 }
134
135 /*
136 * Slots are compatible when all sectors that store up to to size of the image
137 * round up to sector size, in both slot's are able to fit in the scratch
138 * area, and have sizes that are a multiple of each other (powers of two
139 * presumably!).
140 */
141 int
boot_slots_compatible(struct boot_loader_state * state)142 boot_slots_compatible(struct boot_loader_state *state)
143 {
144 size_t num_sectors_primary;
145 size_t num_sectors_secondary;
146 size_t sz0, sz1;
147 size_t primary_slot_sz, secondary_slot_sz;
148 #ifndef MCUBOOT_OVERWRITE_ONLY
149 size_t scratch_sz;
150 #endif
151 size_t i, j;
152 int8_t smaller;
153
154 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
155 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
156 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
157 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
158 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
159 return 0;
160 }
161
162 #ifndef MCUBOOT_OVERWRITE_ONLY
163 scratch_sz = boot_scratch_area_size(state);
164 #endif
165
166 /*
167 * The following loop scans all sectors in a linear fashion, assuring that
168 * for each possible sector in each slot, it is able to fit in the other
169 * slot's sector or sectors. Slot's should be compatible as long as any
170 * number of a slot's sectors are able to fit into another, which only
171 * excludes cases where sector sizes are not a multiple of each other.
172 */
173 i = sz0 = primary_slot_sz = 0;
174 j = sz1 = secondary_slot_sz = 0;
175 smaller = 0;
176 while (i < num_sectors_primary || j < num_sectors_secondary) {
177 if (sz0 == sz1) {
178 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
179 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
180 i++;
181 j++;
182 } else if (sz0 < sz1) {
183 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
184 /* Guarantee that multiple sectors of the secondary slot
185 * fit into the primary slot.
186 */
187 if (smaller == 2) {
188 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
189 return 0;
190 }
191 smaller = 1;
192 i++;
193 } else {
194 size_t sector_size = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
195
196 #ifdef MCUBOOT_DECOMPRESS_IMAGES
197 if (sector_size == 0) {
198 /* Since this supports decompressed images, we can safely exit if slot1 is
199 * smaller than slot0.
200 */
201 break;
202 }
203 #endif
204 sz1 += sector_size;
205 /* Guarantee that multiple sectors of the primary slot
206 * fit into the secondary slot.
207 */
208 if (smaller == 1) {
209 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
210 return 0;
211 }
212 smaller = 2;
213 j++;
214 }
215 #ifndef MCUBOOT_OVERWRITE_ONLY
216 if (sz0 == sz1) {
217 primary_slot_sz += sz0;
218 secondary_slot_sz += sz1;
219 /* Scratch has to fit each swap operation to the size of the larger
220 * sector among the primary slot and the secondary slot.
221 */
222 if (sz0 > scratch_sz || sz1 > scratch_sz) {
223 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
224 return 0;
225 }
226 smaller = sz0 = sz1 = 0;
227 }
228 #endif
229 }
230
231 #ifndef MCUBOOT_DECOMPRESS_IMAGES
232 if ((i != num_sectors_primary) ||
233 (j != num_sectors_secondary) ||
234 (primary_slot_sz != secondary_slot_sz)) {
235 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
236 return 0;
237 }
238 #endif
239
240 return 1;
241 }
242
243 #define BOOT_LOG_SWAP_STATE(area, state) \
244 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
245 "image_ok=0x%x", \
246 (area), \
247 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
248 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
249 "bad"), \
250 (state)->swap_type, \
251 (state)->copy_done, \
252 (state)->image_ok)
253
254 struct boot_status_table {
255 uint8_t bst_magic_primary_slot;
256 uint8_t bst_magic_scratch;
257 uint8_t bst_copy_done_primary_slot;
258 uint8_t bst_status_source;
259 };
260
261 /**
262 * This set of tables maps swap state contents to boot status location.
263 * When searching for a match, these tables must be iterated in order.
264 */
265 static const struct boot_status_table boot_status_tables[] = {
266 {
267 /* | primary slot | scratch |
268 * ----------+--------------+--------------|
269 * magic | Good | Any |
270 * copy-done | Set | N/A |
271 * ----------+--------------+--------------'
272 * source: none |
273 * ----------------------------------------'
274 */
275 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
276 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
277 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
278 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
279 },
280
281 {
282 /* | primary slot | scratch |
283 * ----------+--------------+--------------|
284 * magic | Good | Any |
285 * copy-done | Unset | N/A |
286 * ----------+--------------+--------------'
287 * source: primary slot |
288 * ----------------------------------------'
289 */
290 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
291 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
292 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
293 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
294 },
295
296 {
297 /* | primary slot | scratch |
298 * ----------+--------------+--------------|
299 * magic | Any | Good |
300 * copy-done | Any | N/A |
301 * ----------+--------------+--------------'
302 * source: scratch |
303 * ----------------------------------------'
304 */
305 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
306 .bst_magic_scratch = BOOT_MAGIC_GOOD,
307 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
308 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
309 },
310 {
311 /* | primary slot | scratch |
312 * ----------+--------------+--------------|
313 * magic | Unset | Any |
314 * copy-done | Unset | N/A |
315 * ----------+--------------+--------------|
316 * source: varies |
317 * ----------------------------------------+--------------------------+
318 * This represents one of two cases: |
319 * o No swaps ever (no status to read, so no harm in checking). |
320 * o Mid-revert; status in primary slot. |
321 * -------------------------------------------------------------------'
322 */
323 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
324 .bst_magic_scratch = BOOT_MAGIC_ANY,
325 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
326 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
327 },
328 };
329
330 #define BOOT_STATUS_TABLES_COUNT \
331 (sizeof boot_status_tables / sizeof boot_status_tables[0])
332
333 /**
334 * Determines where in flash the most recent boot status is stored. The boot
335 * status is necessary for completing a swap that was interrupted by a boot
336 * loader reset.
337 *
338 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
339 * be read from.
340 */
341 int
swap_status_source(struct boot_loader_state * state)342 swap_status_source(struct boot_loader_state *state)
343 {
344 const struct boot_status_table *table;
345 #if MCUBOOT_SWAP_USING_SCRATCH
346 struct boot_swap_state state_scratch;
347 #endif
348 struct boot_swap_state state_primary_slot;
349 int rc;
350 size_t i;
351 uint8_t source;
352 uint8_t image_index;
353
354 #if (BOOT_IMAGE_NUMBER == 1)
355 (void)state;
356 #endif
357
358 image_index = BOOT_CURR_IMG(state);
359 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
360 &state_primary_slot);
361 assert(rc == 0);
362
363 #if MCUBOOT_SWAP_USING_SCRATCH
364 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
365 assert(rc == 0);
366 #endif
367
368 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
369 #if MCUBOOT_SWAP_USING_SCRATCH
370 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
371 #endif
372 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
373 table = &boot_status_tables[i];
374
375 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
376 state_primary_slot.magic) &&
377 #if MCUBOOT_SWAP_USING_SCRATCH
378 boot_magic_compatible_check(table->bst_magic_scratch,
379 state_scratch.magic) &&
380 #endif
381 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
382 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
383 {
384 source = table->bst_status_source;
385
386 #if (BOOT_IMAGE_NUMBER > 1) && MCUBOOT_SWAP_USING_SCRATCH
387 /* In case of multi-image boot it can happen that if boot status
388 * info is found on scratch area then it does not belong to the
389 * currently examined image.
390 */
391 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
392 state_scratch.image_num != BOOT_CURR_IMG(state)) {
393 source = BOOT_STATUS_SOURCE_NONE;
394 }
395 #endif
396
397 BOOT_LOG_INF("Boot source: %s",
398 source == BOOT_STATUS_SOURCE_NONE ? "none" :
399 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
400 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
401 "primary slot" : "BUG; can't happen");
402 return source;
403 }
404 }
405
406 BOOT_LOG_INF("Boot source: none");
407 return BOOT_STATUS_SOURCE_NONE;
408 }
409
410 #ifndef MCUBOOT_OVERWRITE_ONLY
411 /**
412 * Calculates the number of sectors the scratch area can contain. A "last"
413 * source sector is specified because images are copied backwards in flash
414 * (final index to index number 0).
415 *
416 * @param last_sector_idx The index of the last source sector
417 * (inclusive).
418 * @param out_first_sector_idx The index of the first source sector
419 * (inclusive) gets written here.
420 *
421 * @return The number of bytes comprised by the
422 * [first-sector, last-sector] range.
423 */
424 static uint32_t
boot_copy_sz(const struct boot_loader_state * state,int last_sector_idx,int * out_first_sector_idx)425 boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
426 int *out_first_sector_idx)
427 {
428 size_t scratch_sz;
429 uint32_t new_sz;
430 uint32_t sz;
431 int i;
432
433 sz = 0;
434
435 scratch_sz = boot_scratch_area_size(state);
436 for (i = last_sector_idx; i >= 0; i--) {
437 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
438 /*
439 * The secondary slot is not being checked here, because
440 * `boot_slots_compatible` already provides assurance that the copy size
441 * will be compatible with the primary slot and scratch.
442 */
443 if (new_sz > scratch_sz) {
444 break;
445 }
446 sz = new_sz;
447 }
448
449 /* i currently refers to a sector that doesn't fit or it is -1 because all
450 * sectors have been processed. In both cases, exclude sector i.
451 */
452 *out_first_sector_idx = i + 1;
453 return sz;
454 }
455
456 /**
457 * Finds the index of the last sector in the primary slot that needs swapping.
458 *
459 * @param state Current bootloader's state.
460 * @param copy_size Total number of bytes to swap.
461 *
462 * @return Index of the last sector in the primary slot that needs swapping.
463 */
464 static int
find_last_sector_idx(const struct boot_loader_state * state,uint32_t copy_size)465 find_last_sector_idx(const struct boot_loader_state *state, uint32_t copy_size)
466 {
467 int last_sector_idx;
468 uint32_t primary_slot_size;
469 uint32_t secondary_slot_size;
470
471 primary_slot_size = 0;
472 secondary_slot_size = 0;
473 last_sector_idx = 0;
474
475 /*
476 * Knowing the size of the largest image between both slots, here we
477 * find what is the last sector in the primary slot that needs swapping.
478 * Since we already know that both slots are compatible, the secondary
479 * slot's last sector is not really required after this check is finished.
480 */
481 while (1) {
482 if ((primary_slot_size < copy_size) ||
483 (primary_slot_size < secondary_slot_size)) {
484 primary_slot_size += boot_img_sector_size(state,
485 BOOT_PRIMARY_SLOT,
486 last_sector_idx);
487 }
488 if ((secondary_slot_size < copy_size) ||
489 (secondary_slot_size < primary_slot_size)) {
490 secondary_slot_size += boot_img_sector_size(state,
491 BOOT_SECONDARY_SLOT,
492 last_sector_idx);
493 }
494 if (primary_slot_size >= copy_size &&
495 secondary_slot_size >= copy_size &&
496 primary_slot_size == secondary_slot_size) {
497 break;
498 }
499 last_sector_idx++;
500 }
501
502 return last_sector_idx;
503 }
504
505 /**
506 * Finds the number of swap operations that have to be performed to swap the two images.
507 *
508 * @param state Current bootloader's state.
509 * @param copy_size Total number of bytes to swap.
510 *
511 * @return The number of swap operations that have to be performed.
512 */
513 static uint32_t
find_swap_count(const struct boot_loader_state * state,uint32_t copy_size)514 find_swap_count(const struct boot_loader_state *state, uint32_t copy_size)
515 {
516 int first_sector_idx;
517 int last_sector_idx;
518 uint32_t swap_count;
519
520 last_sector_idx = find_last_sector_idx(state, copy_size);
521
522 swap_count = 0;
523
524 while (last_sector_idx >= 0) {
525 boot_copy_sz(state, last_sector_idx, &first_sector_idx);
526
527 last_sector_idx = first_sector_idx - 1;
528 swap_count++;
529 }
530
531 return swap_count;
532 }
533
534 /**
535 * Swaps the contents of two flash regions within the two image slots.
536 *
537 * @param idx The index of the first sector in the range of
538 * sectors being swapped.
539 * @param sz The number of bytes to swap.
540 * @param bs The current boot status. This struct gets
541 * updated according to the outcome.
542 *
543 * @return 0 on success; nonzero on failure.
544 */
545 static void
boot_swap_sectors(int idx,uint32_t sz,struct boot_loader_state * state,struct boot_status * bs)546 boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
547 struct boot_status *bs)
548 {
549 const struct flash_area *fap_primary_slot;
550 const struct flash_area *fap_secondary_slot;
551 const struct flash_area *fap_scratch;
552 uint32_t copy_sz;
553 uint32_t trailer_sz;
554 uint32_t sector_sz;
555 uint32_t img_off;
556 uint32_t scratch_trailer_off;
557 struct boot_swap_state swap_state;
558 size_t last_sector;
559 bool erase_scratch;
560 uint8_t image_index;
561 int rc;
562
563 /* Calculate offset from start of image area. */
564 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
565
566 copy_sz = sz;
567 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
568
569 /* sz in this function is always sized on a multiple of the sector size.
570 * The check against the start offset of the last sector
571 * is to determine if we're swapping the last sector. The last sector
572 * needs special handling because it's where the trailer lives. If we're
573 * copying it, we need to use scratch to write the trailer temporarily.
574 *
575 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
576 * controls if special handling is needed (swapping last sector).
577 */
578 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
579 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, last_sector);
580
581 if (sector_sz < trailer_sz) {
582 uint32_t trailer_sector_sz = sector_sz;
583
584 while (trailer_sector_sz < trailer_sz) {
585 /* Consider that the image trailer may span across sectors of
586 * different sizes.
587 */
588 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, --last_sector);
589
590 trailer_sector_sz += sector_sz;
591 }
592 }
593
594 if ((img_off + sz) >
595 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
596 copy_sz -= trailer_sz;
597 }
598
599 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
600
601 image_index = BOOT_CURR_IMG(state);
602
603 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
604 &fap_primary_slot);
605 assert (rc == 0);
606
607 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
608 &fap_secondary_slot);
609 assert (rc == 0);
610
611 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
612 assert (rc == 0);
613
614 if (bs->state == BOOT_STATUS_STATE_0) {
615 BOOT_LOG_DBG("erasing scratch area");
616 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
617 assert(rc == 0);
618
619 if (bs->idx == BOOT_STATUS_IDX_0) {
620 /* Write a trailer to the scratch area, even if we don't need the
621 * scratch area for status. We need a temporary place to store the
622 * `swap-type` while we erase the primary trailer.
623 */
624 rc = swap_status_init(state, fap_scratch, bs);
625 assert(rc == 0);
626
627 if (!bs->use_scratch) {
628 /* Prepare the primary status area... here it is known that the
629 * last sector is not being used by the image data so it's safe
630 * to erase.
631 */
632 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
633 assert(rc == 0);
634
635 rc = swap_status_init(state, fap_primary_slot, bs);
636 assert(rc == 0);
637
638 /* Erase the temporary trailer from the scratch area. */
639 rc = boot_erase_region(fap_scratch, 0,
640 flash_area_get_size(fap_scratch));
641 assert(rc == 0);
642 }
643 }
644
645 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
646 img_off, 0, copy_sz);
647 assert(rc == 0);
648
649 rc = boot_write_status(state, bs);
650 bs->state = BOOT_STATUS_STATE_1;
651 BOOT_STATUS_ASSERT(rc == 0);
652 }
653
654 if (bs->state == BOOT_STATUS_STATE_1) {
655 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
656 assert(rc == 0);
657
658 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
659 img_off, img_off, copy_sz);
660 assert(rc == 0);
661
662 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
663 /* If not all sectors of the slot are being swapped,
664 * guarantee here that only the primary slot will have the state.
665 */
666 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
667 assert(rc == 0);
668 }
669
670 rc = boot_write_status(state, bs);
671 bs->state = BOOT_STATUS_STATE_2;
672 BOOT_STATUS_ASSERT(rc == 0);
673 }
674
675 if (bs->state == BOOT_STATUS_STATE_2) {
676 rc = boot_erase_region(fap_primary_slot, img_off, sz);
677 assert(rc == 0);
678
679 /* NOTE: If this is the final sector, we exclude the image trailer from
680 * this copy (copy_sz was truncated earlier).
681 */
682 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
683 0, img_off, copy_sz);
684 assert(rc == 0);
685
686 if (bs->use_scratch) {
687 scratch_trailer_off = boot_status_off(fap_scratch);
688
689 /* copy current status that is being maintained in scratch */
690 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
691 scratch_trailer_off, img_off + copy_sz,
692 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
693 BOOT_STATUS_ASSERT(rc == 0);
694
695 rc = boot_read_swap_state(fap_scratch, &swap_state);
696 assert(rc == 0);
697
698 if (swap_state.image_ok == BOOT_FLAG_SET) {
699 rc = boot_write_image_ok(fap_primary_slot);
700 assert(rc == 0);
701 }
702
703 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
704 rc = boot_write_swap_info(fap_primary_slot,
705 swap_state.swap_type, image_index);
706 assert(rc == 0);
707 }
708
709 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
710 assert(rc == 0);
711
712 #ifdef MCUBOOT_ENC_IMAGES
713 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
714 assert(rc == 0);
715
716 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
717 assert(rc == 0);
718 #endif
719 rc = boot_write_magic(fap_primary_slot);
720 assert(rc == 0);
721 }
722
723 /* If we wrote a trailer to the scratch area, erase it after we persist
724 * a trailer to the primary slot. We do this to prevent mcuboot from
725 * reading a stale status from the scratch area in case of immediate
726 * reset.
727 */
728 erase_scratch = bs->use_scratch;
729 bs->use_scratch = 0;
730
731 rc = boot_write_status(state, bs);
732 bs->idx++;
733 bs->state = BOOT_STATUS_STATE_0;
734 BOOT_STATUS_ASSERT(rc == 0);
735
736 if (erase_scratch) {
737 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
738 assert(rc == 0);
739 }
740 }
741
742 flash_area_close(fap_primary_slot);
743 flash_area_close(fap_secondary_slot);
744 flash_area_close(fap_scratch);
745 }
746
747 void
swap_run(struct boot_loader_state * state,struct boot_status * bs,uint32_t copy_size)748 swap_run(struct boot_loader_state *state, struct boot_status *bs,
749 uint32_t copy_size)
750 {
751 uint32_t sz;
752 int first_sector_idx;
753 int last_sector_idx;
754 uint32_t swap_idx;
755
756 BOOT_LOG_INF("Starting swap using scratch algorithm.");
757
758 last_sector_idx = find_last_sector_idx(state, copy_size);
759
760 swap_idx = 0;
761 while (last_sector_idx >= 0) {
762 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
763 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
764 boot_swap_sectors(first_sector_idx, sz, state, bs);
765 }
766
767 last_sector_idx = first_sector_idx - 1;
768 swap_idx++;
769 }
770
771 }
772 #endif /* !MCUBOOT_OVERWRITE_ONLY */
773
app_max_size(struct boot_loader_state * state)774 int app_max_size(struct boot_loader_state *state)
775 {
776 size_t num_sectors_primary;
777 size_t num_sectors_secondary;
778 size_t sz0, sz1;
779 size_t primary_slot_sz, secondary_slot_sz;
780 #ifndef MCUBOOT_OVERWRITE_ONLY
781 size_t scratch_sz;
782 #endif
783 size_t i, j;
784 int8_t smaller;
785
786 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
787 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
788
789 #ifndef MCUBOOT_OVERWRITE_ONLY
790 scratch_sz = boot_scratch_area_size(state);
791 #endif
792
793 /*
794 * The following loop scans all sectors in a linear fashion, assuring that
795 * for each possible sector in each slot, it is able to fit in the other
796 * slot's sector or sectors. Slot's should be compatible as long as any
797 * number of a slot's sectors are able to fit into another, which only
798 * excludes cases where sector sizes are not a multiple of each other.
799 */
800 i = sz0 = primary_slot_sz = 0;
801 j = sz1 = secondary_slot_sz = 0;
802 smaller = 0;
803 while (i < num_sectors_primary || j < num_sectors_secondary) {
804 if (sz0 == sz1) {
805 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
806 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
807 i++;
808 j++;
809 } else if (sz0 < sz1) {
810 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
811 /* Guarantee that multiple sectors of the secondary slot
812 * fit into the primary slot.
813 */
814 if (smaller == 2) {
815 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
816 return 0;
817 }
818 smaller = 1;
819 i++;
820 } else {
821 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
822 /* Guarantee that multiple sectors of the primary slot
823 * fit into the secondary slot.
824 */
825 if (smaller == 1) {
826 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
827 return 0;
828 }
829 smaller = 2;
830 j++;
831 }
832 #ifndef MCUBOOT_OVERWRITE_ONLY
833 if (sz0 == sz1) {
834 primary_slot_sz += sz0;
835 secondary_slot_sz += sz1;
836 /* Scratch has to fit each swap operation to the size of the larger
837 * sector among the primary slot and the secondary slot.
838 */
839 if (sz0 > scratch_sz || sz1 > scratch_sz) {
840 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
841 return 0;
842 }
843 smaller = sz0 = sz1 = 0;
844 }
845 #endif
846 }
847
848 #ifdef MCUBOOT_OVERWRITE_ONLY
849 return (sz1 < sz0 ? sz1 : sz0);
850 #else
851 return (secondary_slot_sz < primary_slot_sz ? secondary_slot_sz : primary_slot_sz);
852 #endif
853 }
854 #else
app_max_size(struct boot_loader_state * state)855 int app_max_size(struct boot_loader_state *state)
856 {
857 const struct flash_area *fap;
858 int fa_id;
859 int rc;
860 uint32_t active_slot;
861 int primary_sz, secondary_sz;
862
863 active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
864
865 fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
866 rc = flash_area_open(fa_id, &fap);
867 assert(rc == 0);
868 primary_sz = flash_area_get_size(fap);
869 flash_area_close(fap);
870
871 if (active_slot == BOOT_PRIMARY_SLOT) {
872 active_slot = BOOT_SECONDARY_SLOT;
873 } else {
874 active_slot = BOOT_PRIMARY_SLOT;
875 }
876
877 fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
878 rc = flash_area_open(fa_id, &fap);
879 assert(rc == 0);
880 secondary_sz = flash_area_get_size(fap);
881 flash_area_close(fap);
882
883 return (secondary_sz < primary_sz ? secondary_sz : primary_sz);
884 }
885
886 #endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
887
888 int
boot_read_image_header(struct boot_loader_state * state,int slot,struct image_header * out_hdr,struct boot_status * bs)889 boot_read_image_header(struct boot_loader_state *state, int slot,
890 struct image_header *out_hdr, struct boot_status *bs)
891 {
892 const struct flash_area *fap;
893 #ifdef MCUBOOT_SWAP_USING_SCRATCH
894 uint32_t swap_count;
895 uint32_t swap_size;
896 #endif
897 int area_id;
898 int hdr_slot;
899 int rc = 0;
900
901 #ifndef MCUBOOT_SWAP_USING_SCRATCH
902 (void)bs;
903 #endif
904
905 #if (BOOT_IMAGE_NUMBER == 1)
906 (void)state;
907 #endif
908
909 hdr_slot = slot;
910
911 #ifdef MCUBOOT_SWAP_USING_SCRATCH
912 /* If the slots are being swapped, the headers might have been moved to scratch area or to the
913 * other slot depending on the progress of the swap process.
914 */
915 if (bs && !boot_status_is_reset(bs)) {
916 rc = boot_find_status(BOOT_CURR_IMG(state), &fap);
917
918 if (rc != 0) {
919 rc = BOOT_EFLASH;
920 goto done;
921 }
922
923 rc = boot_read_swap_size(fap, &swap_size);
924 flash_area_close(fap);
925
926 if (rc != 0) {
927 rc = BOOT_EFLASH;
928 goto done;
929 }
930
931 swap_count = find_swap_count(state, swap_size);
932
933 if (bs->idx - BOOT_STATUS_IDX_0 >= swap_count) {
934 /* If all segments have been swapped, the header is located in the other slot */
935 hdr_slot = (slot == BOOT_PRIMARY_SLOT) ? BOOT_SECONDARY_SLOT : BOOT_PRIMARY_SLOT;
936 } else if (bs->idx - BOOT_STATUS_IDX_0 == swap_count - 1) {
937 /* If the last swap operation is in progress, the headers are currently being swapped
938 * since the first segment of each slot is the last to be processed.
939 */
940
941 if (slot == BOOT_SECONDARY_SLOT && bs->state >= BOOT_STATUS_STATE_1) {
942 /* After BOOT_STATUS_STATE_1, the secondary image's header has been moved to the
943 * scratch area.
944 */
945 hdr_slot = BOOT_NUM_SLOTS;
946 } else if (slot == BOOT_PRIMARY_SLOT && bs->state >= BOOT_STATUS_STATE_2) {
947 /* After BOOT_STATUS_STATE_2, the primary image's header has been moved to the
948 * secondary slot.
949 */
950 hdr_slot = BOOT_SECONDARY_SLOT;
951 }
952 }
953 }
954
955 if (hdr_slot == BOOT_NUM_SLOTS) {
956 area_id = FLASH_AREA_IMAGE_SCRATCH;
957 } else {
958 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), hdr_slot);
959 }
960 #else
961 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), hdr_slot);
962 #endif
963
964 rc = flash_area_open(area_id, &fap);
965 if (rc == 0) {
966 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
967 flash_area_close(fap);
968 }
969
970 if (rc != 0) {
971 rc = BOOT_EFLASH;
972 goto done;
973 }
974
975 done:
976 return rc;
977 }
978
979 #endif /* !MCUBOOT_SWAP_USING_MOVE */
980