blob: 8b55d2ab9917801e95d32d9e733571f8ffa6a024 [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;
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030059 int rc = 0;
Fabio Utzig12d59162019-11-28 10:01:59 -030060
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);
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +030098
Fabio Utzig12d59162019-11-28 10:01:59 -030099 rc = flash_area_open(area_id, &fap);
100 if (rc != 0) {
101 rc = BOOT_EFLASH;
102 goto done;
103 }
104
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300105 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200106 if (rc < 0) {
Fabio Utzig12d59162019-11-28 10:01:59 -0300107 rc = BOOT_EFLASH;
108 goto done;
109 }
110
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200111 /* We only know where the headers are located when bs is valid */
112 if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) {
113
114 if (bs->state != BOOT_STATUS_STATE_0) {
115
116 flash_area_close(fap);
117
118 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), saved_slot);
119 rc = flash_area_open(area_id, &fap);
120 if (rc != 0) {
121 rc = BOOT_EFLASH;
122 goto done;
123 }
124
125 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
126 if (rc < 0) {
127 rc = BOOT_EFLASH;
128 goto done;
129 }
130
131 if (out_hdr->ih_magic != IMAGE_MAGIC) {
132 rc = -1;
133 goto done;
134 }
135 }
136 }
137
Fabio Utzig12d59162019-11-28 10:01:59 -0300138 rc = 0;
139
140done:
141 flash_area_close(fap);
142 return rc;
143}
144
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200145#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300146/**
147 * Reads the status of a partially-completed swap, if any. This is necessary
148 * to recover in case the boot lodaer was reset in the middle of a swap
149 * operation.
150 */
151int
152swap_read_status_bytes(const struct flash_area *fap,
153 struct boot_loader_state *state, struct boot_status *bs)
154{
155 uint32_t off;
156 uint8_t status;
157 int max_entries;
158 int found;
159 int found_idx;
160 int invalid;
161 int rc;
162 int i;
163
164 off = boot_status_off(fap);
165 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
166 if (max_entries < 0) {
167 return BOOT_EBADARGS;
168 }
169
170 found = 0;
171 found_idx = 0;
172 invalid = 0;
173 for (i = 0; i < max_entries; i++) {
Fabio Utzig4b2e55f2020-09-24 11:49:20 -0300174 rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
Fabio Utzig12d59162019-11-28 10:01:59 -0300175 &status, 1);
176 if (rc < 0) {
177 return BOOT_EFLASH;
178 }
179
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300180 if (status == flash_area_erased_val(fap)) {
Fabio Utzig12d59162019-11-28 10:01:59 -0300181 if (found && !found_idx) {
182 found_idx = i;
183 }
184 } else if (!found) {
185 found = 1;
186 } else if (found_idx) {
187 invalid = 1;
188 break;
189 }
190 }
191
192 if (invalid) {
193 /* This means there was an error writing status on the last
194 * swap. Tell user and move on to validation!
195 */
David Brown098de832019-12-10 11:58:01 -0700196#if !defined(__BOOTSIM__)
Fabio Utzig12d59162019-11-28 10:01:59 -0300197 BOOT_LOG_ERR("Detected inconsistent status!");
David Brown098de832019-12-10 11:58:01 -0700198#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300199
200#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
201 /* With validation of the primary slot disabled, there is no way
202 * to be sure the swapped primary slot is OK, so abort!
203 */
204 assert(0);
205#endif
206 }
207
208 if (found) {
209 if (!found_idx) {
210 found_idx = i;
211 }
212 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
213 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
214 }
215
216 return 0;
217}
218
219uint32_t
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300220boot_status_internal_off(const struct boot_status *bs, uint32_t elem_sz)
Fabio Utzig12d59162019-11-28 10:01:59 -0300221{
222 int idx_sz;
223
224 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
225
226 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
227 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
228}
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200229#endif /* !MCUBOOT_SWAP_USING_STATUS */
Fabio Utzig12d59162019-11-28 10:01:59 -0300230
231/*
232 * Slots are compatible when all sectors that store up to to size of the image
233 * round up to sector size, in both slot's are able to fit in the scratch
234 * area, and have sizes that are a multiple of each other (powers of two
235 * presumably!).
236 */
237int
238boot_slots_compatible(struct boot_loader_state *state)
239{
240 size_t num_sectors_primary;
241 size_t num_sectors_secondary;
242 size_t sz0, sz1;
243 size_t primary_slot_sz, secondary_slot_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300244#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300245 size_t scratch_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300246#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300247 size_t i, j;
248 int8_t smaller;
249
250 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
251 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200252
253 if (num_sectors_secondary == 0) {
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300254 BOOT_LOG_WRN("Upgrade disabled for image %u", (unsigned)BOOT_CURR_IMG(state));
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200255 return 0;
256 }
257
Fabio Utzig12d59162019-11-28 10:01:59 -0300258 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
259 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
260 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300261 BOOT_LOG_DBG("sectors_primary (%lu) or sectors_secondary (%lu) > BOOT_MAX_IMG_SECTORS (%lu)",
262 (unsigned long)num_sectors_primary,
263 (unsigned long)num_sectors_secondary,
264 (unsigned long)BOOT_MAX_IMG_SECTORS);
Fabio Utzig12d59162019-11-28 10:01:59 -0300265 return 0;
266 }
267
Fabio Utzig74aef312019-11-28 11:05:34 -0300268#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300269 scratch_sz = boot_scratch_area_size(state);
Fabio Utzig74aef312019-11-28 11:05:34 -0300270#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300271
272 /*
273 * The following loop scans all sectors in a linear fashion, assuring that
274 * for each possible sector in each slot, it is able to fit in the other
275 * slot's sector or sectors. Slot's should be compatible as long as any
276 * number of a slot's sectors are able to fit into another, which only
277 * excludes cases where sector sizes are not a multiple of each other.
278 */
279 i = sz0 = primary_slot_sz = 0;
280 j = sz1 = secondary_slot_sz = 0;
281 smaller = 0;
282 while (i < num_sectors_primary || j < num_sectors_secondary) {
283 if (sz0 == sz1) {
284 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
285 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
286 i++;
287 j++;
288 } else if (sz0 < sz1) {
289 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
290 /* Guarantee that multiple sectors of the secondary slot
291 * fit into the primary slot.
292 */
293 if (smaller == 2) {
294 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
295 return 0;
296 }
297 smaller = 1;
298 i++;
299 } else {
300 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
301 /* Guarantee that multiple sectors of the primary slot
302 * fit into the secondary slot.
303 */
304 if (smaller == 1) {
305 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
306 return 0;
307 }
308 smaller = 2;
309 j++;
310 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300311#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300312 if (sz0 == sz1) {
313 primary_slot_sz += sz0;
314 secondary_slot_sz += sz1;
315 /* Scratch has to fit each swap operation to the size of the larger
316 * sector among the primary slot and the secondary slot.
317 */
318 if (sz0 > scratch_sz || sz1 > scratch_sz) {
319 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
320 return 0;
321 }
322 smaller = sz0 = sz1 = 0;
323 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300324#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300325 }
326
327 if ((i != num_sectors_primary) ||
328 (j != num_sectors_secondary) ||
329 (primary_slot_sz != secondary_slot_sz)) {
330 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
331 return 0;
332 }
333
334 return 1;
335}
336
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300337#define BOOT_LOG_SWAP_STATE(area, state) \
338 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, image_ok=0x%x", \
339 (area), \
340 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
341 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
342 "bad"), \
343 (unsigned)(state)->swap_type, \
344 (unsigned)(state)->copy_done, \
345 (unsigned)(state)->image_ok)
Fabio Utzig12d59162019-11-28 10:01:59 -0300346
347struct boot_status_table {
348 uint8_t bst_magic_primary_slot;
349 uint8_t bst_magic_scratch;
350 uint8_t bst_copy_done_primary_slot;
351 uint8_t bst_status_source;
352};
353
354/**
355 * This set of tables maps swap state contents to boot status location.
356 * When searching for a match, these tables must be iterated in order.
357 */
358static const struct boot_status_table boot_status_tables[] = {
359 {
360 /* | primary slot | scratch |
361 * ----------+--------------+--------------|
362 * magic | Good | Any |
363 * copy-done | Set | N/A |
364 * ----------+--------------+--------------'
365 * source: none |
366 * ----------------------------------------'
367 */
368 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
369 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
370 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
371 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
372 },
373
374 {
375 /* | primary slot | scratch |
376 * ----------+--------------+--------------|
377 * magic | Good | Any |
378 * copy-done | Unset | N/A |
379 * ----------+--------------+--------------'
380 * source: primary slot |
381 * ----------------------------------------'
382 */
383 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
384 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
385 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
386 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
387 },
388
389 {
390 /* | primary slot | scratch |
391 * ----------+--------------+--------------|
392 * magic | Any | Good |
393 * copy-done | Any | N/A |
394 * ----------+--------------+--------------'
395 * source: scratch |
396 * ----------------------------------------'
397 */
398 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
399 .bst_magic_scratch = BOOT_MAGIC_GOOD,
400 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
401 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
402 },
403 {
404 /* | primary slot | scratch |
405 * ----------+--------------+--------------|
406 * magic | Unset | Any |
407 * copy-done | Unset | N/A |
408 * ----------+--------------+--------------|
409 * source: varies |
410 * ----------------------------------------+--------------------------+
411 * This represents one of two cases: |
412 * o No swaps ever (no status to read, so no harm in checking). |
413 * o Mid-revert; status in primary slot. |
414 * -------------------------------------------------------------------'
415 */
416 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
417 .bst_magic_scratch = BOOT_MAGIC_ANY,
418 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
419 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
420 },
421};
422
423#define BOOT_STATUS_TABLES_COUNT \
424 (sizeof boot_status_tables / sizeof boot_status_tables[0])
425
426/**
427 * Determines where in flash the most recent boot status is stored. The boot
428 * status is necessary for completing a swap that was interrupted by a boot
429 * loader reset.
430 *
431 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
432 * be read from.
433 */
434int
435swap_status_source(struct boot_loader_state *state)
436{
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000437 const struct boot_status_table *table = NULL;
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300438#if MCUBOOT_SWAP_USING_SCRATCH
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000439 struct boot_swap_state state_scratch = {0};
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300440#endif
Dovhal Artem (CSUKR CSS ICW SW FW 1)f7a3d1b2022-04-01 15:07:37 +0000441 struct boot_swap_state state_primary_slot = {0};
Fabio Utzig12d59162019-11-28 10:01:59 -0300442 int rc;
443 size_t i;
444 uint8_t source;
445 uint8_t image_index;
446
447#if (BOOT_IMAGE_NUMBER == 1)
448 (void)state;
449#endif
450
451 image_index = BOOT_CURR_IMG(state);
452 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
453 &state_primary_slot);
454 assert(rc == 0);
455
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300456#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300457 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
458 assert(rc == 0);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300459#endif
460
461 (void)rc;
Fabio Utzig12d59162019-11-28 10:01:59 -0300462
463 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300464#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300465 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300466#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300467 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
468 table = &boot_status_tables[i];
469
470 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
471 state_primary_slot.magic) &&
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300472#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300473 boot_magic_compatible_check(table->bst_magic_scratch,
474 state_scratch.magic) &&
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300475#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300476 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
477 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
478 {
479 source = table->bst_status_source;
480
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300481#if (BOOT_IMAGE_NUMBER > 1) && MCUBOOT_SWAP_USING_SCRATCH
482 /* In case of multi-image boot it can happen that if boot status
Fabio Utzig12d59162019-11-28 10:01:59 -0300483 * info is found on scratch area then it does not belong to the
484 * currently examined image.
485 */
486 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
487 state_scratch.image_num != BOOT_CURR_IMG(state)) {
488 source = BOOT_STATUS_SOURCE_NONE;
489 }
490#endif
491
492 BOOT_LOG_INF("Boot source: %s",
493 source == BOOT_STATUS_SOURCE_NONE ? "none" :
494 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
495 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
496 "primary slot" : "BUG; can't happen");
497 return source;
498 }
499 }
500
501 BOOT_LOG_INF("Boot source: none");
502 return BOOT_STATUS_SOURCE_NONE;
503}
504
Fabio Utzig74aef312019-11-28 11:05:34 -0300505#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300506/**
507 * Calculates the number of sectors the scratch area can contain. A "last"
508 * source sector is specified because images are copied backwards in flash
509 * (final index to index number 0).
510 *
511 * @param last_sector_idx The index of the last source sector
512 * (inclusive).
513 * @param out_first_sector_idx The index of the first source sector
514 * (inclusive) gets written here.
515 *
516 * @return The number of bytes comprised by the
517 * [first-sector, last-sector] range.
518 */
519static uint32_t
520boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
521 int *out_first_sector_idx)
522{
523 size_t scratch_sz;
524 uint32_t new_sz;
525 uint32_t sz;
526 int i;
527
528 sz = 0;
529
530 scratch_sz = boot_scratch_area_size(state);
531 for (i = last_sector_idx; i >= 0; i--) {
532 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
533 /*
534 * The secondary slot is not being checked here, because
535 * `boot_slots_compatible` already provides assurance that the copy size
536 * will be compatible with the primary slot and scratch.
537 */
538 if (new_sz > scratch_sz) {
539 break;
540 }
541 sz = new_sz;
542 }
543
544 /* i currently refers to a sector that doesn't fit or it is -1 because all
545 * sectors have been processed. In both cases, exclude sector i.
546 */
547 *out_first_sector_idx = i + 1;
548 return sz;
549}
550
551/**
552 * Swaps the contents of two flash regions within the two image slots.
553 *
554 * @param idx The index of the first sector in the range of
555 * sectors being swapped.
556 * @param sz The number of bytes to swap.
557 * @param bs The current boot status. This struct gets
558 * updated according to the outcome.
559 *
560 * @return 0 on success; nonzero on failure.
561 */
562static void
563boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
564 struct boot_status *bs)
565{
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300566 const struct flash_area *fap_primary_slot = NULL;
567 const struct flash_area *fap_secondary_slot = NULL;
568 const struct flash_area *fap_scratch = NULL;
Fabio Utzig12d59162019-11-28 10:01:59 -0300569 uint32_t copy_sz;
570 uint32_t trailer_sz;
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300571 uint32_t sector_sz;
Fabio Utzig12d59162019-11-28 10:01:59 -0300572 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;
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300578 __attribute__((unused)) int rc;
Fabio Utzig12d59162019-11-28 10:01:59 -0300579
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
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300587 /* TODO: this code needs to be refined. It is introduced to overcome
588 * situation when MCUBootApp lives in internal memory, but user app
589 * is executed from different type memory - external in XIP mode in
590 * this case. This situation now arise on PSOC6 when XIP execution is
591 * used, bay may be applicable to other devices, where solution is
592 * distributed between memories with different write/erase sizes.
593 */
594#ifdef CY_BOOT_USE_EXTERNAL_FLASH
595 if (trailer_sz > MEMORY_ALIGN) {
596 trailer_sz = MEMORY_ALIGN;
597 }
598#endif
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300599#else
600 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
601#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300602
603 /* sz in this function is always sized on a multiple of the sector size.
604 * The check against the start offset of the last sector
605 * is to determine if we're swapping the last sector. The last sector
606 * needs special handling because it's where the trailer lives. If we're
607 * copying it, we need to use scratch to write the trailer temporarily.
608 *
609 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
610 * controls if special handling is needed (swapping last sector).
611 */
612 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300613 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, last_sector);
614
615 if (sector_sz < trailer_sz) {
616 uint32_t trailer_sector_sz = sector_sz;
617
618 while (trailer_sector_sz < trailer_sz) {
619 /* Consider that the image trailer may span across sectors of
620 * different sizes.
621 */
622 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, --last_sector);
623
624 trailer_sector_sz += sector_sz;
625 }
626 }
627
Fabio Utzig12d59162019-11-28 10:01:59 -0300628 if ((img_off + sz) >
629 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
630 copy_sz -= trailer_sz;
631 }
632
633 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
634
635 image_index = BOOT_CURR_IMG(state);
636
637 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
638 &fap_primary_slot);
639 assert (rc == 0);
640
641 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
642 &fap_secondary_slot);
643 assert (rc == 0);
644
645 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
646 assert (rc == 0);
647
648 if (bs->state == BOOT_STATUS_STATE_0) {
649 BOOT_LOG_DBG("erasing scratch area");
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300650 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300651 assert(rc == 0);
652
653 if (bs->idx == BOOT_STATUS_IDX_0) {
654 /* Write a trailer to the scratch area, even if we don't need the
655 * scratch area for status. We need a temporary place to store the
656 * `swap-type` while we erase the primary trailer.
David Vinczee574f2d2020-07-10 11:42:03 +0200657 */
Fabio Utzig12d59162019-11-28 10:01:59 -0300658 rc = swap_status_init(state, fap_scratch, bs);
659 assert(rc == 0);
660
661 if (!bs->use_scratch) {
662 /* Prepare the primary status area... here it is known that the
663 * last sector is not being used by the image data so it's safe
664 * to erase.
665 */
666 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
667 assert(rc == 0);
668
669 rc = swap_status_init(state, fap_primary_slot, bs);
670 assert(rc == 0);
671
672 /* Erase the temporary trailer from the scratch area. */
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200673#ifndef MCUBOOT_SWAP_USING_STATUS
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300674 rc = boot_erase_region(fap_scratch, 0,
675 flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300676 assert(rc == 0);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200677#else
678 rc = swap_erase_trailer_sectors(state, fap_scratch);
679 assert(rc == 0);
680#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300681 }
682 }
683
684 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
685 img_off, 0, copy_sz);
686 assert(rc == 0);
687
INFINEON\DovhalA3b578f32024-11-29 01:06:04 +0200688#if defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_SAVE_ENC_IV)
689 rc = boot_write_iv(fap_primary_slot, state->enc[image_index][1].aes_iv);
690 assert(rc == 0);
691#endif
692
Fabio Utzig12d59162019-11-28 10:01:59 -0300693 rc = boot_write_status(state, bs);
694 bs->state = BOOT_STATUS_STATE_1;
695 BOOT_STATUS_ASSERT(rc == 0);
696 }
697
698 if (bs->state == BOOT_STATUS_STATE_1) {
699 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
700 assert(rc == 0);
701
702 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
703 img_off, img_off, copy_sz);
704 assert(rc == 0);
705
706 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
707 /* If not all sectors of the slot are being swapped,
708 * guarantee here that only the primary slot will have the state.
709 */
710 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
711 assert(rc == 0);
712 }
713
INFINEON\DovhalA3b578f32024-11-29 01:06:04 +0200714#if defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_SAVE_ENC_IV)
715 rc = boot_write_iv(fap_primary_slot, state->enc[image_index][1].aes_iv);
716 assert(rc == 0);
717#endif
718
Fabio Utzig12d59162019-11-28 10:01:59 -0300719 rc = boot_write_status(state, bs);
720 bs->state = BOOT_STATUS_STATE_2;
721 BOOT_STATUS_ASSERT(rc == 0);
722 }
723
724 if (bs->state == BOOT_STATUS_STATE_2) {
725 rc = boot_erase_region(fap_primary_slot, img_off, sz);
726 assert(rc == 0);
727
728 /* NOTE: If this is the final sector, we exclude the image trailer from
729 * this copy (copy_sz was truncated earlier).
730 */
731 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
732 0, img_off, copy_sz);
733 assert(rc == 0);
734
735 if (bs->use_scratch) {
736 scratch_trailer_off = boot_status_off(fap_scratch);
737
738 /* copy current status that is being maintained in scratch */
739 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
740 scratch_trailer_off, img_off + copy_sz,
741 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
742 BOOT_STATUS_ASSERT(rc == 0);
743
Roman Okhrimenko977b3752022-03-31 14:40:48 +0300744 rc = boot_read_swap_state(fap_scratch, &swap_state);
Fabio Utzig12d59162019-11-28 10:01:59 -0300745 assert(rc == 0);
746
747 if (swap_state.image_ok == BOOT_FLAG_SET) {
748 rc = boot_write_image_ok(fap_primary_slot);
749 assert(rc == 0);
750 }
751
752 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
753 rc = boot_write_swap_info(fap_primary_slot,
754 swap_state.swap_type, image_index);
755 assert(rc == 0);
756 }
757
758 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
759 assert(rc == 0);
760
761#ifdef MCUBOOT_ENC_IMAGES
Fabio Utzig4741c452019-12-19 15:32:41 -0300762 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300763 assert(rc == 0);
764
Fabio Utzig4741c452019-12-19 15:32:41 -0300765 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300766 assert(rc == 0);
767#endif
768 rc = boot_write_magic(fap_primary_slot);
769 assert(rc == 0);
770 }
771
772 /* If we wrote a trailer to the scratch area, erase it after we persist
773 * a trailer to the primary slot. We do this to prevent mcuboot from
774 * reading a stale status from the scratch area in case of immediate
775 * reset.
776 */
777 erase_scratch = bs->use_scratch;
778 bs->use_scratch = 0;
779
INFINEON\DovhalA3b578f32024-11-29 01:06:04 +0200780#if defined(MCUBOOT_ENC_IMAGES) && defined(MCUBOOT_SAVE_ENC_IV)
781 rc = boot_write_iv(fap_primary_slot, state->enc[image_index][1].aes_iv);
782 assert(rc == 0);
783#endif
784
Fabio Utzig12d59162019-11-28 10:01:59 -0300785 rc = boot_write_status(state, bs);
786 bs->idx++;
787 bs->state = BOOT_STATUS_STATE_0;
788 BOOT_STATUS_ASSERT(rc == 0);
789
790 if (erase_scratch) {
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200791#ifndef MCUBOOT_SWAP_USING_STATUS
Fabio Utzig12d59162019-11-28 10:01:59 -0300792 rc = boot_erase_region(fap_scratch, 0, sz);
793 assert(rc == 0);
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200794#else
795 rc = swap_erase_trailer_sectors(state, fap_scratch);
796 assert(rc == 0);
797
798 rc = swap_erase_trailer_sectors(state, fap_secondary_slot); // TODO: check if needed and fix
799 assert(rc == 0);
800#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300801 }
802 }
803
804 flash_area_close(fap_primary_slot);
805 flash_area_close(fap_secondary_slot);
806 flash_area_close(fap_scratch);
807}
808
809void
810swap_run(struct boot_loader_state *state, struct boot_status *bs,
811 uint32_t copy_size)
812{
813 uint32_t sz;
814 int first_sector_idx;
815 int last_sector_idx;
816 uint32_t swap_idx;
817 int last_idx_secondary_slot;
818 uint32_t primary_slot_size;
819 uint32_t secondary_slot_size;
820 primary_slot_size = 0;
821 secondary_slot_size = 0;
822 last_sector_idx = 0;
823 last_idx_secondary_slot = 0;
824
Roman Okhrimenkodc0ca082023-06-21 20:49:51 +0300825 BOOT_LOG_INF("Starting swap using scratch algorithm.");
826
Fabio Utzig12d59162019-11-28 10:01:59 -0300827 /*
828 * Knowing the size of the largest image between both slots, here we
829 * find what is the last sector in the primary slot that needs swapping.
830 * Since we already know that both slots are compatible, the secondary
831 * slot's last sector is not really required after this check is finished.
832 */
833 while (1) {
834 if ((primary_slot_size < copy_size) ||
835 (primary_slot_size < secondary_slot_size)) {
836 primary_slot_size += boot_img_sector_size(state,
837 BOOT_PRIMARY_SLOT,
838 last_sector_idx);
839 }
840 if ((secondary_slot_size < copy_size) ||
841 (secondary_slot_size < primary_slot_size)) {
842 secondary_slot_size += boot_img_sector_size(state,
843 BOOT_SECONDARY_SLOT,
844 last_idx_secondary_slot);
845 }
846 if (primary_slot_size >= copy_size &&
847 secondary_slot_size >= copy_size &&
848 primary_slot_size == secondary_slot_size) {
849 break;
850 }
851 last_sector_idx++;
852 last_idx_secondary_slot++;
853 }
854
Roman Okhrimenko13f79ed2021-03-11 19:05:41 +0200855 bs->op = BOOT_STATUS_OP_SWAP;
856
Fabio Utzig12d59162019-11-28 10:01:59 -0300857 swap_idx = 0;
858 while (last_sector_idx >= 0) {
859 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
860 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
861 boot_swap_sectors(first_sector_idx, sz, state, bs);
862 }
863
864 last_sector_idx = first_sector_idx - 1;
865 swap_idx++;
866 }
867
868}
David Vinczee574f2d2020-07-10 11:42:03 +0200869#endif /* !MCUBOOT_OVERWRITE_ONLY */
Fabio Utzig12d59162019-11-28 10:01:59 -0300870
David Vinczee574f2d2020-07-10 11:42:03 +0200871#endif /* !MCUBOOT_SWAP_USING_MOVE */