blob: ff93b414ee745e7c636b1c7470a94362d1a43cac [file] [log] [blame]
Fabio Utzig12d59162019-11-28 10:01:59 -03001/*
2 * Copyright (c) 2019 JUUL Labs
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <stddef.h>
19#include <stdbool.h>
20#include <inttypes.h>
21#include <stdlib.h>
22#include <string.h>
23#include "bootutil/bootutil.h"
24#include "bootutil_priv.h"
25#include "swap_priv.h"
26#include "bootutil/bootutil_log.h"
27
28#include "mcuboot_config/mcuboot_config.h"
29
30MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
31
Fabio Utzig74aef312019-11-28 11:05:34 -030032#if !defined(MCUBOOT_SWAP_USING_MOVE)
Fabio Utzig12d59162019-11-28 10:01:59 -030033
34#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
35/*
36 * FIXME: this might have to be updated for threaded sim
37 */
38int boot_status_fails = 0;
39#define BOOT_STATUS_ASSERT(x) \
40 do { \
41 if (!(x)) { \
42 boot_status_fails++; \
43 } \
44 } while (0)
45#else
46#define BOOT_STATUS_ASSERT(x) ASSERT(x)
47#endif
48
49int
50boot_read_image_header(struct boot_loader_state *state, int slot,
51 struct image_header *out_hdr, struct boot_status *bs)
52{
53 const struct flash_area *fap;
54 int area_id;
55 int rc;
56
57 (void)bs;
58
59#if (BOOT_IMAGE_NUMBER == 1)
60 (void)state;
61#endif
62
63 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
64 rc = flash_area_open(area_id, &fap);
65 if (rc != 0) {
66 rc = BOOT_EFLASH;
67 goto done;
68 }
69
70 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
71 if (rc != 0) {
72 rc = BOOT_EFLASH;
73 goto done;
74 }
75
76 rc = 0;
77
78done:
79 flash_area_close(fap);
80 return rc;
81}
82
83/**
84 * Reads the status of a partially-completed swap, if any. This is necessary
85 * to recover in case the boot lodaer was reset in the middle of a swap
86 * operation.
87 */
88int
89swap_read_status_bytes(const struct flash_area *fap,
90 struct boot_loader_state *state, struct boot_status *bs)
91{
92 uint32_t off;
93 uint8_t status;
94 int max_entries;
95 int found;
96 int found_idx;
97 int invalid;
98 int rc;
99 int i;
100
101 off = boot_status_off(fap);
102 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
103 if (max_entries < 0) {
104 return BOOT_EBADARGS;
105 }
106
107 found = 0;
108 found_idx = 0;
109 invalid = 0;
110 for (i = 0; i < max_entries; i++) {
111 rc = flash_area_read_is_empty(fap, off + i * BOOT_WRITE_SZ(state),
112 &status, 1);
113 if (rc < 0) {
114 return BOOT_EFLASH;
115 }
116
117 if (rc == 1) {
118 if (found && !found_idx) {
119 found_idx = i;
120 }
121 } else if (!found) {
122 found = 1;
123 } else if (found_idx) {
124 invalid = 1;
125 break;
126 }
127 }
128
129 if (invalid) {
130 /* This means there was an error writing status on the last
131 * swap. Tell user and move on to validation!
132 */
David Brown098de832019-12-10 11:58:01 -0700133#if !defined(__BOOTSIM__)
Fabio Utzig12d59162019-11-28 10:01:59 -0300134 BOOT_LOG_ERR("Detected inconsistent status!");
David Brown098de832019-12-10 11:58:01 -0700135#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300136
137#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
138 /* With validation of the primary slot disabled, there is no way
139 * to be sure the swapped primary slot is OK, so abort!
140 */
141 assert(0);
142#endif
143 }
144
145 if (found) {
146 if (!found_idx) {
147 found_idx = i;
148 }
149 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
150 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
151 }
152
153 return 0;
154}
155
156uint32_t
157boot_status_internal_off(const struct boot_status *bs, int elem_sz)
158{
159 int idx_sz;
160
161 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
162
163 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
164 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
165}
166
167/*
168 * Slots are compatible when all sectors that store up to to size of the image
169 * round up to sector size, in both slot's are able to fit in the scratch
170 * area, and have sizes that are a multiple of each other (powers of two
171 * presumably!).
172 */
173int
174boot_slots_compatible(struct boot_loader_state *state)
175{
176 size_t num_sectors_primary;
177 size_t num_sectors_secondary;
178 size_t sz0, sz1;
179 size_t primary_slot_sz, secondary_slot_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300180#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300181 size_t scratch_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300182#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300183 size_t i, j;
184 int8_t smaller;
185
186 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
187 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
188 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
189 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
190 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
191 return 0;
192 }
193
Fabio Utzig74aef312019-11-28 11:05:34 -0300194#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300195 scratch_sz = boot_scratch_area_size(state);
Fabio Utzig74aef312019-11-28 11:05:34 -0300196#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300197
198 /*
199 * The following loop scans all sectors in a linear fashion, assuring that
200 * for each possible sector in each slot, it is able to fit in the other
201 * slot's sector or sectors. Slot's should be compatible as long as any
202 * number of a slot's sectors are able to fit into another, which only
203 * excludes cases where sector sizes are not a multiple of each other.
204 */
205 i = sz0 = primary_slot_sz = 0;
206 j = sz1 = secondary_slot_sz = 0;
207 smaller = 0;
208 while (i < num_sectors_primary || j < num_sectors_secondary) {
209 if (sz0 == sz1) {
210 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
211 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
212 i++;
213 j++;
214 } else if (sz0 < sz1) {
215 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
216 /* Guarantee that multiple sectors of the secondary slot
217 * fit into the primary slot.
218 */
219 if (smaller == 2) {
220 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
221 return 0;
222 }
223 smaller = 1;
224 i++;
225 } else {
226 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
227 /* Guarantee that multiple sectors of the primary slot
228 * fit into the secondary slot.
229 */
230 if (smaller == 1) {
231 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
232 return 0;
233 }
234 smaller = 2;
235 j++;
236 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300237#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300238 if (sz0 == sz1) {
239 primary_slot_sz += sz0;
240 secondary_slot_sz += sz1;
241 /* Scratch has to fit each swap operation to the size of the larger
242 * sector among the primary slot and the secondary slot.
243 */
244 if (sz0 > scratch_sz || sz1 > scratch_sz) {
245 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
246 return 0;
247 }
248 smaller = sz0 = sz1 = 0;
249 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300250#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300251 }
252
253 if ((i != num_sectors_primary) ||
254 (j != num_sectors_secondary) ||
255 (primary_slot_sz != secondary_slot_sz)) {
256 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
257 return 0;
258 }
259
260 return 1;
261}
262
263#define BOOT_LOG_SWAP_STATE(area, state) \
264 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
265 "image_ok=0x%x", \
266 (area), \
267 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
268 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
269 "bad"), \
270 (state)->swap_type, \
271 (state)->copy_done, \
272 (state)->image_ok)
273
274struct boot_status_table {
275 uint8_t bst_magic_primary_slot;
276 uint8_t bst_magic_scratch;
277 uint8_t bst_copy_done_primary_slot;
278 uint8_t bst_status_source;
279};
280
281/**
282 * This set of tables maps swap state contents to boot status location.
283 * When searching for a match, these tables must be iterated in order.
284 */
285static const struct boot_status_table boot_status_tables[] = {
286 {
287 /* | primary slot | scratch |
288 * ----------+--------------+--------------|
289 * magic | Good | Any |
290 * copy-done | Set | N/A |
291 * ----------+--------------+--------------'
292 * source: none |
293 * ----------------------------------------'
294 */
295 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
296 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
297 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
298 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
299 },
300
301 {
302 /* | primary slot | scratch |
303 * ----------+--------------+--------------|
304 * magic | Good | Any |
305 * copy-done | Unset | N/A |
306 * ----------+--------------+--------------'
307 * source: primary slot |
308 * ----------------------------------------'
309 */
310 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
311 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
312 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
313 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
314 },
315
316 {
317 /* | primary slot | scratch |
318 * ----------+--------------+--------------|
319 * magic | Any | Good |
320 * copy-done | Any | N/A |
321 * ----------+--------------+--------------'
322 * source: scratch |
323 * ----------------------------------------'
324 */
325 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
326 .bst_magic_scratch = BOOT_MAGIC_GOOD,
327 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
328 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
329 },
330 {
331 /* | primary slot | scratch |
332 * ----------+--------------+--------------|
333 * magic | Unset | Any |
334 * copy-done | Unset | N/A |
335 * ----------+--------------+--------------|
336 * source: varies |
337 * ----------------------------------------+--------------------------+
338 * This represents one of two cases: |
339 * o No swaps ever (no status to read, so no harm in checking). |
340 * o Mid-revert; status in primary slot. |
341 * -------------------------------------------------------------------'
342 */
343 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
344 .bst_magic_scratch = BOOT_MAGIC_ANY,
345 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
346 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
347 },
348};
349
350#define BOOT_STATUS_TABLES_COUNT \
351 (sizeof boot_status_tables / sizeof boot_status_tables[0])
352
353/**
354 * Determines where in flash the most recent boot status is stored. The boot
355 * status is necessary for completing a swap that was interrupted by a boot
356 * loader reset.
357 *
358 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
359 * be read from.
360 */
361int
362swap_status_source(struct boot_loader_state *state)
363{
364 const struct boot_status_table *table;
365 struct boot_swap_state state_scratch;
366 struct boot_swap_state state_primary_slot;
367 int rc;
368 size_t i;
369 uint8_t source;
370 uint8_t image_index;
371
372#if (BOOT_IMAGE_NUMBER == 1)
373 (void)state;
374#endif
375
376 image_index = BOOT_CURR_IMG(state);
377 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
378 &state_primary_slot);
379 assert(rc == 0);
380
381 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
382 assert(rc == 0);
383
384 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
385 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
386
387 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
388 table = &boot_status_tables[i];
389
390 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
391 state_primary_slot.magic) &&
392 boot_magic_compatible_check(table->bst_magic_scratch,
393 state_scratch.magic) &&
394 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
395 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
396 {
397 source = table->bst_status_source;
398
399#if (BOOT_IMAGE_NUMBER > 1)
400 /* In case of multi-image boot it can happen that if boot status
401 * info is found on scratch area then it does not belong to the
402 * currently examined image.
403 */
404 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
405 state_scratch.image_num != BOOT_CURR_IMG(state)) {
406 source = BOOT_STATUS_SOURCE_NONE;
407 }
408#endif
409
410 BOOT_LOG_INF("Boot source: %s",
411 source == BOOT_STATUS_SOURCE_NONE ? "none" :
412 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
413 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
414 "primary slot" : "BUG; can't happen");
415 return source;
416 }
417 }
418
419 BOOT_LOG_INF("Boot source: none");
420 return BOOT_STATUS_SOURCE_NONE;
421}
422
Fabio Utzig74aef312019-11-28 11:05:34 -0300423#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300424/**
425 * Calculates the number of sectors the scratch area can contain. A "last"
426 * source sector is specified because images are copied backwards in flash
427 * (final index to index number 0).
428 *
429 * @param last_sector_idx The index of the last source sector
430 * (inclusive).
431 * @param out_first_sector_idx The index of the first source sector
432 * (inclusive) gets written here.
433 *
434 * @return The number of bytes comprised by the
435 * [first-sector, last-sector] range.
436 */
437static uint32_t
438boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
439 int *out_first_sector_idx)
440{
441 size_t scratch_sz;
442 uint32_t new_sz;
443 uint32_t sz;
444 int i;
445
446 sz = 0;
447
448 scratch_sz = boot_scratch_area_size(state);
449 for (i = last_sector_idx; i >= 0; i--) {
450 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
451 /*
452 * The secondary slot is not being checked here, because
453 * `boot_slots_compatible` already provides assurance that the copy size
454 * will be compatible with the primary slot and scratch.
455 */
456 if (new_sz > scratch_sz) {
457 break;
458 }
459 sz = new_sz;
460 }
461
462 /* i currently refers to a sector that doesn't fit or it is -1 because all
463 * sectors have been processed. In both cases, exclude sector i.
464 */
465 *out_first_sector_idx = i + 1;
466 return sz;
467}
468
469/**
470 * Swaps the contents of two flash regions within the two image slots.
471 *
472 * @param idx The index of the first sector in the range of
473 * sectors being swapped.
474 * @param sz The number of bytes to swap.
475 * @param bs The current boot status. This struct gets
476 * updated according to the outcome.
477 *
478 * @return 0 on success; nonzero on failure.
479 */
480static void
481boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
482 struct boot_status *bs)
483{
484 const struct flash_area *fap_primary_slot;
485 const struct flash_area *fap_secondary_slot;
486 const struct flash_area *fap_scratch;
487 uint32_t copy_sz;
488 uint32_t trailer_sz;
489 uint32_t img_off;
490 uint32_t scratch_trailer_off;
491 struct boot_swap_state swap_state;
492 size_t last_sector;
493 bool erase_scratch;
494 uint8_t image_index;
495 int rc;
496
497 /* Calculate offset from start of image area. */
498 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
499
500 copy_sz = sz;
501 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
502
503 /* sz in this function is always sized on a multiple of the sector size.
504 * The check against the start offset of the last sector
505 * is to determine if we're swapping the last sector. The last sector
506 * needs special handling because it's where the trailer lives. If we're
507 * copying it, we need to use scratch to write the trailer temporarily.
508 *
509 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
510 * controls if special handling is needed (swapping last sector).
511 */
512 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
513 if ((img_off + sz) >
514 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
515 copy_sz -= trailer_sz;
516 }
517
518 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
519
520 image_index = BOOT_CURR_IMG(state);
521
522 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
523 &fap_primary_slot);
524 assert (rc == 0);
525
526 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
527 &fap_secondary_slot);
528 assert (rc == 0);
529
530 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
531 assert (rc == 0);
532
533 if (bs->state == BOOT_STATUS_STATE_0) {
534 BOOT_LOG_DBG("erasing scratch area");
535 rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
536 assert(rc == 0);
537
538 if (bs->idx == BOOT_STATUS_IDX_0) {
539 /* Write a trailer to the scratch area, even if we don't need the
540 * scratch area for status. We need a temporary place to store the
541 * `swap-type` while we erase the primary trailer.
542 */
543 rc = swap_status_init(state, fap_scratch, bs);
544 assert(rc == 0);
545
546 if (!bs->use_scratch) {
547 /* Prepare the primary status area... here it is known that the
548 * last sector is not being used by the image data so it's safe
549 * to erase.
550 */
551 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
552 assert(rc == 0);
553
554 rc = swap_status_init(state, fap_primary_slot, bs);
555 assert(rc == 0);
556
557 /* Erase the temporary trailer from the scratch area. */
558 rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
559 assert(rc == 0);
560 }
561 }
562
563 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
564 img_off, 0, copy_sz);
565 assert(rc == 0);
566
567 rc = boot_write_status(state, bs);
568 bs->state = BOOT_STATUS_STATE_1;
569 BOOT_STATUS_ASSERT(rc == 0);
570 }
571
572 if (bs->state == BOOT_STATUS_STATE_1) {
573 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
574 assert(rc == 0);
575
576 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
577 img_off, img_off, copy_sz);
578 assert(rc == 0);
579
580 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
581 /* If not all sectors of the slot are being swapped,
582 * guarantee here that only the primary slot will have the state.
583 */
584 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
585 assert(rc == 0);
586 }
587
588 rc = boot_write_status(state, bs);
589 bs->state = BOOT_STATUS_STATE_2;
590 BOOT_STATUS_ASSERT(rc == 0);
591 }
592
593 if (bs->state == BOOT_STATUS_STATE_2) {
594 rc = boot_erase_region(fap_primary_slot, img_off, sz);
595 assert(rc == 0);
596
597 /* NOTE: If this is the final sector, we exclude the image trailer from
598 * this copy (copy_sz was truncated earlier).
599 */
600 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
601 0, img_off, copy_sz);
602 assert(rc == 0);
603
604 if (bs->use_scratch) {
605 scratch_trailer_off = boot_status_off(fap_scratch);
606
607 /* copy current status that is being maintained in scratch */
608 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
609 scratch_trailer_off, img_off + copy_sz,
610 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
611 BOOT_STATUS_ASSERT(rc == 0);
612
613 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH,
614 &swap_state);
615 assert(rc == 0);
616
617 if (swap_state.image_ok == BOOT_FLAG_SET) {
618 rc = boot_write_image_ok(fap_primary_slot);
619 assert(rc == 0);
620 }
621
622 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
623 rc = boot_write_swap_info(fap_primary_slot,
624 swap_state.swap_type, image_index);
625 assert(rc == 0);
626 }
627
628 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
629 assert(rc == 0);
630
631#ifdef MCUBOOT_ENC_IMAGES
Fabio Utzig4741c452019-12-19 15:32:41 -0300632 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300633 assert(rc == 0);
634
Fabio Utzig4741c452019-12-19 15:32:41 -0300635 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300636 assert(rc == 0);
637#endif
638 rc = boot_write_magic(fap_primary_slot);
639 assert(rc == 0);
640 }
641
642 /* If we wrote a trailer to the scratch area, erase it after we persist
643 * a trailer to the primary slot. We do this to prevent mcuboot from
644 * reading a stale status from the scratch area in case of immediate
645 * reset.
646 */
647 erase_scratch = bs->use_scratch;
648 bs->use_scratch = 0;
649
650 rc = boot_write_status(state, bs);
651 bs->idx++;
652 bs->state = BOOT_STATUS_STATE_0;
653 BOOT_STATUS_ASSERT(rc == 0);
654
655 if (erase_scratch) {
656 rc = boot_erase_region(fap_scratch, 0, sz);
657 assert(rc == 0);
658 }
659 }
660
661 flash_area_close(fap_primary_slot);
662 flash_area_close(fap_secondary_slot);
663 flash_area_close(fap_scratch);
664}
665
666void
667swap_run(struct boot_loader_state *state, struct boot_status *bs,
668 uint32_t copy_size)
669{
670 uint32_t sz;
671 int first_sector_idx;
672 int last_sector_idx;
673 uint32_t swap_idx;
674 int last_idx_secondary_slot;
675 uint32_t primary_slot_size;
676 uint32_t secondary_slot_size;
677 primary_slot_size = 0;
678 secondary_slot_size = 0;
679 last_sector_idx = 0;
680 last_idx_secondary_slot = 0;
681
682 /*
683 * Knowing the size of the largest image between both slots, here we
684 * find what is the last sector in the primary slot that needs swapping.
685 * Since we already know that both slots are compatible, the secondary
686 * slot's last sector is not really required after this check is finished.
687 */
688 while (1) {
689 if ((primary_slot_size < copy_size) ||
690 (primary_slot_size < secondary_slot_size)) {
691 primary_slot_size += boot_img_sector_size(state,
692 BOOT_PRIMARY_SLOT,
693 last_sector_idx);
694 }
695 if ((secondary_slot_size < copy_size) ||
696 (secondary_slot_size < primary_slot_size)) {
697 secondary_slot_size += boot_img_sector_size(state,
698 BOOT_SECONDARY_SLOT,
699 last_idx_secondary_slot);
700 }
701 if (primary_slot_size >= copy_size &&
702 secondary_slot_size >= copy_size &&
703 primary_slot_size == secondary_slot_size) {
704 break;
705 }
706 last_sector_idx++;
707 last_idx_secondary_slot++;
708 }
709
710 swap_idx = 0;
711 while (last_sector_idx >= 0) {
712 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
713 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
714 boot_swap_sectors(first_sector_idx, sz, state, bs);
715 }
716
717 last_sector_idx = first_sector_idx - 1;
718 swap_idx++;
719 }
720
721}
Fabio Utzig74aef312019-11-28 11:05:34 -0300722#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300723
724#endif