blob: d0f822e825f6d689711007a767f6ffa2edd11115 [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
Carlos Falgueras GarcĂ­aa4b4b0f2021-06-22 10:00:22 +020031BOOT_LOG_MODULE_DECLARE(mcuboot);
Fabio Utzig12d59162019-11-28 10:01:59 -030032
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
Tamas Banfe031092020-09-10 17:32:39 +020050#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
Fabio Utzig12d59162019-11-28 10:01:59 -030051/**
52 * Reads the status of a partially-completed swap, if any. This is necessary
53 * to recover in case the boot lodaer was reset in the middle of a swap
54 * operation.
55 */
56int
57swap_read_status_bytes(const struct flash_area *fap,
58 struct boot_loader_state *state, struct boot_status *bs)
59{
60 uint32_t off;
61 uint8_t status;
62 int max_entries;
63 int found;
64 int found_idx;
65 int invalid;
66 int rc;
67 int i;
68
69 off = boot_status_off(fap);
70 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
71 if (max_entries < 0) {
72 return BOOT_EBADARGS;
73 }
74
75 found = 0;
76 found_idx = 0;
77 invalid = 0;
78 for (i = 0; i < max_entries; i++) {
Fabio Utzig4b2e55f2020-09-24 11:49:20 -030079 rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
Fabio Utzig12d59162019-11-28 10:01:59 -030080 &status, 1);
81 if (rc < 0) {
82 return BOOT_EFLASH;
83 }
84
Fabio Utzig4b2e55f2020-09-24 11:49:20 -030085 if (bootutil_buffer_is_erased(fap, &status, 1)) {
Fabio Utzig12d59162019-11-28 10:01:59 -030086 if (found && !found_idx) {
87 found_idx = i;
88 }
89 } else if (!found) {
90 found = 1;
91 } else if (found_idx) {
92 invalid = 1;
93 break;
94 }
95 }
96
97 if (invalid) {
98 /* This means there was an error writing status on the last
99 * swap. Tell user and move on to validation!
100 */
David Brown098de832019-12-10 11:58:01 -0700101#if !defined(__BOOTSIM__)
Fabio Utzig12d59162019-11-28 10:01:59 -0300102 BOOT_LOG_ERR("Detected inconsistent status!");
David Brown098de832019-12-10 11:58:01 -0700103#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300104
105#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
106 /* With validation of the primary slot disabled, there is no way
107 * to be sure the swapped primary slot is OK, so abort!
108 */
109 assert(0);
110#endif
111 }
112
113 if (found) {
114 if (!found_idx) {
115 found_idx = i;
116 }
117 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
118 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
119 }
120
121 return 0;
122}
123
124uint32_t
125boot_status_internal_off(const struct boot_status *bs, int elem_sz)
126{
127 int idx_sz;
128
129 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
130
131 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
132 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
133}
134
135/*
136 * Slots are compatible when all sectors that store up to to size of the image
137 * round up to sector size, in both slot's are able to fit in the scratch
138 * area, and have sizes that are a multiple of each other (powers of two
139 * presumably!).
140 */
141int
142boot_slots_compatible(struct boot_loader_state *state)
143{
144 size_t num_sectors_primary;
145 size_t num_sectors_secondary;
146 size_t sz0, sz1;
147 size_t primary_slot_sz, secondary_slot_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300148#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300149 size_t scratch_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300150#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300151 size_t i, j;
152 int8_t smaller;
153
154 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
155 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
156 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
157 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
158 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
159 return 0;
160 }
161
Fabio Utzig74aef312019-11-28 11:05:34 -0300162#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300163 scratch_sz = boot_scratch_area_size(state);
Fabio Utzig74aef312019-11-28 11:05:34 -0300164#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300165
166 /*
167 * The following loop scans all sectors in a linear fashion, assuring that
168 * for each possible sector in each slot, it is able to fit in the other
169 * slot's sector or sectors. Slot's should be compatible as long as any
170 * number of a slot's sectors are able to fit into another, which only
171 * excludes cases where sector sizes are not a multiple of each other.
172 */
173 i = sz0 = primary_slot_sz = 0;
174 j = sz1 = secondary_slot_sz = 0;
175 smaller = 0;
176 while (i < num_sectors_primary || j < num_sectors_secondary) {
177 if (sz0 == sz1) {
178 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
179 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
180 i++;
181 j++;
182 } else if (sz0 < sz1) {
183 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
184 /* Guarantee that multiple sectors of the secondary slot
185 * fit into the primary slot.
186 */
187 if (smaller == 2) {
188 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
189 return 0;
190 }
191 smaller = 1;
192 i++;
193 } else {
194 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
195 /* Guarantee that multiple sectors of the primary slot
196 * fit into the secondary slot.
197 */
198 if (smaller == 1) {
199 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
200 return 0;
201 }
202 smaller = 2;
203 j++;
204 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300205#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300206 if (sz0 == sz1) {
207 primary_slot_sz += sz0;
208 secondary_slot_sz += sz1;
209 /* Scratch has to fit each swap operation to the size of the larger
210 * sector among the primary slot and the secondary slot.
211 */
212 if (sz0 > scratch_sz || sz1 > scratch_sz) {
213 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
214 return 0;
215 }
216 smaller = sz0 = sz1 = 0;
217 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300218#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300219 }
220
221 if ((i != num_sectors_primary) ||
222 (j != num_sectors_secondary) ||
223 (primary_slot_sz != secondary_slot_sz)) {
224 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
225 return 0;
226 }
227
228 return 1;
229}
230
231#define BOOT_LOG_SWAP_STATE(area, state) \
232 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
233 "image_ok=0x%x", \
234 (area), \
235 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
236 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
237 "bad"), \
238 (state)->swap_type, \
239 (state)->copy_done, \
240 (state)->image_ok)
241
242struct boot_status_table {
243 uint8_t bst_magic_primary_slot;
244 uint8_t bst_magic_scratch;
245 uint8_t bst_copy_done_primary_slot;
246 uint8_t bst_status_source;
247};
248
249/**
250 * This set of tables maps swap state contents to boot status location.
251 * When searching for a match, these tables must be iterated in order.
252 */
253static const struct boot_status_table boot_status_tables[] = {
254 {
255 /* | primary slot | scratch |
256 * ----------+--------------+--------------|
257 * magic | Good | Any |
258 * copy-done | Set | N/A |
259 * ----------+--------------+--------------'
260 * source: none |
261 * ----------------------------------------'
262 */
263 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
264 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
265 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
266 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
267 },
268
269 {
270 /* | primary slot | scratch |
271 * ----------+--------------+--------------|
272 * magic | Good | Any |
273 * copy-done | Unset | N/A |
274 * ----------+--------------+--------------'
275 * source: primary slot |
276 * ----------------------------------------'
277 */
278 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
279 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
280 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
281 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
282 },
283
284 {
285 /* | primary slot | scratch |
286 * ----------+--------------+--------------|
287 * magic | Any | Good |
288 * copy-done | Any | N/A |
289 * ----------+--------------+--------------'
290 * source: scratch |
291 * ----------------------------------------'
292 */
293 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
294 .bst_magic_scratch = BOOT_MAGIC_GOOD,
295 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
296 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
297 },
298 {
299 /* | primary slot | scratch |
300 * ----------+--------------+--------------|
301 * magic | Unset | Any |
302 * copy-done | Unset | N/A |
303 * ----------+--------------+--------------|
304 * source: varies |
305 * ----------------------------------------+--------------------------+
306 * This represents one of two cases: |
307 * o No swaps ever (no status to read, so no harm in checking). |
308 * o Mid-revert; status in primary slot. |
309 * -------------------------------------------------------------------'
310 */
311 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
312 .bst_magic_scratch = BOOT_MAGIC_ANY,
313 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
314 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
315 },
316};
317
318#define BOOT_STATUS_TABLES_COUNT \
319 (sizeof boot_status_tables / sizeof boot_status_tables[0])
320
321/**
322 * Determines where in flash the most recent boot status is stored. The boot
323 * status is necessary for completing a swap that was interrupted by a boot
324 * loader reset.
325 *
326 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
327 * be read from.
328 */
329int
330swap_status_source(struct boot_loader_state *state)
331{
332 const struct boot_status_table *table;
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200333#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300334 struct boot_swap_state state_scratch;
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200335#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300336 struct boot_swap_state state_primary_slot;
337 int rc;
338 size_t i;
339 uint8_t source;
340 uint8_t image_index;
341
342#if (BOOT_IMAGE_NUMBER == 1)
343 (void)state;
344#endif
345
346 image_index = BOOT_CURR_IMG(state);
347 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
348 &state_primary_slot);
349 assert(rc == 0);
350
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200351#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300352 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
353 assert(rc == 0);
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200354#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300355
356 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200357#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300358 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200359#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300360 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
361 table = &boot_status_tables[i];
362
363 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
364 state_primary_slot.magic) &&
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200365#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300366 boot_magic_compatible_check(table->bst_magic_scratch,
367 state_scratch.magic) &&
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200368#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300369 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
370 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
371 {
372 source = table->bst_status_source;
373
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200374#if (BOOT_IMAGE_NUMBER > 1) && MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300375 /* In case of multi-image boot it can happen that if boot status
376 * info is found on scratch area then it does not belong to the
377 * currently examined image.
378 */
379 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
380 state_scratch.image_num != BOOT_CURR_IMG(state)) {
381 source = BOOT_STATUS_SOURCE_NONE;
382 }
383#endif
384
385 BOOT_LOG_INF("Boot source: %s",
386 source == BOOT_STATUS_SOURCE_NONE ? "none" :
387 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
388 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
389 "primary slot" : "BUG; can't happen");
390 return source;
391 }
392 }
393
394 BOOT_LOG_INF("Boot source: none");
395 return BOOT_STATUS_SOURCE_NONE;
396}
397
Fabio Utzig74aef312019-11-28 11:05:34 -0300398#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300399/**
400 * Calculates the number of sectors the scratch area can contain. A "last"
401 * source sector is specified because images are copied backwards in flash
402 * (final index to index number 0).
403 *
404 * @param last_sector_idx The index of the last source sector
405 * (inclusive).
406 * @param out_first_sector_idx The index of the first source sector
407 * (inclusive) gets written here.
408 *
409 * @return The number of bytes comprised by the
410 * [first-sector, last-sector] range.
411 */
412static uint32_t
413boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
414 int *out_first_sector_idx)
415{
416 size_t scratch_sz;
417 uint32_t new_sz;
418 uint32_t sz;
419 int i;
420
421 sz = 0;
422
423 scratch_sz = boot_scratch_area_size(state);
424 for (i = last_sector_idx; i >= 0; i--) {
425 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
426 /*
427 * The secondary slot is not being checked here, because
428 * `boot_slots_compatible` already provides assurance that the copy size
429 * will be compatible with the primary slot and scratch.
430 */
431 if (new_sz > scratch_sz) {
432 break;
433 }
434 sz = new_sz;
435 }
436
437 /* i currently refers to a sector that doesn't fit or it is -1 because all
438 * sectors have been processed. In both cases, exclude sector i.
439 */
440 *out_first_sector_idx = i + 1;
441 return sz;
442}
443
444/**
Thomas Altenbachf515bb12024-04-26 18:31:57 +0200445 * Finds the index of the last sector in the primary slot that needs swapping.
446 *
447 * @param state Current bootloader's state.
448 * @param copy_size Total number of bytes to swap.
449 *
450 * @return Index of the last sector in the primary slot that needs swapping.
451 */
452static int
453find_last_sector_idx(const struct boot_loader_state *state, uint32_t copy_size)
454{
455 int last_sector_idx;
456 uint32_t primary_slot_size;
457 uint32_t secondary_slot_size;
458
459 primary_slot_size = 0;
460 secondary_slot_size = 0;
461 last_sector_idx = 0;
462
463 /*
464 * Knowing the size of the largest image between both slots, here we
465 * find what is the last sector in the primary slot that needs swapping.
466 * Since we already know that both slots are compatible, the secondary
467 * slot's last sector is not really required after this check is finished.
468 */
469 while (1) {
470 if ((primary_slot_size < copy_size) ||
471 (primary_slot_size < secondary_slot_size)) {
472 primary_slot_size += boot_img_sector_size(state,
473 BOOT_PRIMARY_SLOT,
474 last_sector_idx);
475 }
476 if ((secondary_slot_size < copy_size) ||
477 (secondary_slot_size < primary_slot_size)) {
478 secondary_slot_size += boot_img_sector_size(state,
479 BOOT_SECONDARY_SLOT,
480 last_sector_idx);
481 }
482 if (primary_slot_size >= copy_size &&
483 secondary_slot_size >= copy_size &&
484 primary_slot_size == secondary_slot_size) {
485 break;
486 }
487 last_sector_idx++;
488 }
489
490 return last_sector_idx;
491}
492
493/**
494 * Finds the number of swap operations that have to be performed to swap the two images.
495 *
496 * @param state Current bootloader's state.
497 * @param copy_size Total number of bytes to swap.
498 *
499 * @return The number of swap operations that have to be performed.
500*/
501static uint32_t
502find_swap_count(const struct boot_loader_state *state, uint32_t copy_size)
503{
504 int first_sector_idx;
505 int last_sector_idx;
506 uint32_t swap_count;
507
508 last_sector_idx = find_last_sector_idx(state, copy_size);
509
510 swap_count = 0;
511
512 while (last_sector_idx >= 0) {
513 boot_copy_sz(state, last_sector_idx, &first_sector_idx);
514
515 last_sector_idx = first_sector_idx - 1;
516 swap_count++;
517 }
518
519 return swap_count;
520}
521
522/**
Fabio Utzig12d59162019-11-28 10:01:59 -0300523 * Swaps the contents of two flash regions within the two image slots.
524 *
525 * @param idx The index of the first sector in the range of
526 * sectors being swapped.
527 * @param sz The number of bytes to swap.
528 * @param bs The current boot status. This struct gets
529 * updated according to the outcome.
530 *
531 * @return 0 on success; nonzero on failure.
532 */
533static void
534boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
535 struct boot_status *bs)
536{
537 const struct flash_area *fap_primary_slot;
538 const struct flash_area *fap_secondary_slot;
539 const struct flash_area *fap_scratch;
540 uint32_t copy_sz;
541 uint32_t trailer_sz;
Gustavo Henrique Niheiffe4ec92021-11-30 17:27:48 -0300542 uint32_t sector_sz;
Fabio Utzig12d59162019-11-28 10:01:59 -0300543 uint32_t img_off;
544 uint32_t scratch_trailer_off;
545 struct boot_swap_state swap_state;
546 size_t last_sector;
547 bool erase_scratch;
548 uint8_t image_index;
549 int rc;
550
551 /* Calculate offset from start of image area. */
552 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
553
554 copy_sz = sz;
555 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
556
557 /* sz in this function is always sized on a multiple of the sector size.
558 * The check against the start offset of the last sector
559 * is to determine if we're swapping the last sector. The last sector
560 * needs special handling because it's where the trailer lives. If we're
561 * copying it, we need to use scratch to write the trailer temporarily.
562 *
563 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
564 * controls if special handling is needed (swapping last sector).
565 */
566 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
Gustavo Henrique Niheiffe4ec92021-11-30 17:27:48 -0300567 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, last_sector);
568
569 if (sector_sz < trailer_sz) {
570 uint32_t trailer_sector_sz = sector_sz;
571
572 while (trailer_sector_sz < trailer_sz) {
573 /* Consider that the image trailer may span across sectors of
574 * different sizes.
575 */
576 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, --last_sector);
577
578 trailer_sector_sz += sector_sz;
579 }
580 }
581
Fabio Utzig12d59162019-11-28 10:01:59 -0300582 if ((img_off + sz) >
583 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
584 copy_sz -= trailer_sz;
585 }
586
587 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
588
589 image_index = BOOT_CURR_IMG(state);
590
591 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
592 &fap_primary_slot);
593 assert (rc == 0);
594
595 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
596 &fap_secondary_slot);
597 assert (rc == 0);
598
599 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
600 assert (rc == 0);
601
602 if (bs->state == BOOT_STATUS_STATE_0) {
603 BOOT_LOG_DBG("erasing scratch area");
Dominik Ermel260ae092021-04-23 05:38:45 +0000604 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300605 assert(rc == 0);
606
607 if (bs->idx == BOOT_STATUS_IDX_0) {
608 /* Write a trailer to the scratch area, even if we don't need the
609 * scratch area for status. We need a temporary place to store the
610 * `swap-type` while we erase the primary trailer.
David Vinczee574f2d2020-07-10 11:42:03 +0200611 */
Fabio Utzig12d59162019-11-28 10:01:59 -0300612 rc = swap_status_init(state, fap_scratch, bs);
613 assert(rc == 0);
614
615 if (!bs->use_scratch) {
616 /* Prepare the primary status area... here it is known that the
617 * last sector is not being used by the image data so it's safe
618 * to erase.
619 */
620 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
621 assert(rc == 0);
622
623 rc = swap_status_init(state, fap_primary_slot, bs);
624 assert(rc == 0);
625
626 /* Erase the temporary trailer from the scratch area. */
Dominik Ermel260ae092021-04-23 05:38:45 +0000627 rc = boot_erase_region(fap_scratch, 0,
628 flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300629 assert(rc == 0);
630 }
631 }
632
633 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
634 img_off, 0, copy_sz);
635 assert(rc == 0);
636
637 rc = boot_write_status(state, bs);
638 bs->state = BOOT_STATUS_STATE_1;
639 BOOT_STATUS_ASSERT(rc == 0);
640 }
641
642 if (bs->state == BOOT_STATUS_STATE_1) {
643 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
644 assert(rc == 0);
645
646 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
647 img_off, img_off, copy_sz);
648 assert(rc == 0);
649
650 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
651 /* If not all sectors of the slot are being swapped,
652 * guarantee here that only the primary slot will have the state.
653 */
654 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
655 assert(rc == 0);
656 }
657
658 rc = boot_write_status(state, bs);
659 bs->state = BOOT_STATUS_STATE_2;
660 BOOT_STATUS_ASSERT(rc == 0);
661 }
662
663 if (bs->state == BOOT_STATUS_STATE_2) {
664 rc = boot_erase_region(fap_primary_slot, img_off, sz);
665 assert(rc == 0);
666
667 /* NOTE: If this is the final sector, we exclude the image trailer from
668 * this copy (copy_sz was truncated earlier).
669 */
670 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
671 0, img_off, copy_sz);
672 assert(rc == 0);
673
674 if (bs->use_scratch) {
675 scratch_trailer_off = boot_status_off(fap_scratch);
676
677 /* copy current status that is being maintained in scratch */
678 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
679 scratch_trailer_off, img_off + copy_sz,
680 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
681 BOOT_STATUS_ASSERT(rc == 0);
682
Dominik Ermel29aed1d2021-05-25 11:59:19 +0000683 rc = boot_read_swap_state(fap_scratch, &swap_state);
Fabio Utzig12d59162019-11-28 10:01:59 -0300684 assert(rc == 0);
685
686 if (swap_state.image_ok == BOOT_FLAG_SET) {
687 rc = boot_write_image_ok(fap_primary_slot);
688 assert(rc == 0);
689 }
690
691 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
692 rc = boot_write_swap_info(fap_primary_slot,
693 swap_state.swap_type, image_index);
694 assert(rc == 0);
695 }
696
697 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
698 assert(rc == 0);
699
700#ifdef MCUBOOT_ENC_IMAGES
Fabio Utzig4741c452019-12-19 15:32:41 -0300701 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300702 assert(rc == 0);
703
Fabio Utzig4741c452019-12-19 15:32:41 -0300704 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300705 assert(rc == 0);
706#endif
707 rc = boot_write_magic(fap_primary_slot);
708 assert(rc == 0);
709 }
710
711 /* If we wrote a trailer to the scratch area, erase it after we persist
712 * a trailer to the primary slot. We do this to prevent mcuboot from
713 * reading a stale status from the scratch area in case of immediate
714 * reset.
715 */
716 erase_scratch = bs->use_scratch;
717 bs->use_scratch = 0;
718
719 rc = boot_write_status(state, bs);
720 bs->idx++;
721 bs->state = BOOT_STATUS_STATE_0;
722 BOOT_STATUS_ASSERT(rc == 0);
723
724 if (erase_scratch) {
Stephane Le Roy259d9892021-08-26 10:51:24 +0200725 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300726 assert(rc == 0);
727 }
728 }
729
730 flash_area_close(fap_primary_slot);
731 flash_area_close(fap_secondary_slot);
732 flash_area_close(fap_scratch);
733}
734
735void
736swap_run(struct boot_loader_state *state, struct boot_status *bs,
737 uint32_t copy_size)
738{
739 uint32_t sz;
740 int first_sector_idx;
741 int last_sector_idx;
742 uint32_t swap_idx;
Fabio Utzig12d59162019-11-28 10:01:59 -0300743
Andrzej Puzdrowskif9dbf682021-12-23 12:32:28 +0100744 BOOT_LOG_INF("Starting swap using scratch algorithm.");
745
Thomas Altenbachf515bb12024-04-26 18:31:57 +0200746 last_sector_idx = find_last_sector_idx(state, copy_size);
Fabio Utzig12d59162019-11-28 10:01:59 -0300747
748 swap_idx = 0;
749 while (last_sector_idx >= 0) {
750 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
751 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
752 boot_swap_sectors(first_sector_idx, sz, state, bs);
753 }
754
755 last_sector_idx = first_sector_idx - 1;
756 swap_idx++;
757 }
758
759}
David Vinczee574f2d2020-07-10 11:42:03 +0200760#endif /* !MCUBOOT_OVERWRITE_ONLY */
Fabio Utzig12d59162019-11-28 10:01:59 -0300761
Jamie McCrae3016d002023-03-14 12:35:51 +0000762int app_max_size(struct boot_loader_state *state)
763{
764 size_t num_sectors_primary;
765 size_t num_sectors_secondary;
766 size_t sz0, sz1;
767 size_t primary_slot_sz, secondary_slot_sz;
768#ifndef MCUBOOT_OVERWRITE_ONLY
769 size_t scratch_sz;
770#endif
771 size_t i, j;
772 int8_t smaller;
773
774 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
775 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
776
777#ifndef MCUBOOT_OVERWRITE_ONLY
778 scratch_sz = boot_scratch_area_size(state);
779#endif
780
781 /*
782 * The following loop scans all sectors in a linear fashion, assuring that
783 * for each possible sector in each slot, it is able to fit in the other
784 * slot's sector or sectors. Slot's should be compatible as long as any
785 * number of a slot's sectors are able to fit into another, which only
786 * excludes cases where sector sizes are not a multiple of each other.
787 */
788 i = sz0 = primary_slot_sz = 0;
789 j = sz1 = secondary_slot_sz = 0;
790 smaller = 0;
791 while (i < num_sectors_primary || j < num_sectors_secondary) {
792 if (sz0 == sz1) {
793 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
794 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
795 i++;
796 j++;
797 } else if (sz0 < sz1) {
798 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
799 /* Guarantee that multiple sectors of the secondary slot
800 * fit into the primary slot.
801 */
802 if (smaller == 2) {
803 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
804 return 0;
805 }
806 smaller = 1;
807 i++;
808 } else {
809 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
810 /* Guarantee that multiple sectors of the primary slot
811 * fit into the secondary slot.
812 */
813 if (smaller == 1) {
814 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
815 return 0;
816 }
817 smaller = 2;
818 j++;
819 }
820#ifndef MCUBOOT_OVERWRITE_ONLY
821 if (sz0 == sz1) {
822 primary_slot_sz += sz0;
823 secondary_slot_sz += sz1;
824 /* Scratch has to fit each swap operation to the size of the larger
825 * sector among the primary slot and the secondary slot.
826 */
827 if (sz0 > scratch_sz || sz1 > scratch_sz) {
828 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
829 return 0;
830 }
831 smaller = sz0 = sz1 = 0;
832 }
833#endif
834 }
835
836#ifdef MCUBOOT_OVERWRITE_ONLY
837 return (sz1 < sz0 ? sz1 : sz0);
838#else
839 return (secondary_slot_sz < primary_slot_sz ? secondary_slot_sz : primary_slot_sz);
840#endif
841}
842#else
843int app_max_size(struct boot_loader_state *state)
844{
845 const struct flash_area *fap;
846 int fa_id;
847 int rc;
848 uint32_t active_slot;
849 int primary_sz, secondary_sz;
850
851 active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
852
853 fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
854 rc = flash_area_open(fa_id, &fap);
855 assert(rc == 0);
856 primary_sz = flash_area_get_size(fap);
857 flash_area_close(fap);
858
859 if (active_slot == BOOT_PRIMARY_SLOT) {
860 active_slot = BOOT_SECONDARY_SLOT;
861 } else {
862 active_slot = BOOT_PRIMARY_SLOT;
863 }
864
865 fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
866 rc = flash_area_open(fa_id, &fap);
867 assert(rc == 0);
868 secondary_sz = flash_area_get_size(fap);
869 flash_area_close(fap);
870
871 return (secondary_sz < primary_sz ? secondary_sz : primary_sz);
872}
873
Tamas Banfe031092020-09-10 17:32:39 +0200874#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
David Vinczee574f2d2020-07-10 11:42:03 +0200875
Thomas Altenbachf515bb12024-04-26 18:31:57 +0200876int
877boot_read_image_header(struct boot_loader_state *state, int slot,
878 struct image_header *out_hdr, struct boot_status *bs)
879{
880 const struct flash_area *fap;
881#ifdef MCUBOOT_SWAP_USING_SCRATCH
882 uint32_t swap_count;
883 uint32_t swap_size;
884#endif
885 int area_id;
886 int hdr_slot;
887 int rc = 0;
888
889#ifndef MCUBOOT_SWAP_USING_SCRATCH
890 (void)bs;
891#endif
892
893#if (BOOT_IMAGE_NUMBER == 1)
894 (void)state;
895#endif
896
897 hdr_slot = slot;
898
899#ifdef MCUBOOT_SWAP_USING_SCRATCH
900 /* If the slots are being swapped, the headers might have been moved to scratch area or to the
901 * other slot depending on the progress of the swap process.
902 */
903 if (bs && !boot_status_is_reset(bs)) {
904 rc = boot_find_status(BOOT_CURR_IMG(state), &fap);
905
906 if (rc != 0) {
907 rc = BOOT_EFLASH;
908 goto done;
909 }
910
911 rc = boot_read_swap_size(fap, &swap_size);
912 flash_area_close(fap);
913
914 if (rc != 0) {
915 rc = BOOT_EFLASH;
916 goto done;
917 }
918
919 swap_count = find_swap_count(state, swap_size);
920
921 if (bs->idx - BOOT_STATUS_IDX_0 >= swap_count) {
922 /* If all segments have been swapped, the header is located in the other slot */
923 hdr_slot = (slot == BOOT_PRIMARY_SLOT) ? BOOT_SECONDARY_SLOT : BOOT_PRIMARY_SLOT;
924 } else if (bs->idx - BOOT_STATUS_IDX_0 == swap_count - 1) {
925 /* If the last swap operation is in progress, the headers are currently being swapped
926 * since the first segment of each slot is the last to be processed.
927 */
928
929 if (slot == BOOT_SECONDARY_SLOT && bs->state >= BOOT_STATUS_STATE_1) {
930 /* After BOOT_STATUS_STATE_1, the secondary image's header has been moved to the
931 * scratch area.
932 */
933 hdr_slot = BOOT_NUM_SLOTS;
934 } else if (slot == BOOT_PRIMARY_SLOT && bs->state >= BOOT_STATUS_STATE_2) {
935 /* After BOOT_STATUS_STATE_2, the primary image's header has been moved to the
936 * secondary slot.
937 */
938 hdr_slot = BOOT_SECONDARY_SLOT;
939 }
940 }
941 }
942
943 if (hdr_slot == BOOT_NUM_SLOTS) {
944 area_id = FLASH_AREA_IMAGE_SCRATCH;
945 } else {
946 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), hdr_slot);
947 }
948#else
949 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), hdr_slot);
950#endif
951
952 rc = flash_area_open(area_id, &fap);
953 if (rc == 0) {
954 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
955 flash_area_close(fap);
956 }
957
958 if (rc != 0) {
959 rc = BOOT_EFLASH;
960 goto done;
961 }
962
963done:
964 return rc;
965}
966
David Vinczee574f2d2020-07-10 11:42:03 +0200967#endif /* !MCUBOOT_SWAP_USING_MOVE */