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