1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Copyright (c) 2019 JUUL Labs
5 * Copyright (c) 2025 Nordic Semiconductor ASA
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 #include <stddef.h>
21 #include <stdbool.h>
22 #include <inttypes.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "bootutil/bootutil.h"
26 #include "bootutil_priv.h"
27 #include "swap_priv.h"
28 #include "bootutil/bootutil_log.h"
29
30 #include "mcuboot_config/mcuboot_config.h"
31
32 BOOT_LOG_MODULE_DECLARE(mcuboot);
33
34 #ifdef MCUBOOT_SWAP_USING_OFFSET
35
36 #if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
37 /*
38 * FIXME: this might have to be updated for threaded sim
39 */
40 int boot_status_fails = 0;
41 #define BOOT_STATUS_ASSERT(x) \
42 do { \
43 if (!(x)) { \
44 boot_status_fails++; \
45 } \
46 } while (0)
47 #else
48 #define BOOT_STATUS_ASSERT(x) ASSERT(x)
49 #endif
50
51 #if defined(MCUBOOT_ENC_IMAGES)
52 #define BOOT_COPY_REGION(state, fap_pri, fap_sec, pri_off, sec_off, sz, sector_off) \
53 boot_copy_region(state, fap_pri, fap_sec, pri_off, sec_off, sz, sector_off)
54 #else
55 #define BOOT_COPY_REGION(state, fap_pri, fap_sec, pri_off, sec_off, sz, sector_off) \
56 boot_copy_region(state, fap_pri, fap_sec, pri_off, sec_off, sz)
57 #endif
58
find_last_idx(struct boot_loader_state * state,uint32_t swap_size)59 uint32_t find_last_idx(struct boot_loader_state *state, uint32_t swap_size)
60 {
61 uint32_t sector_sz;
62 uint32_t sz;
63 uint32_t last_idx;
64
65 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
66 sz = 0;
67 last_idx = 0;
68
69 while (1) {
70 sz += sector_sz;
71 last_idx++;
72 if (sz >= swap_size) {
73 break;
74 }
75 }
76
77 return last_idx;
78 }
79
boot_read_image_header(struct boot_loader_state * state,int slot,struct image_header * out_hdr,struct boot_status * bs)80 int boot_read_image_header(struct boot_loader_state *state, int slot,
81 struct image_header *out_hdr, struct boot_status *bs)
82 {
83 const struct flash_area *fap;
84 uint32_t off = 0;
85 uint32_t sz;
86 uint32_t last_idx;
87 uint32_t swap_size;
88 int area_id;
89 int rc;
90 bool check_other_sector = true;
91
92 #if (BOOT_IMAGE_NUMBER == 1)
93 (void)state;
94 #endif
95
96 if (bs == NULL) {
97 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
98
99 if (slot == BOOT_SECONDARY_SLOT &&
100 boot_swap_type_multi(BOOT_CURR_IMG(state)) != BOOT_SWAP_TYPE_REVERT) {
101 off = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0);
102 }
103 } else {
104 if (!boot_status_is_reset(bs)) {
105 check_other_sector = false;
106 boot_find_status(BOOT_CURR_IMG(state), &fap);
107
108 if (fap == NULL || boot_read_swap_size(fap, &swap_size)) {
109 rc = BOOT_EFLASH;
110 goto done;
111 }
112
113 flash_area_close(fap);
114 last_idx = find_last_idx(state, swap_size);
115 sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
116
117 /*
118 * Find the correct offset or slot where the image header is expected to
119 * be found for the steps where it is moved or swapped.
120 */
121 if (bs->swap_type == BOOT_SWAP_TYPE_REVERT ||
122 boot_swap_type_multi(BOOT_CURR_IMG(state)) == BOOT_SWAP_TYPE_REVERT) {
123 if (slot == 0) {
124 if (((bs->idx - BOOT_STATUS_IDX_0) > last_idx ||
125 ((bs->idx - BOOT_STATUS_IDX_0) == last_idx &&
126 bs->state == BOOT_STATUS_STATE_1))) {
127 slot = 1;
128 off = sz;
129 } else {
130 slot = 0;
131 off = 0;
132 }
133 } else if (slot == 1) {
134 if ((bs->idx - BOOT_STATUS_IDX_0) > last_idx ||
135 ((bs->idx - BOOT_STATUS_IDX_0) == last_idx &&
136 bs->state == BOOT_STATUS_STATE_2)) {
137 slot = 0;
138 off = 0;
139 } else {
140 slot = 1;
141 off = 0;
142 }
143 }
144 } else {
145 if (slot == 0) {
146 if ((bs->idx > BOOT_STATUS_IDX_0 ||
147 (bs->idx == BOOT_STATUS_IDX_0 && bs->state == BOOT_STATUS_STATE_1)) &&
148 bs->idx <= last_idx) {
149 slot = 1;
150 off = 0;
151 } else {
152 slot = 0;
153 off = 0;
154 }
155 } else if (slot == 1) {
156 if (bs->idx > BOOT_STATUS_IDX_0) {
157 slot = 0;
158 off = 0;
159 } else {
160 slot = 1;
161 off = sz;
162 }
163 }
164 }
165
166 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
167 } else {
168 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
169
170 if (bs->swap_type == BOOT_SWAP_TYPE_REVERT ||
171 boot_swap_type_multi(BOOT_CURR_IMG(state)) == BOOT_SWAP_TYPE_REVERT) {
172 off = 0;
173 }
174 else if (slot == BOOT_SECONDARY_SLOT) {
175 off = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0);
176 }
177 }
178 }
179
180 rc = flash_area_open(area_id, &fap);
181 if (rc != 0) {
182 rc = BOOT_EFLASH;
183 goto done;
184 }
185
186 rc = flash_area_read(fap, off, out_hdr, sizeof *out_hdr);
187 if (rc != 0) {
188 rc = BOOT_EFLASH;
189 goto done;
190 }
191
192 if (check_other_sector == true && out_hdr->ih_magic != IMAGE_MAGIC &&
193 slot == BOOT_SECONDARY_SLOT) {
194 if (boot_swap_type_multi(BOOT_CURR_IMG(state)) != BOOT_SWAP_TYPE_REVERT) {
195 off = 0;
196 } else {
197 off = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0);
198 }
199
200 rc = flash_area_read(fap, off, out_hdr, sizeof(*out_hdr));
201 if (rc != 0) {
202 rc = BOOT_EFLASH;
203 goto done;
204 }
205 }
206
207 #if defined(MCUBOOT_BOOTSTRAP)
208 if (out_hdr->ih_magic == IMAGE_MAGIC && (bs != NULL || state->bootstrap_secondary_offset_set[
209 BOOT_CURR_IMG(state)] == false) &&
210 slot == BOOT_SECONDARY_SLOT) {
211 state->bootstrap_secondary_offset_set[BOOT_CURR_IMG(state)] = true;
212 #else
213 if (out_hdr->ih_magic == IMAGE_MAGIC && bs != NULL && slot == BOOT_SECONDARY_SLOT) {
214 #endif
215 state->secondary_offset[BOOT_CURR_IMG(state)] = off;
216 }
217
218 /* We only know where the headers are located when bs is valid */
219 if (bs != NULL && out_hdr->ih_magic != IMAGE_MAGIC) {
220 rc = -1;
221 goto done;
222 }
223
224 rc = 0;
225
226 done:
227 flash_area_close(fap);
228 return rc;
229 }
230
231 int swap_read_status_bytes(const struct flash_area *fap, struct boot_loader_state *state,
232 struct boot_status *bs)
233 {
234 uint32_t off;
235 uint8_t status;
236 int max_entries;
237 int found_idx;
238 uint8_t write_sz;
239 int rc;
240 int last_rc;
241 int erased_sections;
242 int i;
243
244 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
245
246 if (max_entries < 0) {
247 return BOOT_EBADARGS;
248 }
249
250 erased_sections = 0;
251 found_idx = -1;
252 /* Skip erased sectors at the end */
253 last_rc = 1;
254 write_sz = BOOT_WRITE_SZ(state);
255 off = boot_status_off(fap);
256 for (i = max_entries; i > 0; i--) {
257 rc = flash_area_read(fap, off + (i - 1) * write_sz, &status, 1);
258 if (rc < 0) {
259 return BOOT_EFLASH;
260 }
261
262 if (bootutil_buffer_is_erased(fap, &status, 1)) {
263 if (rc != last_rc) {
264 erased_sections++;
265 }
266 } else {
267 if (found_idx == -1) {
268 found_idx = i;
269 }
270 }
271 last_rc = rc;
272 }
273
274 if (erased_sections > 1) {
275 /* This means there was an error writing status on the last swap. Tell user and move on
276 * to validation!
277 */
278 #if !defined(__BOOTSIM__)
279 BOOT_LOG_ERR("Detected inconsistent status!");
280 #endif
281
282 #if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
283 /* With validation of the primary slot disabled, there is no way to be sure the swapped
284 * primary slot is OK, so abort!
285 */
286 assert(0);
287 #endif
288 }
289
290 if (found_idx == -1) {
291 /* no swap status found; nothing to do */
292 } else {
293 bs->op = BOOT_STATUS_OP_SWAP;
294 bs->idx = (found_idx / BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_IDX_0;
295 bs->state = (found_idx % BOOT_STATUS_SWAP_STATE_COUNT) + BOOT_STATUS_STATE_0;
296 }
297
298 return 0;
299 }
300
301 uint32_t boot_status_internal_off(const struct boot_status *bs, int elem_sz)
302 {
303 uint32_t off;
304 int idx_sz;
305
306 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
307 off = (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
308 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
309
310 return off;
311 }
312
313 static int app_max_sectors(struct boot_loader_state *state)
314 {
315 uint32_t sz = 0;
316 uint32_t sector_sz;
317 uint32_t trailer_sz;
318 uint32_t first_trailer_idx;
319
320 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
321 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
322 first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
323
324 while (1) {
325 sz += sector_sz;
326
327 if (sz >= trailer_sz) {
328 break;
329 }
330
331 first_trailer_idx--;
332 }
333
334 return first_trailer_idx;
335 }
336
337 int boot_slots_compatible(struct boot_loader_state *state)
338 {
339 size_t num_sectors_pri;
340 size_t num_sectors_sec;
341 size_t sector_sz_pri = 0;
342 size_t sector_sz_sec = 0;
343 size_t i;
344 size_t num_usable_sectors_pri;
345
346 num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
347 num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
348 num_usable_sectors_pri = app_max_sectors(state);
349
350 if ((num_sectors_pri != num_sectors_sec) &&
351 ((num_sectors_pri + 1) != num_sectors_sec) &&
352 ((num_usable_sectors_pri + 1) != (num_sectors_sec))) {
353 BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors");
354 BOOT_LOG_DBG("slot0 sectors: %d, slot1 sectors: %d, usable slot0 sectors: %d",
355 (int)num_sectors_pri, (int)num_sectors_sec,
356 (int)(num_usable_sectors_pri - 1));
357 return 0;
358 } else if (num_sectors_pri > BOOT_MAX_IMG_SECTORS) {
359 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
360 return 0;
361 }
362
363 if ((num_usable_sectors_pri + 1) != num_sectors_sec) {
364 BOOT_LOG_DBG("Non-optimal sector distribution, slot0 has %d usable sectors "
365 "but slot1 has %d usable sectors", (int)(num_usable_sectors_pri),
366 ((int)num_sectors_sec - 1));
367 }
368
369 for (i = 0; i < num_sectors_pri; i++) {
370 sector_sz_pri = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
371 sector_sz_sec = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, i);
372 if (sector_sz_pri != sector_sz_sec) {
373 BOOT_LOG_WRN("Cannot upgrade: not same sector layout");
374 return 0;
375 }
376 }
377
378 #ifdef MCUBOOT_SLOT0_EXPECTED_ERASE_SIZE
379 if (sector_sz_pri != MCUBOOT_SLOT0_EXPECTED_ERASE_SIZE) {
380 BOOT_LOG_DBG("Discrepancy, slot0 expected erase size: %d, actual: %d",
381 MCUBOOT_SLOT0_EXPECTED_ERASE_SIZE, sector_sz_pri);
382 }
383 #endif
384 #ifdef MCUBOOT_SLOT1_EXPECTED_ERASE_SIZE
385 if (sector_sz_sec != MCUBOOT_SLOT1_EXPECTED_ERASE_SIZE) {
386 BOOT_LOG_DBG("Discrepancy, slot1 expected erase size: %d, actual: %d",
387 MCUBOOT_SLOT1_EXPECTED_ERASE_SIZE, sector_sz_sec);
388 }
389 #endif
390
391 #if defined(MCUBOOT_SLOT0_EXPECTED_WRITE_SIZE) || defined(MCUBOOT_SLOT1_EXPECTED_WRITE_SIZE)
392 if (!swap_write_block_size_check(state)) {
393 BOOT_LOG_WRN("Cannot upgrade: slot write sizes are not compatible");
394 return 0;
395 }
396 #endif
397
398 if (num_sectors_pri > num_sectors_sec) {
399 if (sector_sz_pri != boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i)) {
400 BOOT_LOG_WRN("Cannot upgrade: not same sector layout");
401 return 0;
402 }
403 }
404
405 return 1;
406 }
407
408 #define BOOT_LOG_SWAP_STATE(area, state) \
409 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
410 "image_ok=0x%x", \
411 (area), \
412 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
413 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
414 "bad"), \
415 (state)->swap_type, \
416 (state)->copy_done, \
417 (state)->image_ok)
418
419 int swap_status_source(struct boot_loader_state *state)
420 {
421 struct boot_swap_state state_primary_slot;
422 struct boot_swap_state state_secondary_slot;
423 int rc;
424 uint8_t source;
425 uint8_t image_index;
426
427 #if (BOOT_IMAGE_NUMBER == 1)
428 (void)state;
429 #endif
430
431 image_index = BOOT_CURR_IMG(state);
432 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index), &state_primary_slot);
433 assert(rc == 0);
434 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
435
436 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SECONDARY(image_index),
437 &state_secondary_slot);
438 assert(rc == 0);
439 BOOT_LOG_SWAP_STATE("Secondary image", &state_secondary_slot);
440
441 if (state_primary_slot.magic == BOOT_MAGIC_GOOD &&
442 state_primary_slot.copy_done == BOOT_FLAG_UNSET &&
443 state_secondary_slot.magic != BOOT_MAGIC_GOOD) {
444
445 source = BOOT_STATUS_SOURCE_PRIMARY_SLOT;
446
447 BOOT_LOG_INF("Boot source: primary slot");
448 return source;
449 }
450
451 BOOT_LOG_INF("Boot source: none");
452 return BOOT_STATUS_SOURCE_NONE;
453 }
454
455 static void boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
456 struct boot_status *bs, const struct flash_area *fap_pri,
457 const struct flash_area *fap_sec, bool skip_primary,
458 bool skip_secondary)
459 {
460 uint32_t pri_off;
461 uint32_t sec_off;
462 uint32_t sec_up_off;
463 int rc = 0;
464
465 pri_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
466 sec_off = boot_img_sector_off(state, BOOT_SECONDARY_SLOT, idx);
467 sec_up_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, (idx + 1));
468
469 if (bs->state == BOOT_STATUS_STATE_0) {
470 if (skip_primary == true) {
471 BOOT_LOG_DBG("Skipping erase of secondary 0x%x and copy from primary 0x%x", sec_off,
472 pri_off);
473 } else {
474 /* Copy from slot 0 X to slot 1 X */
475 BOOT_LOG_DBG("Erasing secondary 0x%x of 0x%x", sec_off, sz);
476 rc = boot_erase_region(fap_sec, sec_off, sz);
477 assert(rc == 0);
478
479 BOOT_LOG_DBG("Copying primary 0x%x -> secondary 0x%x of 0x%x", pri_off, sec_off, sz);
480 rc = BOOT_COPY_REGION(state, fap_pri, fap_sec, pri_off, sec_off, sz, 0);
481 assert(rc == 0);
482 }
483
484 rc = boot_write_status(state, bs);
485 bs->state = BOOT_STATUS_STATE_1;
486 BOOT_STATUS_ASSERT(rc == 0);
487 }
488
489 if (bs->state == BOOT_STATUS_STATE_1) {
490 if (skip_secondary == true) {
491 BOOT_LOG_DBG("Skipping erase of primary 0x%x and copy from secondary 0x%x", pri_off,
492 sec_up_off);
493 } else {
494 /* Erase slot 0 X */
495 BOOT_LOG_DBG("Erasing primary 0x%x of 0x%x", pri_off, sz);
496 rc = boot_erase_region(fap_pri, pri_off, sz);
497 assert(rc == 0);
498
499 /* Copy from slot 1 (X + 1) to slot 0 X */
500 BOOT_LOG_DBG("Copying secondary 0x%x -> primary 0x%x of 0x%x", sec_up_off, pri_off,
501 sz);
502 rc = BOOT_COPY_REGION(state, fap_sec, fap_pri, sec_up_off, pri_off, sz, 0);
503 assert(rc == 0);
504 }
505
506 rc = boot_write_status(state, bs);
507 bs->idx++;
508 bs->state = BOOT_STATUS_STATE_0;
509 BOOT_STATUS_ASSERT(rc == 0);
510 }
511 }
512
513 static void boot_swap_sectors_revert(int idx, uint32_t sz, struct boot_loader_state *state,
514 struct boot_status *bs, const struct flash_area *fap_pri,
515 const struct flash_area *fap_sec, uint32_t sector_sz,
516 bool skip_primary, bool skip_secondary)
517 {
518 uint32_t pri_off;
519 uint32_t sec_off;
520 uint32_t sec_up_off;
521 int rc = 0;
522 #if !defined(MCUBOOT_ENC_IMAGES)
523 (void)sector_sz;
524 #endif
525
526 pri_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
527 sec_off = boot_img_sector_off(state, BOOT_SECONDARY_SLOT, idx + 1);
528 sec_up_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
529
530 if (bs->state == BOOT_STATUS_STATE_0) {
531 if (skip_primary == true) {
532 BOOT_LOG_DBG("Skipping erase of secondary 0x%x and copy from primary 0x%x", sec_off,
533 pri_off);
534 } else {
535 /* Copy from slot 0 X to slot 1 X */
536 BOOT_LOG_DBG("Erasing secondary 0x%x of 0x%x", sec_off, sz);
537 rc = boot_erase_region(fap_sec, sec_off, sz);
538 assert(rc == 0);
539
540 BOOT_LOG_DBG("Copying primary 0x%x -> secondary 0x%x of 0x%x", pri_off, sec_off, sz);
541 rc = BOOT_COPY_REGION(state, fap_pri, fap_sec, pri_off, sec_off, sz, sector_sz);
542 assert(rc == 0);
543 }
544
545 rc = boot_write_status(state, bs);
546 bs->state = BOOT_STATUS_STATE_1;
547 BOOT_STATUS_ASSERT(rc == 0);
548 }
549
550 if (bs->state == BOOT_STATUS_STATE_1) {
551 if (skip_secondary == true) {
552 BOOT_LOG_DBG("Skipping erase of primary 0x%x and copy from secondary 0x%x", pri_off,
553 sec_up_off);
554 } else {
555 /* Erase slot 0 X */
556 BOOT_LOG_DBG("Erasing primary 0x%x of 0x%x", pri_off, sz);
557 rc = boot_erase_region(fap_pri, pri_off, sz);
558 assert(rc == 0);
559
560 /* Copy from slot 1 (X + 1) to slot 0 X */
561 BOOT_LOG_DBG("Copying secondary 0x%x -> primary 0x%x of 0x%x", sec_up_off, pri_off,
562 sz);
563 rc = BOOT_COPY_REGION(state, fap_sec, fap_pri, sec_up_off, pri_off, sz, 0);
564 assert(rc == 0);
565 }
566
567 rc = boot_write_status(state, bs);
568 bs->idx++;
569 bs->state = BOOT_STATUS_STATE_0;
570 BOOT_STATUS_ASSERT(rc == 0);
571 }
572 }
573
574 /*
575 * When starting a revert the swap status exists in the primary slot, and
576 * the status in the secondary slot is erased. To start the swap, the status
577 * area in the primary slot must be re-initialized; if during the small
578 * window of time between re-initializing it and writing the first metadata
579 * a reset happens, the swap process is broken and cannot be resumed.
580 *
581 * This function handles the issue by making the revert look like a permanent
582 * upgrade (by initializing the secondary slot).
583 */
584 void fixup_revert(const struct boot_loader_state *state, struct boot_status *bs,
585 const struct flash_area *fap_sec)
586 {
587 struct boot_swap_state swap_state;
588 int rc;
589
590 #if (BOOT_IMAGE_NUMBER == 1)
591 (void)state;
592 #endif
593
594 /* No fixup required */
595 if (bs->swap_type != BOOT_SWAP_TYPE_REVERT ||
596 bs->idx != BOOT_STATUS_IDX_0) {
597 return;
598 }
599
600 rc = boot_read_swap_state(fap_sec, &swap_state);
601 assert(rc == 0);
602
603 BOOT_LOG_SWAP_STATE("Secondary image", &swap_state);
604
605 if (swap_state.magic == BOOT_MAGIC_UNSET) {
606 rc = swap_erase_trailer_sectors(state, fap_sec);
607 assert(rc == 0);
608
609 rc = boot_write_copy_done(fap_sec);
610 assert(rc == 0);
611
612 rc = swap_status_init(state, fap_sec, bs);
613 assert(rc == 0);
614 }
615 }
616
617 void swap_run(struct boot_loader_state *state, struct boot_status *bs,
618 uint32_t copy_size)
619 {
620 uint32_t sz;
621 uint32_t sector_sz;
622 uint32_t idx;
623 uint32_t trailer_sz;
624 uint32_t first_trailer_idx;
625 uint32_t last_idx;
626 uint32_t used_sectors_pri;
627 uint32_t used_sectors_sec;
628 uint8_t image_index;
629 const struct flash_area *fap_pri;
630 const struct flash_area *fap_sec;
631 int rc;
632
633 BOOT_LOG_INF("Starting swap using offset algorithm.");
634
635 last_idx = find_last_idx(state, copy_size);
636 sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
637
638 /* When starting a new swap upgrade, check that there is enough space */
639 if (boot_status_is_reset(bs)) {
640 sz = 0;
641 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
642 first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
643
644 while (1) {
645 sz += sector_sz;
646 if (sz >= trailer_sz) {
647 break;
648 }
649 first_trailer_idx--;
650 }
651
652 if (last_idx >= first_trailer_idx) {
653 BOOT_LOG_WRN("Not enough free space to run swap upgrade");
654 BOOT_LOG_WRN("required %d bytes but only %d are available",
655 (last_idx + 1) * sector_sz,
656 first_trailer_idx * sector_sz);
657 bs->swap_type = BOOT_SWAP_TYPE_NONE;
658 return;
659 }
660 }
661
662 image_index = BOOT_CURR_IMG(state);
663
664 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), &fap_pri);
665 assert (rc == 0);
666
667 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec);
668 assert (rc == 0);
669
670 fixup_revert(state, bs, fap_sec);
671
672 /* Init areas for storing swap status */
673 if (bs->idx == BOOT_STATUS_IDX_0) {
674 int rc;
675
676 if (bs->source != BOOT_STATUS_SOURCE_PRIMARY_SLOT) {
677 rc = swap_erase_trailer_sectors(state, fap_pri);
678 assert(rc == 0);
679
680 rc = swap_status_init(state, fap_pri, bs);
681 assert(rc == 0);
682 }
683
684 rc = swap_erase_trailer_sectors(state, fap_sec);
685 assert(rc == 0);
686 }
687
688 bs->op = BOOT_STATUS_OP_SWAP;
689 idx = 0;
690 used_sectors_pri = ((state->imgs[BOOT_CURR_IMG(state)][BOOT_PRIMARY_SLOT].hdr.ih_hdr_size +
691 state->imgs[BOOT_CURR_IMG(state)][BOOT_PRIMARY_SLOT].hdr.ih_protect_tlv_size +
692 state->imgs[BOOT_CURR_IMG(state)][BOOT_PRIMARY_SLOT].hdr.ih_img_size) + sector_sz - 1) /
693 sector_sz;
694 used_sectors_sec = ((state->imgs[BOOT_CURR_IMG(state)][BOOT_SECONDARY_SLOT].hdr.ih_hdr_size +
695 state->imgs[BOOT_CURR_IMG(state)][BOOT_SECONDARY_SLOT].hdr.ih_protect_tlv_size +
696 state->imgs[BOOT_CURR_IMG(state)][BOOT_SECONDARY_SLOT].hdr.ih_img_size) + sector_sz - 1) /
697 sector_sz;
698
699 if (bs->swap_type == BOOT_SWAP_TYPE_REVERT ||
700 boot_swap_type_multi(BOOT_CURR_IMG(state)) == BOOT_SWAP_TYPE_REVERT) {
701 while (idx <= last_idx) {
702 if (idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
703 uint32_t mirror_idx = last_idx - idx;
704
705 boot_swap_sectors_revert(mirror_idx, sector_sz, state, bs, fap_pri, fap_sec,
706 sector_sz,
707 (mirror_idx > used_sectors_pri ? true : false),
708 (mirror_idx > used_sectors_sec ? true : false));
709 }
710
711 idx++;
712 }
713
714 /* Erase the first sector in the secondary slot before completing revert so that the
715 * status is not wrongly used as a valid header. Also erase the trailer in the secondary
716 * to allow for a future update to be loaded
717 */
718 rc = boot_erase_region(fap_sec, boot_img_sector_off(state, BOOT_SECONDARY_SLOT, 0),
719 sector_sz);
720 assert(rc == 0);
721 rc = swap_erase_trailer_sectors(state, fap_sec);
722 assert(rc == 0);
723 } else {
724 while (idx <= last_idx) {
725 if (idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
726 boot_swap_sectors(idx, sector_sz, state, bs, fap_pri, fap_sec,
727 (idx > used_sectors_pri ? true : false),
728 (idx > used_sectors_sec ? true : false));
729 }
730
731 idx++;
732 }
733 }
734
735 flash_area_close(fap_pri);
736 flash_area_close(fap_sec);
737 }
738
739 int app_max_size(struct boot_loader_state *state)
740 {
741 uint32_t sector_sz_primary;
742 uint32_t sector_sz_secondary;
743 uint32_t sz_primary;
744 uint32_t sz_secondary;
745
746 sector_sz_primary = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0);
747 sector_sz_secondary = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0);
748
749 /* Account for image flags and move sector */
750 sz_primary = app_max_sectors(state) * sector_sz_primary;
751 sz_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) * sector_sz_secondary -
752 sector_sz_primary;
753
754 return (sz_primary <= sz_secondary ? sz_primary : sz_secondary);
755 }
756
757 /* Compute the total size of the given image. Includes the size of the TLVs. */
758 int boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size)
759 {
760 const struct flash_area *fap;
761 struct image_tlv_info info;
762 uint32_t off;
763 uint32_t secondary_slot_off = 0;
764 uint32_t protect_tlv_size;
765 int area_id;
766 int rc;
767
768 #if (BOOT_IMAGE_NUMBER == 1)
769 (void)state;
770 #endif
771
772 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
773 rc = flash_area_open(area_id, &fap);
774 if (rc != 0) {
775 rc = BOOT_EFLASH;
776 goto done;
777 }
778
779 off = BOOT_TLV_OFF(boot_img_hdr(state, slot));
780
781 if (slot == BOOT_SECONDARY_SLOT) {
782 /* Check in the secondary position in the upgrade slot */
783 secondary_slot_off = state->secondary_offset[BOOT_CURR_IMG(state)];
784 }
785
786 if (flash_area_read(fap, (off + secondary_slot_off), &info, sizeof(info))) {
787 rc = BOOT_EFLASH;
788 goto done;
789 }
790
791 protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size;
792 if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
793 if (protect_tlv_size != info.it_tlv_tot) {
794 rc = BOOT_EBADIMAGE;
795 goto done;
796 }
797
798 if (flash_area_read(fap, (off + secondary_slot_off + info.it_tlv_tot),
799 &info, sizeof(info))) {
800 rc = BOOT_EFLASH;
801 goto done;
802 }
803 } else if (protect_tlv_size != 0) {
804 rc = BOOT_EBADIMAGE;
805 goto done;
806 }
807
808 if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
809 rc = BOOT_EBADIMAGE;
810 goto done;
811 }
812
813 *size = off + protect_tlv_size + info.it_tlv_tot;
814 rc = 0;
815
816 done:
817 flash_area_close(fap);
818 return rc;
819 }
820
821 #endif
822