blob: 66cbdce5ff10bdb3dbabdd427e880fcf177c1f09 [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
50int
51boot_read_image_header(struct boot_loader_state *state, int slot,
52 struct image_header *out_hdr, struct boot_status *bs)
53{
54 const struct flash_area *fap;
55 int area_id;
Dominik Ermel6138b4f2021-10-19 11:46:36 +000056 int rc = 0;
Fabio Utzig12d59162019-11-28 10:01:59 -030057
58 (void)bs;
59
60#if (BOOT_IMAGE_NUMBER == 1)
61 (void)state;
62#endif
63
64 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
Dominik Ermel6138b4f2021-10-19 11:46:36 +000065
Fabio Utzig12d59162019-11-28 10:01:59 -030066 rc = flash_area_open(area_id, &fap);
Dominik Ermel6138b4f2021-10-19 11:46:36 +000067 if (rc == 0) {
68 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
69 flash_area_close(fap);
Fabio Utzig12d59162019-11-28 10:01:59 -030070 }
71
Fabio Utzig12d59162019-11-28 10:01:59 -030072 if (rc != 0) {
73 rc = BOOT_EFLASH;
Fabio Utzig12d59162019-11-28 10:01:59 -030074 }
75
Fabio Utzig12d59162019-11-28 10:01:59 -030076 return rc;
77}
78
Tamas Banfe031092020-09-10 17:32:39 +020079#if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)
Fabio Utzig12d59162019-11-28 10:01:59 -030080/**
81 * Reads the status of a partially-completed swap, if any. This is necessary
82 * to recover in case the boot lodaer was reset in the middle of a swap
83 * operation.
84 */
85int
86swap_read_status_bytes(const struct flash_area *fap,
87 struct boot_loader_state *state, struct boot_status *bs)
88{
89 uint32_t off;
90 uint8_t status;
91 int max_entries;
92 int found;
93 int found_idx;
94 int invalid;
95 int rc;
96 int i;
97
98 off = boot_status_off(fap);
99 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
100 if (max_entries < 0) {
101 return BOOT_EBADARGS;
102 }
103
104 found = 0;
105 found_idx = 0;
106 invalid = 0;
107 for (i = 0; i < max_entries; i++) {
Fabio Utzig4b2e55f2020-09-24 11:49:20 -0300108 rc = flash_area_read(fap, off + i * BOOT_WRITE_SZ(state),
Fabio Utzig12d59162019-11-28 10:01:59 -0300109 &status, 1);
110 if (rc < 0) {
111 return BOOT_EFLASH;
112 }
113
Fabio Utzig4b2e55f2020-09-24 11:49:20 -0300114 if (bootutil_buffer_is_erased(fap, &status, 1)) {
Fabio Utzig12d59162019-11-28 10:01:59 -0300115 if (found && !found_idx) {
116 found_idx = i;
117 }
118 } else if (!found) {
119 found = 1;
120 } else if (found_idx) {
121 invalid = 1;
122 break;
123 }
124 }
125
126 if (invalid) {
127 /* This means there was an error writing status on the last
128 * swap. Tell user and move on to validation!
129 */
David Brown098de832019-12-10 11:58:01 -0700130#if !defined(__BOOTSIM__)
Fabio Utzig12d59162019-11-28 10:01:59 -0300131 BOOT_LOG_ERR("Detected inconsistent status!");
David Brown098de832019-12-10 11:58:01 -0700132#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300133
134#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
135 /* With validation of the primary slot disabled, there is no way
136 * to be sure the swapped primary slot is OK, so abort!
137 */
138 assert(0);
139#endif
140 }
141
142 if (found) {
143 if (!found_idx) {
144 found_idx = i;
145 }
146 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
147 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
148 }
149
150 return 0;
151}
152
153uint32_t
154boot_status_internal_off(const struct boot_status *bs, int elem_sz)
155{
156 int idx_sz;
157
158 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
159
160 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
161 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
162}
163
164/*
165 * Slots are compatible when all sectors that store up to to size of the image
166 * round up to sector size, in both slot's are able to fit in the scratch
167 * area, and have sizes that are a multiple of each other (powers of two
168 * presumably!).
169 */
170int
171boot_slots_compatible(struct boot_loader_state *state)
172{
173 size_t num_sectors_primary;
174 size_t num_sectors_secondary;
175 size_t sz0, sz1;
176 size_t primary_slot_sz, secondary_slot_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300177#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300178 size_t scratch_sz;
Fabio Utzig74aef312019-11-28 11:05:34 -0300179#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300180 size_t i, j;
181 int8_t smaller;
182
183 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
184 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
185 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
186 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
187 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
188 return 0;
189 }
190
Fabio Utzig74aef312019-11-28 11:05:34 -0300191#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300192 scratch_sz = boot_scratch_area_size(state);
Fabio Utzig74aef312019-11-28 11:05:34 -0300193#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300194
195 /*
196 * The following loop scans all sectors in a linear fashion, assuring that
197 * for each possible sector in each slot, it is able to fit in the other
198 * slot's sector or sectors. Slot's should be compatible as long as any
199 * number of a slot's sectors are able to fit into another, which only
200 * excludes cases where sector sizes are not a multiple of each other.
201 */
202 i = sz0 = primary_slot_sz = 0;
203 j = sz1 = secondary_slot_sz = 0;
204 smaller = 0;
205 while (i < num_sectors_primary || j < num_sectors_secondary) {
206 if (sz0 == sz1) {
207 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
208 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
209 i++;
210 j++;
211 } else if (sz0 < sz1) {
212 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
213 /* Guarantee that multiple sectors of the secondary slot
214 * fit into the primary slot.
215 */
216 if (smaller == 2) {
217 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
218 return 0;
219 }
220 smaller = 1;
221 i++;
222 } else {
223 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
224 /* Guarantee that multiple sectors of the primary slot
225 * fit into the secondary slot.
226 */
227 if (smaller == 1) {
228 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
229 return 0;
230 }
231 smaller = 2;
232 j++;
233 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300234#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300235 if (sz0 == sz1) {
236 primary_slot_sz += sz0;
237 secondary_slot_sz += sz1;
238 /* Scratch has to fit each swap operation to the size of the larger
239 * sector among the primary slot and the secondary slot.
240 */
241 if (sz0 > scratch_sz || sz1 > scratch_sz) {
242 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
243 return 0;
244 }
245 smaller = sz0 = sz1 = 0;
246 }
Fabio Utzig74aef312019-11-28 11:05:34 -0300247#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300248 }
249
250 if ((i != num_sectors_primary) ||
251 (j != num_sectors_secondary) ||
252 (primary_slot_sz != secondary_slot_sz)) {
253 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
254 return 0;
255 }
256
257 return 1;
258}
259
260#define BOOT_LOG_SWAP_STATE(area, state) \
261 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
262 "image_ok=0x%x", \
263 (area), \
264 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
265 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
266 "bad"), \
267 (state)->swap_type, \
268 (state)->copy_done, \
269 (state)->image_ok)
270
271struct boot_status_table {
272 uint8_t bst_magic_primary_slot;
273 uint8_t bst_magic_scratch;
274 uint8_t bst_copy_done_primary_slot;
275 uint8_t bst_status_source;
276};
277
278/**
279 * This set of tables maps swap state contents to boot status location.
280 * When searching for a match, these tables must be iterated in order.
281 */
282static const struct boot_status_table boot_status_tables[] = {
283 {
284 /* | primary slot | scratch |
285 * ----------+--------------+--------------|
286 * magic | Good | Any |
287 * copy-done | Set | N/A |
288 * ----------+--------------+--------------'
289 * source: none |
290 * ----------------------------------------'
291 */
292 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
293 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
294 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
295 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
296 },
297
298 {
299 /* | primary slot | scratch |
300 * ----------+--------------+--------------|
301 * magic | Good | Any |
302 * copy-done | Unset | N/A |
303 * ----------+--------------+--------------'
304 * source: primary slot |
305 * ----------------------------------------'
306 */
307 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
308 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
309 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
310 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
311 },
312
313 {
314 /* | primary slot | scratch |
315 * ----------+--------------+--------------|
316 * magic | Any | Good |
317 * copy-done | Any | N/A |
318 * ----------+--------------+--------------'
319 * source: scratch |
320 * ----------------------------------------'
321 */
322 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
323 .bst_magic_scratch = BOOT_MAGIC_GOOD,
324 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
325 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
326 },
327 {
328 /* | primary slot | scratch |
329 * ----------+--------------+--------------|
330 * magic | Unset | Any |
331 * copy-done | Unset | N/A |
332 * ----------+--------------+--------------|
333 * source: varies |
334 * ----------------------------------------+--------------------------+
335 * This represents one of two cases: |
336 * o No swaps ever (no status to read, so no harm in checking). |
337 * o Mid-revert; status in primary slot. |
338 * -------------------------------------------------------------------'
339 */
340 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
341 .bst_magic_scratch = BOOT_MAGIC_ANY,
342 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
343 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
344 },
345};
346
347#define BOOT_STATUS_TABLES_COUNT \
348 (sizeof boot_status_tables / sizeof boot_status_tables[0])
349
350/**
351 * Determines where in flash the most recent boot status is stored. The boot
352 * status is necessary for completing a swap that was interrupted by a boot
353 * loader reset.
354 *
355 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
356 * be read from.
357 */
358int
359swap_status_source(struct boot_loader_state *state)
360{
361 const struct boot_status_table *table;
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200362#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300363 struct boot_swap_state state_scratch;
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200364#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300365 struct boot_swap_state state_primary_slot;
366 int rc;
367 size_t i;
368 uint8_t source;
369 uint8_t image_index;
370
371#if (BOOT_IMAGE_NUMBER == 1)
372 (void)state;
373#endif
374
375 image_index = BOOT_CURR_IMG(state);
376 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
377 &state_primary_slot);
378 assert(rc == 0);
379
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200380#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300381 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
382 assert(rc == 0);
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200383#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300384
385 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200386#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300387 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200388#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300389 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
390 table = &boot_status_tables[i];
391
392 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
393 state_primary_slot.magic) &&
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200394#if MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300395 boot_magic_compatible_check(table->bst_magic_scratch,
396 state_scratch.magic) &&
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200397#endif
Fabio Utzig12d59162019-11-28 10:01:59 -0300398 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
399 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
400 {
401 source = table->bst_status_source;
402
Andrzej Puzdrowski414f9152021-05-13 11:22:04 +0200403#if (BOOT_IMAGE_NUMBER > 1) && MCUBOOT_SWAP_USING_SCRATCH
Fabio Utzig12d59162019-11-28 10:01:59 -0300404 /* In case of multi-image boot it can happen that if boot status
405 * info is found on scratch area then it does not belong to the
406 * currently examined image.
407 */
408 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
409 state_scratch.image_num != BOOT_CURR_IMG(state)) {
410 source = BOOT_STATUS_SOURCE_NONE;
411 }
412#endif
413
414 BOOT_LOG_INF("Boot source: %s",
415 source == BOOT_STATUS_SOURCE_NONE ? "none" :
416 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
417 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
418 "primary slot" : "BUG; can't happen");
419 return source;
420 }
421 }
422
423 BOOT_LOG_INF("Boot source: none");
424 return BOOT_STATUS_SOURCE_NONE;
425}
426
Fabio Utzig74aef312019-11-28 11:05:34 -0300427#ifndef MCUBOOT_OVERWRITE_ONLY
Fabio Utzig12d59162019-11-28 10:01:59 -0300428/**
429 * Calculates the number of sectors the scratch area can contain. A "last"
430 * source sector is specified because images are copied backwards in flash
431 * (final index to index number 0).
432 *
433 * @param last_sector_idx The index of the last source sector
434 * (inclusive).
435 * @param out_first_sector_idx The index of the first source sector
436 * (inclusive) gets written here.
437 *
438 * @return The number of bytes comprised by the
439 * [first-sector, last-sector] range.
440 */
441static uint32_t
442boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
443 int *out_first_sector_idx)
444{
445 size_t scratch_sz;
446 uint32_t new_sz;
447 uint32_t sz;
448 int i;
449
450 sz = 0;
451
452 scratch_sz = boot_scratch_area_size(state);
453 for (i = last_sector_idx; i >= 0; i--) {
454 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
455 /*
456 * The secondary slot is not being checked here, because
457 * `boot_slots_compatible` already provides assurance that the copy size
458 * will be compatible with the primary slot and scratch.
459 */
460 if (new_sz > scratch_sz) {
461 break;
462 }
463 sz = new_sz;
464 }
465
466 /* i currently refers to a sector that doesn't fit or it is -1 because all
467 * sectors have been processed. In both cases, exclude sector i.
468 */
469 *out_first_sector_idx = i + 1;
470 return sz;
471}
472
473/**
474 * Swaps the contents of two flash regions within the two image slots.
475 *
476 * @param idx The index of the first sector in the range of
477 * sectors being swapped.
478 * @param sz The number of bytes to swap.
479 * @param bs The current boot status. This struct gets
480 * updated according to the outcome.
481 *
482 * @return 0 on success; nonzero on failure.
483 */
484static void
485boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
486 struct boot_status *bs)
487{
488 const struct flash_area *fap_primary_slot;
489 const struct flash_area *fap_secondary_slot;
490 const struct flash_area *fap_scratch;
491 uint32_t copy_sz;
492 uint32_t trailer_sz;
Gustavo Henrique Niheiffe4ec92021-11-30 17:27:48 -0300493 uint32_t sector_sz;
Fabio Utzig12d59162019-11-28 10:01:59 -0300494 uint32_t img_off;
495 uint32_t scratch_trailer_off;
496 struct boot_swap_state swap_state;
497 size_t last_sector;
498 bool erase_scratch;
499 uint8_t image_index;
500 int rc;
501
502 /* Calculate offset from start of image area. */
503 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
504
505 copy_sz = sz;
506 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
507
508 /* sz in this function is always sized on a multiple of the sector size.
509 * The check against the start offset of the last sector
510 * is to determine if we're swapping the last sector. The last sector
511 * needs special handling because it's where the trailer lives. If we're
512 * copying it, we need to use scratch to write the trailer temporarily.
513 *
514 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
515 * controls if special handling is needed (swapping last sector).
516 */
517 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
Gustavo Henrique Niheiffe4ec92021-11-30 17:27:48 -0300518 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, last_sector);
519
520 if (sector_sz < trailer_sz) {
521 uint32_t trailer_sector_sz = sector_sz;
522
523 while (trailer_sector_sz < trailer_sz) {
524 /* Consider that the image trailer may span across sectors of
525 * different sizes.
526 */
527 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, --last_sector);
528
529 trailer_sector_sz += sector_sz;
530 }
531 }
532
Fabio Utzig12d59162019-11-28 10:01:59 -0300533 if ((img_off + sz) >
534 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
535 copy_sz -= trailer_sz;
536 }
537
538 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
539
540 image_index = BOOT_CURR_IMG(state);
541
542 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
543 &fap_primary_slot);
544 assert (rc == 0);
545
546 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
547 &fap_secondary_slot);
548 assert (rc == 0);
549
550 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
551 assert (rc == 0);
552
553 if (bs->state == BOOT_STATUS_STATE_0) {
554 BOOT_LOG_DBG("erasing scratch area");
Dominik Ermel260ae092021-04-23 05:38:45 +0000555 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300556 assert(rc == 0);
557
558 if (bs->idx == BOOT_STATUS_IDX_0) {
559 /* Write a trailer to the scratch area, even if we don't need the
560 * scratch area for status. We need a temporary place to store the
561 * `swap-type` while we erase the primary trailer.
David Vinczee574f2d2020-07-10 11:42:03 +0200562 */
Fabio Utzig12d59162019-11-28 10:01:59 -0300563 rc = swap_status_init(state, fap_scratch, bs);
564 assert(rc == 0);
565
566 if (!bs->use_scratch) {
567 /* Prepare the primary status area... here it is known that the
568 * last sector is not being used by the image data so it's safe
569 * to erase.
570 */
571 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
572 assert(rc == 0);
573
574 rc = swap_status_init(state, fap_primary_slot, bs);
575 assert(rc == 0);
576
577 /* Erase the temporary trailer from the scratch area. */
Dominik Ermel260ae092021-04-23 05:38:45 +0000578 rc = boot_erase_region(fap_scratch, 0,
579 flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300580 assert(rc == 0);
581 }
582 }
583
584 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
585 img_off, 0, copy_sz);
586 assert(rc == 0);
587
588 rc = boot_write_status(state, bs);
589 bs->state = BOOT_STATUS_STATE_1;
590 BOOT_STATUS_ASSERT(rc == 0);
591 }
592
593 if (bs->state == BOOT_STATUS_STATE_1) {
594 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
595 assert(rc == 0);
596
597 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
598 img_off, img_off, copy_sz);
599 assert(rc == 0);
600
601 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
602 /* If not all sectors of the slot are being swapped,
603 * guarantee here that only the primary slot will have the state.
604 */
605 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
606 assert(rc == 0);
607 }
608
609 rc = boot_write_status(state, bs);
610 bs->state = BOOT_STATUS_STATE_2;
611 BOOT_STATUS_ASSERT(rc == 0);
612 }
613
614 if (bs->state == BOOT_STATUS_STATE_2) {
615 rc = boot_erase_region(fap_primary_slot, img_off, sz);
616 assert(rc == 0);
617
618 /* NOTE: If this is the final sector, we exclude the image trailer from
619 * this copy (copy_sz was truncated earlier).
620 */
621 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
622 0, img_off, copy_sz);
623 assert(rc == 0);
624
625 if (bs->use_scratch) {
626 scratch_trailer_off = boot_status_off(fap_scratch);
627
628 /* copy current status that is being maintained in scratch */
629 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
630 scratch_trailer_off, img_off + copy_sz,
631 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
632 BOOT_STATUS_ASSERT(rc == 0);
633
Dominik Ermel29aed1d2021-05-25 11:59:19 +0000634 rc = boot_read_swap_state(fap_scratch, &swap_state);
Fabio Utzig12d59162019-11-28 10:01:59 -0300635 assert(rc == 0);
636
637 if (swap_state.image_ok == BOOT_FLAG_SET) {
638 rc = boot_write_image_ok(fap_primary_slot);
639 assert(rc == 0);
640 }
641
642 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
643 rc = boot_write_swap_info(fap_primary_slot,
644 swap_state.swap_type, image_index);
645 assert(rc == 0);
646 }
647
648 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
649 assert(rc == 0);
650
651#ifdef MCUBOOT_ENC_IMAGES
Fabio Utzig4741c452019-12-19 15:32:41 -0300652 rc = boot_write_enc_key(fap_primary_slot, 0, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300653 assert(rc == 0);
654
Fabio Utzig4741c452019-12-19 15:32:41 -0300655 rc = boot_write_enc_key(fap_primary_slot, 1, bs);
Fabio Utzig12d59162019-11-28 10:01:59 -0300656 assert(rc == 0);
657#endif
658 rc = boot_write_magic(fap_primary_slot);
659 assert(rc == 0);
660 }
661
662 /* If we wrote a trailer to the scratch area, erase it after we persist
663 * a trailer to the primary slot. We do this to prevent mcuboot from
664 * reading a stale status from the scratch area in case of immediate
665 * reset.
666 */
667 erase_scratch = bs->use_scratch;
668 bs->use_scratch = 0;
669
670 rc = boot_write_status(state, bs);
671 bs->idx++;
672 bs->state = BOOT_STATUS_STATE_0;
673 BOOT_STATUS_ASSERT(rc == 0);
674
675 if (erase_scratch) {
Stephane Le Roy259d9892021-08-26 10:51:24 +0200676 rc = boot_erase_region(fap_scratch, 0, flash_area_get_size(fap_scratch));
Fabio Utzig12d59162019-11-28 10:01:59 -0300677 assert(rc == 0);
678 }
679 }
680
681 flash_area_close(fap_primary_slot);
682 flash_area_close(fap_secondary_slot);
683 flash_area_close(fap_scratch);
684}
685
686void
687swap_run(struct boot_loader_state *state, struct boot_status *bs,
688 uint32_t copy_size)
689{
690 uint32_t sz;
691 int first_sector_idx;
692 int last_sector_idx;
693 uint32_t swap_idx;
694 int last_idx_secondary_slot;
695 uint32_t primary_slot_size;
696 uint32_t secondary_slot_size;
697 primary_slot_size = 0;
698 secondary_slot_size = 0;
699 last_sector_idx = 0;
700 last_idx_secondary_slot = 0;
701
Andrzej Puzdrowskif9dbf682021-12-23 12:32:28 +0100702 BOOT_LOG_INF("Starting swap using scratch algorithm.");
703
Fabio Utzig12d59162019-11-28 10:01:59 -0300704 /*
705 * Knowing the size of the largest image between both slots, here we
706 * find what is the last sector in the primary slot that needs swapping.
707 * Since we already know that both slots are compatible, the secondary
708 * slot's last sector is not really required after this check is finished.
709 */
710 while (1) {
711 if ((primary_slot_size < copy_size) ||
712 (primary_slot_size < secondary_slot_size)) {
713 primary_slot_size += boot_img_sector_size(state,
714 BOOT_PRIMARY_SLOT,
715 last_sector_idx);
716 }
717 if ((secondary_slot_size < copy_size) ||
718 (secondary_slot_size < primary_slot_size)) {
719 secondary_slot_size += boot_img_sector_size(state,
720 BOOT_SECONDARY_SLOT,
721 last_idx_secondary_slot);
722 }
723 if (primary_slot_size >= copy_size &&
724 secondary_slot_size >= copy_size &&
725 primary_slot_size == secondary_slot_size) {
726 break;
727 }
728 last_sector_idx++;
729 last_idx_secondary_slot++;
730 }
731
732 swap_idx = 0;
733 while (last_sector_idx >= 0) {
734 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
735 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
736 boot_swap_sectors(first_sector_idx, sz, state, bs);
737 }
738
739 last_sector_idx = first_sector_idx - 1;
740 swap_idx++;
741 }
742
743}
David Vinczee574f2d2020-07-10 11:42:03 +0200744#endif /* !MCUBOOT_OVERWRITE_ONLY */
Fabio Utzig12d59162019-11-28 10:01:59 -0300745
Jamie McCrae3016d002023-03-14 12:35:51 +0000746int app_max_size(struct boot_loader_state *state)
747{
748 size_t num_sectors_primary;
749 size_t num_sectors_secondary;
750 size_t sz0, sz1;
751 size_t primary_slot_sz, secondary_slot_sz;
752#ifndef MCUBOOT_OVERWRITE_ONLY
753 size_t scratch_sz;
754#endif
755 size_t i, j;
756 int8_t smaller;
757
758 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
759 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
760
761#ifndef MCUBOOT_OVERWRITE_ONLY
762 scratch_sz = boot_scratch_area_size(state);
763#endif
764
765 /*
766 * The following loop scans all sectors in a linear fashion, assuring that
767 * for each possible sector in each slot, it is able to fit in the other
768 * slot's sector or sectors. Slot's should be compatible as long as any
769 * number of a slot's sectors are able to fit into another, which only
770 * excludes cases where sector sizes are not a multiple of each other.
771 */
772 i = sz0 = primary_slot_sz = 0;
773 j = sz1 = secondary_slot_sz = 0;
774 smaller = 0;
775 while (i < num_sectors_primary || j < num_sectors_secondary) {
776 if (sz0 == sz1) {
777 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
778 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
779 i++;
780 j++;
781 } else if (sz0 < sz1) {
782 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
783 /* Guarantee that multiple sectors of the secondary slot
784 * fit into the primary slot.
785 */
786 if (smaller == 2) {
787 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
788 return 0;
789 }
790 smaller = 1;
791 i++;
792 } else {
793 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
794 /* Guarantee that multiple sectors of the primary slot
795 * fit into the secondary slot.
796 */
797 if (smaller == 1) {
798 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
799 return 0;
800 }
801 smaller = 2;
802 j++;
803 }
804#ifndef MCUBOOT_OVERWRITE_ONLY
805 if (sz0 == sz1) {
806 primary_slot_sz += sz0;
807 secondary_slot_sz += sz1;
808 /* Scratch has to fit each swap operation to the size of the larger
809 * sector among the primary slot and the secondary slot.
810 */
811 if (sz0 > scratch_sz || sz1 > scratch_sz) {
812 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
813 return 0;
814 }
815 smaller = sz0 = sz1 = 0;
816 }
817#endif
818 }
819
820#ifdef MCUBOOT_OVERWRITE_ONLY
821 return (sz1 < sz0 ? sz1 : sz0);
822#else
823 return (secondary_slot_sz < primary_slot_sz ? secondary_slot_sz : primary_slot_sz);
824#endif
825}
826#else
827int app_max_size(struct boot_loader_state *state)
828{
829 const struct flash_area *fap;
830 int fa_id;
831 int rc;
832 uint32_t active_slot;
833 int primary_sz, secondary_sz;
834
835 active_slot = state->slot_usage[BOOT_CURR_IMG(state)].active_slot;
836
837 fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
838 rc = flash_area_open(fa_id, &fap);
839 assert(rc == 0);
840 primary_sz = flash_area_get_size(fap);
841 flash_area_close(fap);
842
843 if (active_slot == BOOT_PRIMARY_SLOT) {
844 active_slot = BOOT_SECONDARY_SLOT;
845 } else {
846 active_slot = BOOT_PRIMARY_SLOT;
847 }
848
849 fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), active_slot);
850 rc = flash_area_open(fa_id, &fap);
851 assert(rc == 0);
852 secondary_sz = flash_area_get_size(fap);
853 flash_area_close(fap);
854
855 return (secondary_sz < primary_sz ? secondary_sz : primary_sz);
856}
857
Tamas Banfe031092020-09-10 17:32:39 +0200858#endif /* !MCUBOOT_DIRECT_XIP && !MCUBOOT_RAM_LOAD */
David Vinczee574f2d2020-07-10 11:42:03 +0200859
860#endif /* !MCUBOOT_SWAP_USING_MOVE */