blob: fea6ce18000aae766bcd5a48ec93a1463b3905dd [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
Roman Okhrimenko977b3752022-03-31 14:40:48 +030034BOOT_LOG_MODULE_DECLARE(mcuboot);
Fabio Utzig12d59162019-11-28 10:01:59 -030035
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{
Roman Okhrimenko977b3752022-03-31 14:40:48 +030057 const struct flash_area *fap = NULL;
Fabio Utzig12d59162019-11-28 10:01:59 -030058 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) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +030077#if MCUBOOT_SWAP_USING_SCRATCH
78 /* encrypted scratch partition needs area wrapper */
79 uint32_t image_proc_size = boot_scratch_area_size(state) * bs->idx;
80 uint32_t primary_img_size = boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_img_size;
81 uint32_t secondary_img_size = boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_img_size;
82 slot = 2;
83
84 if (secondary_img_size >= primary_img_size &&
85 secondary_img_size - primary_img_size < image_proc_size) {
86 slot = 1;
87 }
88#else
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020089 slot = 1;
Roman Okhrimenko977b3752022-03-31 14:40:48 +030090#endif
91 } else {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +020092 slot = 2;
93 }
94 }
95 }
96
Fabio Utzig12d59162019-11-28 10:01:59 -030097 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
98 rc = flash_area_open(area_id, &fap);
99 if (rc != 0) {
100 rc = BOOT_EFLASH;
101 goto done;
102 }
103
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300104 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200105 if (rc < 0) {
Fabio Utzig12d59162019-11-28 10:01:59 -0300106 rc = BOOT_EFLASH;
107 goto done;
108 }
109
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200110 /* We only know where the headers are located when bs is valid */
111 if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) {
112
113 if (bs->state != BOOT_STATUS_STATE_0) {
114
115 flash_area_close(fap);
116
117 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), saved_slot);
118 rc = flash_area_open(area_id, &fap);
119 if (rc != 0) {
120 rc = BOOT_EFLASH;
121 goto done;
122 }
123
124 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
125 if (rc < 0) {
126 rc = BOOT_EFLASH;
127 goto done;
128 }
129
130 if (out_hdr->ih_magic != IMAGE_MAGIC) {
131 rc = -1;
132 goto done;
133 }
134 }
135 }
136
Fabio Utzig12d59162019-11-28 10:01:59 -0300137 rc = 0;
138
139done:
140 flash_area_close(fap);
141 return rc;
142}
143
Tamas Banfe031092020-09-10 17:32:39 +0200144#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200145
146#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300147/**
148 * Reads the status of a partially-completed swap, if any. This is necessary
149 * to recover in case the boot lodaer was reset in the middle of a swap
150 * operation.
151 */
152int
153swap_read_status_bytes(const struct flash_area *fap,
154 struct boot_loader_state *state, struct boot_status *bs)
155{
156 uint32_t off;
157 uint8_t status;
158 int max_entries;
159 int found;
160 int found_idx;
161 int invalid;
162 int rc;
163 int i;
164
165 off = boot_status_off(fap);
166 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
167 if (max_entries < 0) {
168 return BOOT_EBADARGS;
169 }
170
171 found = 0;
172 found_idx = 0;
173 invalid = 0;
174 for (i = 0; i < max_entries; i++) {
Fabio Utzig4b2e55f2020-09-24 11:49:20 -0300175 rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
Fabio Utzig12d59162019-11-28 10:01:59 -0300176 &status, 1);
177 if (rc < 0) {
178 return BOOT_EFLASH;
179 }
180
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300181 if (status == flash_area_erased_val(fap)) {
Fabio Utzig12d59162019-11-28 10:01:59 -0300182 if (found && !found_idx) {
183 found_idx = i;
184 }
185 } else if (!found) {
186 found = 1;
187 } else if (found_idx) {
188 invalid = 1;
189 break;
190 }
191 }
192
193 if (invalid) {
194 /* This means there was an error writing status on the last
195 * swap. Tell user and move on to validation!
196 */
David Brown098de832019-12-10 11:58:01 -0700197#if !defined(__BOOTSIM__)
Fabio Utzig12d59162019-11-28 10:01:59 -0300198 BOOT_LOG_ERR("Detected inconsistent status!");
David Brown098de832019-12-10 11:58:01 -0700199#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300200
201#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
202 /* With validation of the primary slot disabled, there is no way
203 * to be sure the swapped primary slot is OK, so abort!
204 */
205 assert(0);
206#endif
207 }
208
209 if (found) {
210 if (!found_idx) {
211 found_idx = i;
212 }
213 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
214 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
215 }
216
217 return 0;
218}
219
220uint32_t
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300221boot_status_internal_off(const struct boot_status *bs, uint32_t elem_sz)
Fabio Utzig12d59162019-11-28 10:01:59 -0300222{
223 int idx_sz;
224
225 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
226
227 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
228 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
229}
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200230#endif /* !MCUBOOT_SWAP_USING_STATUS */
Fabio Utzig12d59162019-11-28 10:01:59 -0300231
232/*
233 * Slots are compatible when all sectors that store up to to size of the image
234 * round up to sector size, in both slot's are able to fit in the scratch
235 * area, and have sizes that are a multiple of each other (powers of two
236 * presumably!).
237 */
238int
239boot_slots_compatible(struct boot_loader_state *state)
240{
241 size_t num_sectors_primary;
242 size_t num_sectors_secondary;
243 size_t sz0, sz1;
244 size_t primary_slot_sz, secondary_slot_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300245#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300246 size_t scratch_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300247#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300248 size_t i, j;
249 int8_t smaller;
250
251 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
252 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200253
254 if (num_sectors_secondary == 0) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300255 BOOT_LOG_WRN("Upgrade disabled for image %u", (unsigned)BOOT_CURR_IMG(state));
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200256 return 0;
257 }
258
Fabio Utzig12d59162019-11-28 10:01:59 -0300259 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
260 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
261 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300262 BOOT_LOG_DBG("sectors_primary (%lu) or sectors_secondary (%lu) > BOOT_MAX_IMG_SECTORS (%lu)",
263 (unsigned long)num_sectors_primary,
264 (unsigned long)num_sectors_secondary,
265 (unsigned long)BOOT_MAX_IMG_SECTORS);
Fabio Utzig12d59162019-11-28 10:01:59 -0300266 return 0;
267 }
268
Fabio Utzig74aef312019-11-28 11:05:34 -0300269#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300270 scratch_sz = boot_scratch_area_size(state);
Fabio Utzig74aef312019-11-28 11:05:34 -0300271#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300272
273 /*
274 * The following loop scans all sectors in a linear fashion, assuring that
275 * for each possible sector in each slot, it is able to fit in the other
276 * slot's sector or sectors. Slot's should be compatible as long as any
277 * number of a slot's sectors are able to fit into another, which only
278 * excludes cases where sector sizes are not a multiple of each other.
279 */
280 i = sz0 = primary_slot_sz = 0;
281 j = sz1 = secondary_slot_sz = 0;
282 smaller = 0;
283 while (i < num_sectors_primary || j < num_sectors_secondary) {
284 if (sz0 == sz1) {
285 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
286 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
287 i++;
288 j++;
289 } else if (sz0 < sz1) {
290 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
291 /* Guarantee that multiple sectors of the secondary slot
292 * fit into the primary slot.
293 */
294 if (smaller == 2) {
295 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
296 return 0;
297 }
298 smaller = 1;
299 i++;
300 } else {
301 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
302 /* Guarantee that multiple sectors of the primary slot
303 * fit into the secondary slot.
304 */
305 if (smaller == 1) {
306 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
307 return 0;
308 }
309 smaller = 2;
310 j++;
311 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300312#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300313 if (sz0 == sz1) {
314 primary_slot_sz += sz0;
315 secondary_slot_sz += sz1;
316 /* Scratch has to fit each swap operation to the size of the larger
317 * sector among the primary slot and the secondary slot.
318 */
319 if (sz0 > scratch_sz || sz1 > scratch_sz) {
320 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
321 return 0;
322 }
323 smaller = sz0 = sz1 = 0;
324 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300325#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300326 }
327
328 if ((i != num_sectors_primary) ||
329 (j != num_sectors_secondary) ||
330 (primary_slot_sz != secondary_slot_sz)) {
331 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
332 return 0;
333 }
334
335 return 1;
336}
337
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300338#define BOOT_LOG_SWAP_STATE(area, state) \
339 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, image_ok=0x%x", \
340 (area), \
341 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
342 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
343 "bad"), \
344 (unsigned)(state)->swap_type, \
345 (unsigned)(state)->copy_done, \
346 (unsigned)(state)->image_ok)
Fabio Utzig12d59162019-11-28 10:01:59 -0300347
348struct boot_status_table {
349 uint8_t bst_magic_primary_slot;
350 uint8_t bst_magic_scratch;
351 uint8_t bst_copy_done_primary_slot;
352 uint8_t bst_status_source;
353};
354
355/**
356 * This set of tables maps swap state contents to boot status location.
357 * When searching for a match, these tables must be iterated in order.
358 */
359static const struct boot_status_table boot_status_tables[] = {
360 {
361 /* | primary slot | scratch |
362 * ----------+--------------+--------------|
363 * magic | Good | Any |
364 * copy-done | Set | N/A |
365 * ----------+--------------+--------------'
366 * source: none |
367 * ----------------------------------------'
368 */
369 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
370 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
371 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
372 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
373 },
374
375 {
376 /* | primary slot | scratch |
377 * ----------+--------------+--------------|
378 * magic | Good | Any |
379 * copy-done | Unset | N/A |
380 * ----------+--------------+--------------'
381 * source: primary slot |
382 * ----------------------------------------'
383 */
384 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
385 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
386 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
387 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
388 },
389
390 {
391 /* | primary slot | scratch |
392 * ----------+--------------+--------------|
393 * magic | Any | Good |
394 * copy-done | Any | N/A |
395 * ----------+--------------+--------------'
396 * source: scratch |
397 * ----------------------------------------'
398 */
399 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
400 .bst_magic_scratch = BOOT_MAGIC_GOOD,
401 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
402 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
403 },
404 {
405 /* | primary slot | scratch |
406 * ----------+--------------+--------------|
407 * magic | Unset | Any |
408 * copy-done | Unset | N/A |
409 * ----------+--------------+--------------|
410 * source: varies |
411 * ----------------------------------------+--------------------------+
412 * This represents one of two cases: |
413 * o No swaps ever (no status to read, so no harm in checking). |
414 * o Mid-revert; status in primary slot. |
415 * -------------------------------------------------------------------'
416 */
417 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
418 .bst_magic_scratch = BOOT_MAGIC_ANY,
419 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
420 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
421 },
422};
423
424#define BOOT_STATUS_TABLES_COUNT \
425 (sizeof boot_status_tables / sizeof boot_status_tables[0])
426
427/**
428 * Determines where in flash the most recent boot status is stored. The boot
429 * status is necessary for completing a swap that was interrupted by a boot
430 * loader reset.
431 *
432 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
433 * be read from.
434 */
435int
436swap_status_source(struct boot_loader_state *state)
437{
438 const struct boot_status_table *table;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300439#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300440 struct boot_swap_state state_scratch;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300441#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300442 struct boot_swap_state state_primary_slot;
443 int rc;
444 size_t i;
445 uint8_t source;
446 uint8_t image_index;
447
448#if (BOOT_IMAGE_NUMBER == 1)
449 (void)state;
450#endif
451
452 image_index = BOOT_CURR_IMG(state);
453 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
454 &state_primary_slot);
455 assert(rc == 0);
456
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300457#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300458 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
459 assert(rc == 0);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300460#endif
461
462 (void)rc;
Fabio Utzig12d59162019-11-28 10:01:59 -0300463
464 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300465#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300466 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300467#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300468 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
469 table = &boot_status_tables[i];
470
471 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
472 state_primary_slot.magic) &&
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300473#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300474 boot_magic_compatible_check(table->bst_magic_scratch,
475 state_scratch.magic) &&
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300476#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300477 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
478 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
479 {
480 source = table->bst_status_source;
481
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300482#if (BOOT_IMAGE_NUMBER > 1) && MCUBOOT_SWAP_USING_SCRATCH
483 /* In case of multi-image boot it can happen that if boot status
Fabio Utzig12d59162019-11-28 10:01:59 -0300484 * info is found on scratch area then it does not belong to the
485 * currently examined image.
486 */
487 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
488 state_scratch.image_num != BOOT_CURR_IMG(state)) {
489 source = BOOT_STATUS_SOURCE_NONE;
490 }
491#endif
492
493 BOOT_LOG_INF("Boot source: %s",
494 source == BOOT_STATUS_SOURCE_NONE ? "none" :
495 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
496 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
497 "primary slot" : "BUG; can't happen");
498 return source;
499 }
500 }
501
502 BOOT_LOG_INF("Boot source: none");
503 return BOOT_STATUS_SOURCE_NONE;
504}
505
Fabio Utzig74aef312019-11-28 11:05:34 -0300506#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300507/**
508 * Calculates the number of sectors the scratch area can contain. A "last"
509 * source sector is specified because images are copied backwards in flash
510 * (final index to index number 0).
511 *
512 * @param last_sector_idx The index of the last source sector
513 * (inclusive).
514 * @param out_first_sector_idx The index of the first source sector
515 * (inclusive) gets written here.
516 *
517 * @return The number of bytes comprised by the
518 * [first-sector, last-sector] range.
519 */
520static uint32_t
521boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
522 int *out_first_sector_idx)
523{
524 size_t scratch_sz;
525 uint32_t new_sz;
526 uint32_t sz;
527 int i;
528
529 sz = 0;
530
531 scratch_sz = boot_scratch_area_size(state);
532 for (i = last_sector_idx; i >= 0; i--) {
533 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
534 /*
535 * The secondary slot is not being checked here, because
536 * `boot_slots_compatible` already provides assurance that the copy size
537 * will be compatible with the primary slot and scratch.
538 */
539 if (new_sz > scratch_sz) {
540 break;
541 }
542 sz = new_sz;
543 }
544
545 /* i currently refers to a sector that doesn't fit or it is -1 because all
546 * sectors have been processed. In both cases, exclude sector i.
547 */
548 *out_first_sector_idx = i + 1;
549 return sz;
550}
551
552/**
553 * Swaps the contents of two flash regions within the two image slots.
554 *
555 * @param idx The index of the first sector in the range of
556 * sectors being swapped.
557 * @param sz The number of bytes to swap.
558 * @param bs The current boot status. This struct gets
559 * updated according to the outcome.
560 *
561 * @return 0 on success; nonzero on failure.
562 */
563static void
564boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
565 struct boot_status *bs)
566{
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300567 const struct flash_area *fap_primary_slot = NULL;
568 const struct flash_area *fap_secondary_slot = NULL;
569 const struct flash_area *fap_scratch = NULL;
Fabio Utzig12d59162019-11-28 10:01:59 -0300570 uint32_t copy_sz;
571 uint32_t trailer_sz;
572 uint32_t img_off;
573 uint32_t scratch_trailer_off;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300574 struct boot_swap_state swap_state = {0};
Fabio Utzig12d59162019-11-28 10:01:59 -0300575 size_t last_sector;
576 bool erase_scratch;
577 uint8_t image_index;
578 int rc;
579
580 /* Calculate offset from start of image area. */
581 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
582
583 copy_sz = sz;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300584
585#ifdef MCUBOOT_SWAP_USING_STATUS
586 trailer_sz = BOOT_WRITE_SZ(state); // TODO: deep investigation in swap_status use case
587#else
588 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
589#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300590
591 /* sz in this function is always sized on a multiple of the sector size.
592 * The check against the start offset of the last sector
593 * is to determine if we're swapping the last sector. The last sector
594 * needs special handling because it's where the trailer lives. If we're
595 * copying it, we need to use scratch to write the trailer temporarily.
596 *
597 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
598 * controls if special handling is needed (swapping last sector).
599 */
600 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
601 if ((img_off + sz) >
602 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
603 copy_sz -= trailer_sz;
604 }
605
606 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
607
608 image_index = BOOT_CURR_IMG(state);
609
610 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
611 &fap_primary_slot);
612 assert (rc == 0);
613
614 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
615 &fap_secondary_slot);
616 assert (rc == 0);
617
618 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
619 assert (rc == 0);
620
621 if (bs->state == BOOT_STATUS_STATE_0) {
622 BOOT_LOG_DBG("erasing scratch area");
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300623 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300624 assert(rc == 0);
625
626 if (bs->idx == BOOT_STATUS_IDX_0) {
627 /* Write a trailer to the scratch area, even if we don't need the
628 * scratch area for status. We need a temporary place to store the
629 * `swap-type` while we erase the primary trailer.
David Vinczee574f2d2020-07-10 11:42:03 +0200630 */
Fabio Utzig12d59162019-11-28 10:01:59 -0300631 rc = swap_status_init(state, fap_scratch, bs);
632 assert(rc == 0);
633
634 if (!bs->use_scratch) {
635 /* Prepare the primary status area... here it is known that the
636 * last sector is not being used by the image data so it's safe
637 * to erase.
638 */
639 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
640 assert(rc == 0);
641
642 rc = swap_status_init(state, fap_primary_slot, bs);
643 assert(rc == 0);
644
645 /* Erase the temporary trailer from the scratch area. */
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200646#ifndef MCUBOOT_SWAP_USING_STATUS
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300647 rc = boot_erase_region(fap_scratch, 0,
648 flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300649 assert(rc == 0);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200650#else
651 rc = swap_erase_trailer_sectors(state, fap_scratch);
652 assert(rc == 0);
653#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300654 }
655 }
656
657 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
658 img_off, 0, copy_sz);
659 assert(rc == 0);
660
661 rc = boot_write_status(state, bs);
662 bs->state = BOOT_STATUS_STATE_1;
663 BOOT_STATUS_ASSERT(rc == 0);
664 }
665
666 if (bs->state == BOOT_STATUS_STATE_1) {
667 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
668 assert(rc == 0);
669
670 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
671 img_off, img_off, copy_sz);
672 assert(rc == 0);
673
674 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
675 /* If not all sectors of the slot are being swapped,
676 * guarantee here that only the primary slot will have the state.
677 */
678 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
679 assert(rc == 0);
680 }
681
682 rc = boot_write_status(state, bs);
683 bs->state = BOOT_STATUS_STATE_2;
684 BOOT_STATUS_ASSERT(rc == 0);
685 }
686
687 if (bs->state == BOOT_STATUS_STATE_2) {
688 rc = boot_erase_region(fap_primary_slot, img_off, sz);
689 assert(rc == 0);
690
691 /* NOTE: If this is the final sector, we exclude the image trailer from
692 * this copy (copy_sz was truncated earlier).
693 */
694 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
695 0, img_off, copy_sz);
696 assert(rc == 0);
697
698 if (bs->use_scratch) {
699 scratch_trailer_off = boot_status_off(fap_scratch);
700
701 /* copy current status that is being maintained in scratch */
702 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
703 scratch_trailer_off, img_off + copy_sz,
704 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
705 BOOT_STATUS_ASSERT(rc == 0);
706
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300707 rc = boot_read_swap_state(fap_scratch, &swap_state);
Fabio Utzig12d59162019-11-28 10:01:59 -0300708 assert(rc == 0);
709
710 if (swap_state.image_ok == BOOT_FLAG_SET) {
711 rc = boot_write_image_ok(fap_primary_slot);
712 assert(rc == 0);
713 }
714
715 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
716 rc = boot_write_swap_info(fap_primary_slot,
717 swap_state.swap_type, image_index);
718 assert(rc == 0);
719 }
720
721 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
722 assert(rc == 0);
723
724#ifdef MCUBOOT_ENC_IMAGES
Fabio Utzig4741c452019-12-19 15:32:41 -0300725 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300726 assert(rc == 0);
727
Fabio Utzig4741c452019-12-19 15:32:41 -0300728 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300729 assert(rc == 0);
730#endif
731 rc = boot_write_magic(fap_primary_slot);
732 assert(rc == 0);
733 }
734
735 /* If we wrote a trailer to the scratch area, erase it after we persist
736 * a trailer to the primary slot. We do this to prevent mcuboot from
737 * reading a stale status from the scratch area in case of immediate
738 * reset.
739 */
740 erase_scratch = bs->use_scratch;
741 bs->use_scratch = 0;
742
743 rc = boot_write_status(state, bs);
744 bs->idx++;
745 bs->state = BOOT_STATUS_STATE_0;
746 BOOT_STATUS_ASSERT(rc == 0);
747
748 if (erase_scratch) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200749#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300750 rc = boot_erase_region(fap_scratch, 0, sz);
751 assert(rc == 0);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200752#else
753 rc = swap_erase_trailer_sectors(state, fap_scratch);
754 assert(rc == 0);
755
756 rc = swap_erase_trailer_sectors(state, fap_secondary_slot); // TODO: check if needed and fix
757 assert(rc == 0);
758#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300759 }
760 }
761
762 flash_area_close(fap_primary_slot);
763 flash_area_close(fap_secondary_slot);
764 flash_area_close(fap_scratch);
765}
766
767void
768swap_run(struct boot_loader_state *state, struct boot_status *bs,
769 uint32_t copy_size)
770{
771 uint32_t sz;
772 int first_sector_idx;
773 int last_sector_idx;
774 uint32_t swap_idx;
775 int last_idx_secondary_slot;
776 uint32_t primary_slot_size;
777 uint32_t secondary_slot_size;
778 primary_slot_size = 0;
779 secondary_slot_size = 0;
780 last_sector_idx = 0;
781 last_idx_secondary_slot = 0;
782
783 /*
784 * Knowing the size of the largest image between both slots, here we
785 * find what is the last sector in the primary slot that needs swapping.
786 * Since we already know that both slots are compatible, the secondary
787 * slot's last sector is not really required after this check is finished.
788 */
789 while (1) {
790 if ((primary_slot_size < copy_size) ||
791 (primary_slot_size < secondary_slot_size)) {
792 primary_slot_size += boot_img_sector_size(state,
793 BOOT_PRIMARY_SLOT,
794 last_sector_idx);
795 }
796 if ((secondary_slot_size < copy_size) ||
797 (secondary_slot_size < primary_slot_size)) {
798 secondary_slot_size += boot_img_sector_size(state,
799 BOOT_SECONDARY_SLOT,
800 last_idx_secondary_slot);
801 }
802 if (primary_slot_size >= copy_size &&
803 secondary_slot_size >= copy_size &&
804 primary_slot_size == secondary_slot_size) {
805 break;
806 }
807 last_sector_idx++;
808 last_idx_secondary_slot++;
809 }
810
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200811 bs->op = BOOT_STATUS_OP_SWAP;
812
Fabio Utzig12d59162019-11-28 10:01:59 -0300813 swap_idx = 0;
814 while (last_sector_idx >= 0) {
815 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
816 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
817 boot_swap_sectors(first_sector_idx, sz, state, bs);
818 }
819
820 last_sector_idx = first_sector_idx - 1;
821 swap_idx++;
822 }
823
824}
David Vinczee574f2d2020-07-10 11:42:03 +0200825#endif /* !MCUBOOT_OVERWRITE_ONLY */
Fabio Utzig12d59162019-11-28 10:01:59 -0300826
Tamas Banfe031092020-09-10 17:32:39 +0200827#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
David Vinczee574f2d2020-07-10 11:42:03 +0200828
829#endif /* !MCUBOOT_SWAP_USING_MOVE */