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