blob: 9b9228a898bc5afd5bbf7f648ce457cf3fee5d33 [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"
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020027#ifdef MCUBOOT_SWAP_USING_STATUS
28#include "swap_status.h"
29#endif
Fabio Utzig12d59162019-11-28 10:01:59 -030030#include "bootutil/bootutil_log.h"
31
32#include "mcuboot_config/mcuboot_config.h"
33
34MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
35
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020036#ifndef MCUBOOT_SWAP_USING_MOVE
Fabio Utzig12d59162019-11-28 10:01:59 -030037
38#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
39/*
40 * FIXME: this might have to be updated for threaded sim
41 */
42int boot_status_fails = 0;
43#define BOOT_STATUS_ASSERT(x) \
44 do { \
45 if (!(x)) { \
46 boot_status_fails++; \
47 } \
48 } while (0)
49#else
50#define BOOT_STATUS_ASSERT(x) ASSERT(x)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020051#endif /* defined(MCUBOOT_VALIDATE_PRIMARY_SLOT) */
Fabio Utzig12d59162019-11-28 10:01:59 -030052
53int
54boot_read_image_header(struct boot_loader_state *state, int slot,
55 struct image_header *out_hdr, struct boot_status *bs)
56{
57 const struct flash_area *fap;
58 int area_id;
59 int rc;
60
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020061 int saved_slot = slot;
62
Fabio Utzig12d59162019-11-28 10:01:59 -030063 (void)bs;
64
65#if (BOOT_IMAGE_NUMBER == 1)
66 (void)state;
67#endif
68
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020069 if (bs != NULL) {
70 if (bs->state == BOOT_STATUS_STATE_1) {
71 if (slot == 1) {
72 slot = 2;
73 }
74 }
75 else if (bs->state == BOOT_STATUS_STATE_2) {
76 if (slot == 0) {
77 slot = 1;
78 }
79 else {
80 slot = 2;
81 }
82 }
83 }
84
Fabio Utzig12d59162019-11-28 10:01:59 -030085 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
86 rc = flash_area_open(area_id, &fap);
87 if (rc != 0) {
88 rc = BOOT_EFLASH;
89 goto done;
90 }
91
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020092 rc = flash_area_read_is_empty(fap, 0, out_hdr, sizeof *out_hdr);
93 if (rc < 0) {
Fabio Utzig12d59162019-11-28 10:01:59 -030094 rc = BOOT_EFLASH;
95 goto done;
96 }
97
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020098 if (rc == 1) {
99 memset(out_hdr, 0, sizeof(*out_hdr));
100 }
101
102 /* We only know where the headers are located when bs is valid */
103 if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) {
104
105 if (bs->state != BOOT_STATUS_STATE_0) {
106
107 flash_area_close(fap);
108
109 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), saved_slot);
110 rc = flash_area_open(area_id, &fap);
111 if (rc != 0) {
112 rc = BOOT_EFLASH;
113 goto done;
114 }
115
116 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
117 if (rc < 0) {
118 rc = BOOT_EFLASH;
119 goto done;
120 }
121
122 if (out_hdr->ih_magic != IMAGE_MAGIC) {
123 rc = -1;
124 goto done;
125 }
126 }
127 }
128
Fabio Utzig12d59162019-11-28 10:01:59 -0300129 rc = 0;
130
131done:
132 flash_area_close(fap);
133 return rc;
134}
135
Tamas Banfe031092020-09-10 17:32:39 +0200136#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200137
138#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300139/**
140 * Reads the status of a partially-completed swap, if any. This is necessary
141 * to recover in case the boot lodaer was reset in the middle of a swap
142 * operation.
143 */
144int
145swap_read_status_bytes(const struct flash_area *fap,
146 struct boot_loader_state *state, struct boot_status *bs)
147{
148 uint32_t off;
149 uint8_t status;
150 int max_entries;
151 int found;
152 int found_idx;
153 int invalid;
154 int rc;
155 int i;
156
157 off = boot_status_off(fap);
158 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
159 if (max_entries < 0) {
160 return BOOT_EBADARGS;
161 }
162
163 found = 0;
164 found_idx = 0;
165 invalid = 0;
166 for (i = 0; i < max_entries; i++) {
Fabio Utzig4b2e55f2020-09-24 11:49:20 -0300167 rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
Fabio Utzig12d59162019-11-28 10:01:59 -0300168 &status, 1);
169 if (rc < 0) {
170 return BOOT_EFLASH;
171 }
172
Fabio Utzig4b2e55f2020-09-24 11:49:20 -0300173 if (bootutil_buffer_is_erased(fap, &status, 1)) {
Fabio Utzig12d59162019-11-28 10:01:59 -0300174 if (found && !found_idx) {
175 found_idx = i;
176 }
177 } else if (!found) {
178 found = 1;
179 } else if (found_idx) {
180 invalid = 1;
181 break;
182 }
183 }
184
185 if (invalid) {
186 /* This means there was an error writing status on the last
187 * swap. Tell user and move on to validation!
188 */
David Brown098de832019-12-10 11:58:01 -0700189#if !defined(__BOOTSIM__)
Fabio Utzig12d59162019-11-28 10:01:59 -0300190 BOOT_LOG_ERR("Detected inconsistent status!");
David Brown098de832019-12-10 11:58:01 -0700191#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300192
193#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
194 /* With validation of the primary slot disabled, there is no way
195 * to be sure the swapped primary slot is OK, so abort!
196 */
197 assert(0);
198#endif
199 }
200
201 if (found) {
202 if (!found_idx) {
203 found_idx = i;
204 }
205 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
206 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
207 }
208
209 return 0;
210}
211
212uint32_t
213boot_status_internal_off(const struct boot_status *bs, int elem_sz)
214{
215 int idx_sz;
216
217 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
218
219 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
220 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
221}
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200222#endif /* !MCUBOOT_SWAP_USING_STATUS */
Fabio Utzig12d59162019-11-28 10:01:59 -0300223
224/*
225 * Slots are compatible when all sectors that store up to to size of the image
226 * round up to sector size, in both slot's are able to fit in the scratch
227 * area, and have sizes that are a multiple of each other (powers of two
228 * presumably!).
229 */
230int
231boot_slots_compatible(struct boot_loader_state *state)
232{
233 size_t num_sectors_primary;
234 size_t num_sectors_secondary;
235 size_t sz0, sz1;
236 size_t primary_slot_sz, secondary_slot_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300237#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300238 size_t scratch_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300239#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300240 size_t i, j;
241 int8_t smaller;
242
243 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
244 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200245
246 if (num_sectors_secondary == 0) {
247 BOOT_LOG_WRN("Upgrade disabled for image %d", BOOT_CURR_IMG(state));
248 return 0;
249 }
250
Fabio Utzig12d59162019-11-28 10:01:59 -0300251 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
252 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
253 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200254 BOOT_LOG_DBG("sectors_primary (%d) or sectors_secondary (%d) > BOOT_MAX_IMG_SECTORS (%d)",
255 num_sectors_primary, num_sectors_secondary, BOOT_MAX_IMG_SECTORS);
Fabio Utzig12d59162019-11-28 10:01:59 -0300256 return 0;
257 }
258
Fabio Utzig74aef312019-11-28 11:05:34 -0300259#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300260 scratch_sz = boot_scratch_area_size(state);
Fabio Utzig74aef312019-11-28 11:05:34 -0300261#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300262
263 /*
264 * The following loop scans all sectors in a linear fashion, assuring that
265 * for each possible sector in each slot, it is able to fit in the other
266 * slot's sector or sectors. Slot's should be compatible as long as any
267 * number of a slot's sectors are able to fit into another, which only
268 * excludes cases where sector sizes are not a multiple of each other.
269 */
270 i = sz0 = primary_slot_sz = 0;
271 j = sz1 = secondary_slot_sz = 0;
272 smaller = 0;
273 while (i < num_sectors_primary || j < num_sectors_secondary) {
274 if (sz0 == sz1) {
275 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
276 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
277 i++;
278 j++;
279 } else if (sz0 < sz1) {
280 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
281 /* Guarantee that multiple sectors of the secondary slot
282 * fit into the primary slot.
283 */
284 if (smaller == 2) {
285 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
286 return 0;
287 }
288 smaller = 1;
289 i++;
290 } else {
291 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
292 /* Guarantee that multiple sectors of the primary slot
293 * fit into the secondary slot.
294 */
295 if (smaller == 1) {
296 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
297 return 0;
298 }
299 smaller = 2;
300 j++;
301 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300302#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300303 if (sz0 == sz1) {
304 primary_slot_sz += sz0;
305 secondary_slot_sz += sz1;
306 /* Scratch has to fit each swap operation to the size of the larger
307 * sector among the primary slot and the secondary slot.
308 */
309 if (sz0 > scratch_sz || sz1 > scratch_sz) {
310 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
311 return 0;
312 }
313 smaller = sz0 = sz1 = 0;
314 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300315#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300316 }
317
318 if ((i != num_sectors_primary) ||
319 (j != num_sectors_secondary) ||
320 (primary_slot_sz != secondary_slot_sz)) {
321 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
322 return 0;
323 }
324
325 return 1;
326}
327
328#define BOOT_LOG_SWAP_STATE(area, state) \
329 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
330 "image_ok=0x%x", \
331 (area), \
332 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
333 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
334 "bad"), \
335 (state)->swap_type, \
336 (state)->copy_done, \
337 (state)->image_ok)
338
339struct boot_status_table {
340 uint8_t bst_magic_primary_slot;
341 uint8_t bst_magic_scratch;
342 uint8_t bst_copy_done_primary_slot;
343 uint8_t bst_status_source;
344};
345
346/**
347 * This set of tables maps swap state contents to boot status location.
348 * When searching for a match, these tables must be iterated in order.
349 */
350static const struct boot_status_table boot_status_tables[] = {
351 {
352 /* | primary slot | scratch |
353 * ----------+--------------+--------------|
354 * magic | Good | Any |
355 * copy-done | Set | N/A |
356 * ----------+--------------+--------------'
357 * source: none |
358 * ----------------------------------------'
359 */
360 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
361 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
362 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
363 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
364 },
365
366 {
367 /* | primary slot | scratch |
368 * ----------+--------------+--------------|
369 * magic | Good | Any |
370 * copy-done | Unset | N/A |
371 * ----------+--------------+--------------'
372 * source: primary slot |
373 * ----------------------------------------'
374 */
375 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
376 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
377 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
378 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
379 },
380
381 {
382 /* | primary slot | scratch |
383 * ----------+--------------+--------------|
384 * magic | Any | Good |
385 * copy-done | Any | N/A |
386 * ----------+--------------+--------------'
387 * source: scratch |
388 * ----------------------------------------'
389 */
390 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
391 .bst_magic_scratch = BOOT_MAGIC_GOOD,
392 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
393 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
394 },
395 {
396 /* | primary slot | scratch |
397 * ----------+--------------+--------------|
398 * magic | Unset | Any |
399 * copy-done | Unset | N/A |
400 * ----------+--------------+--------------|
401 * source: varies |
402 * ----------------------------------------+--------------------------+
403 * This represents one of two cases: |
404 * o No swaps ever (no status to read, so no harm in checking). |
405 * o Mid-revert; status in primary slot. |
406 * -------------------------------------------------------------------'
407 */
408 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
409 .bst_magic_scratch = BOOT_MAGIC_ANY,
410 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
411 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
412 },
413};
414
415#define BOOT_STATUS_TABLES_COUNT \
416 (sizeof boot_status_tables / sizeof boot_status_tables[0])
417
418/**
419 * Determines where in flash the most recent boot status is stored. The boot
420 * status is necessary for completing a swap that was interrupted by a boot
421 * loader reset.
422 *
423 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
424 * be read from.
425 */
426int
427swap_status_source(struct boot_loader_state *state)
428{
429 const struct boot_status_table *table;
430 struct boot_swap_state state_scratch;
431 struct boot_swap_state state_primary_slot;
432 int rc;
433 size_t i;
434 uint8_t source;
435 uint8_t image_index;
436
437#if (BOOT_IMAGE_NUMBER == 1)
438 (void)state;
439#endif
440
441 image_index = BOOT_CURR_IMG(state);
442 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
443 &state_primary_slot);
444 assert(rc == 0);
445
446 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
447 assert(rc == 0);
448
449 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
450 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
451
452 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
453 table = &boot_status_tables[i];
454
455 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
456 state_primary_slot.magic) &&
457 boot_magic_compatible_check(table->bst_magic_scratch,
458 state_scratch.magic) &&
459 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
460 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
461 {
462 source = table->bst_status_source;
463
464#if (BOOT_IMAGE_NUMBER > 1)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200465 /* In case of multi image boot it can happen that if boot status
Fabio Utzig12d59162019-11-28 10:01:59 -0300466 * info is found on scratch area then it does not belong to the
467 * currently examined image.
468 */
469 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
470 state_scratch.image_num != BOOT_CURR_IMG(state)) {
471 source = BOOT_STATUS_SOURCE_NONE;
472 }
473#endif
474
475 BOOT_LOG_INF("Boot source: %s",
476 source == BOOT_STATUS_SOURCE_NONE ? "none" :
477 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
478 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
479 "primary slot" : "BUG; can't happen");
480 return source;
481 }
482 }
483
484 BOOT_LOG_INF("Boot source: none");
485 return BOOT_STATUS_SOURCE_NONE;
486}
487
Fabio Utzig74aef312019-11-28 11:05:34 -0300488#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300489/**
490 * Calculates the number of sectors the scratch area can contain. A "last"
491 * source sector is specified because images are copied backwards in flash
492 * (final index to index number 0).
493 *
494 * @param last_sector_idx The index of the last source sector
495 * (inclusive).
496 * @param out_first_sector_idx The index of the first source sector
497 * (inclusive) gets written here.
498 *
499 * @return The number of bytes comprised by the
500 * [first-sector, last-sector] range.
501 */
502static uint32_t
503boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
504 int *out_first_sector_idx)
505{
506 size_t scratch_sz;
507 uint32_t new_sz;
508 uint32_t sz;
509 int i;
510
511 sz = 0;
512
513 scratch_sz = boot_scratch_area_size(state);
514 for (i = last_sector_idx; i >= 0; i--) {
515 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
516 /*
517 * The secondary slot is not being checked here, because
518 * `boot_slots_compatible` already provides assurance that the copy size
519 * will be compatible with the primary slot and scratch.
520 */
521 if (new_sz > scratch_sz) {
522 break;
523 }
524 sz = new_sz;
525 }
526
527 /* i currently refers to a sector that doesn't fit or it is -1 because all
528 * sectors have been processed. In both cases, exclude sector i.
529 */
530 *out_first_sector_idx = i + 1;
531 return sz;
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 */
545static void
546boot_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 img_off;
555 uint32_t scratch_trailer_off;
556 struct boot_swap_state swap_state;
557 size_t last_sector;
558 bool erase_scratch;
559 uint8_t image_index;
560 int rc;
561
562 /* Calculate offset from start of image area. */
563 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
564
565 copy_sz = sz;
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200566 // trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); // TODO: fixme for status use case
567 trailer_sz = BOOT_WRITE_SZ(state);
Fabio Utzig12d59162019-11-28 10:01:59 -0300568
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 if ((img_off + sz) >
580 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
581 copy_sz -= trailer_sz;
582 }
583
584 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
585
586 image_index = BOOT_CURR_IMG(state);
587
588 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
589 &fap_primary_slot);
590 assert (rc == 0);
591
592 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
593 &fap_secondary_slot);
594 assert (rc == 0);
595
596 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
597 assert (rc == 0);
598
599 if (bs->state == BOOT_STATUS_STATE_0) {
600 BOOT_LOG_DBG("erasing scratch area");
601 rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
602 assert(rc == 0);
603
604 if (bs->idx == BOOT_STATUS_IDX_0) {
605 /* Write a trailer to the scratch area, even if we don't need the
606 * scratch area for status. We need a temporary place to store the
607 * `swap-type` while we erase the primary trailer.
David Vinczee574f2d2020-07-10 11:42:03 +0200608 */
Fabio Utzig12d59162019-11-28 10:01:59 -0300609 rc = swap_status_init(state, fap_scratch, bs);
610 assert(rc == 0);
611
612 if (!bs->use_scratch) {
613 /* Prepare the primary status area... here it is known that the
614 * last sector is not being used by the image data so it's safe
615 * to erase.
616 */
617 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
618 assert(rc == 0);
619
620 rc = swap_status_init(state, fap_primary_slot, bs);
621 assert(rc == 0);
622
623 /* Erase the temporary trailer from the scratch area. */
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200624#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300625 rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
626 assert(rc == 0);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200627#else
628 rc = swap_erase_trailer_sectors(state, fap_scratch);
629 assert(rc == 0);
630#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300631 }
632 }
633
634 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
635 img_off, 0, copy_sz);
636 assert(rc == 0);
637
638 rc = boot_write_status(state, bs);
639 bs->state = BOOT_STATUS_STATE_1;
640 BOOT_STATUS_ASSERT(rc == 0);
641 }
642
643 if (bs->state == BOOT_STATUS_STATE_1) {
644 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
645 assert(rc == 0);
646
647 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
648 img_off, img_off, copy_sz);
649 assert(rc == 0);
650
651 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
652 /* If not all sectors of the slot are being swapped,
653 * guarantee here that only the primary slot will have the state.
654 */
655 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
656 assert(rc == 0);
657 }
658
659 rc = boot_write_status(state, bs);
660 bs->state = BOOT_STATUS_STATE_2;
661 BOOT_STATUS_ASSERT(rc == 0);
662 }
663
664 if (bs->state == BOOT_STATUS_STATE_2) {
665 rc = boot_erase_region(fap_primary_slot, img_off, sz);
666 assert(rc == 0);
667
668 /* NOTE: If this is the final sector, we exclude the image trailer from
669 * this copy (copy_sz was truncated earlier).
670 */
671 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
672 0, img_off, copy_sz);
673 assert(rc == 0);
674
675 if (bs->use_scratch) {
676 scratch_trailer_off = boot_status_off(fap_scratch);
677
678 /* copy current status that is being maintained in scratch */
679 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
680 scratch_trailer_off, img_off + copy_sz,
681 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
682 BOOT_STATUS_ASSERT(rc == 0);
683
684 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH,
685 &swap_state);
686 assert(rc == 0);
687
688 if (swap_state.image_ok == BOOT_FLAG_SET) {
689 rc = boot_write_image_ok(fap_primary_slot);
690 assert(rc == 0);
691 }
692
693 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
694 rc = boot_write_swap_info(fap_primary_slot,
695 swap_state.swap_type, image_index);
696 assert(rc == 0);
697 }
698
699 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
700 assert(rc == 0);
701
702#ifdef MCUBOOT_ENC_IMAGES
Fabio Utzig4741c452019-12-19 15:32:41 -0300703 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300704 assert(rc == 0);
705
Fabio Utzig4741c452019-12-19 15:32:41 -0300706 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300707 assert(rc == 0);
708#endif
709 rc = boot_write_magic(fap_primary_slot);
710 assert(rc == 0);
711 }
712
713 /* If we wrote a trailer to the scratch area, erase it after we persist
714 * a trailer to the primary slot. We do this to prevent mcuboot from
715 * reading a stale status from the scratch area in case of immediate
716 * reset.
717 */
718 erase_scratch = bs->use_scratch;
719 bs->use_scratch = 0;
720
721 rc = boot_write_status(state, bs);
722 bs->idx++;
723 bs->state = BOOT_STATUS_STATE_0;
724 BOOT_STATUS_ASSERT(rc == 0);
725
726 if (erase_scratch) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200727#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300728 rc = boot_erase_region(fap_scratch, 0, sz);
729 assert(rc == 0);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200730#else
731 rc = swap_erase_trailer_sectors(state, fap_scratch);
732 assert(rc == 0);
733
734 rc = swap_erase_trailer_sectors(state, fap_secondary_slot); // TODO: check if needed and fix
735 assert(rc == 0);
736#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300737 }
738 }
739
740 flash_area_close(fap_primary_slot);
741 flash_area_close(fap_secondary_slot);
742 flash_area_close(fap_scratch);
743}
744
745void
746swap_run(struct boot_loader_state *state, struct boot_status *bs,
747 uint32_t copy_size)
748{
749 uint32_t sz;
750 int first_sector_idx;
751 int last_sector_idx;
752 uint32_t swap_idx;
753 int last_idx_secondary_slot;
754 uint32_t primary_slot_size;
755 uint32_t secondary_slot_size;
756 primary_slot_size = 0;
757 secondary_slot_size = 0;
758 last_sector_idx = 0;
759 last_idx_secondary_slot = 0;
760
761 /*
762 * Knowing the size of the largest image between both slots, here we
763 * find what is the last sector in the primary slot that needs swapping.
764 * Since we already know that both slots are compatible, the secondary
765 * slot's last sector is not really required after this check is finished.
766 */
767 while (1) {
768 if ((primary_slot_size < copy_size) ||
769 (primary_slot_size < secondary_slot_size)) {
770 primary_slot_size += boot_img_sector_size(state,
771 BOOT_PRIMARY_SLOT,
772 last_sector_idx);
773 }
774 if ((secondary_slot_size < copy_size) ||
775 (secondary_slot_size < primary_slot_size)) {
776 secondary_slot_size += boot_img_sector_size(state,
777 BOOT_SECONDARY_SLOT,
778 last_idx_secondary_slot);
779 }
780 if (primary_slot_size >= copy_size &&
781 secondary_slot_size >= copy_size &&
782 primary_slot_size == secondary_slot_size) {
783 break;
784 }
785 last_sector_idx++;
786 last_idx_secondary_slot++;
787 }
788
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200789 bs->op = BOOT_STATUS_OP_SWAP;
790
Fabio Utzig12d59162019-11-28 10:01:59 -0300791 swap_idx = 0;
792 while (last_sector_idx >= 0) {
793 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
794 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
795 boot_swap_sectors(first_sector_idx, sz, state, bs);
796 }
797
798 last_sector_idx = first_sector_idx - 1;
799 swap_idx++;
800 }
801
802}
David Vinczee574f2d2020-07-10 11:42:03 +0200803#endif /* !MCUBOOT_OVERWRITE_ONLY */
Fabio Utzig12d59162019-11-28 10:01:59 -0300804
Tamas Banfe031092020-09-10 17:32:39 +0200805#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
David Vinczee574f2d2020-07-10 11:42:03 +0200806
807#endif /* !MCUBOOT_SWAP_USING_MOVE */