blob: bf638bfa8076154a7ce20353e97342952e794298 [file] [log] [blame]
Fabio Utzig12d59162019-11-28 10:01:59 -03001/*
2 * Copyright (c) 2019 JUUL Labs
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <stddef.h>
19#include <stdbool.h>
20#include <inttypes.h>
21#include <stdlib.h>
22#include <string.h>
23#include "bootutil/bootutil.h"
24#include "bootutil_priv.h"
25#include "swap_priv.h"
26#include "bootutil/bootutil_log.h"
27
28#include "mcuboot_config/mcuboot_config.h"
29
30MCUBOOT_LOG_MODULE_DECLARE(mcuboot);
31
32#if MCUBOOT_SWAP_USING_SCRATCH
33
34#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
35/*
36 * FIXME: this might have to be updated for threaded sim
37 */
38int boot_status_fails = 0;
39#define BOOT_STATUS_ASSERT(x) \
40 do { \
41 if (!(x)) { \
42 boot_status_fails++; \
43 } \
44 } while (0)
45#else
46#define BOOT_STATUS_ASSERT(x) ASSERT(x)
47#endif
48
49int
50boot_read_image_header(struct boot_loader_state *state, int slot,
51 struct image_header *out_hdr, struct boot_status *bs)
52{
53 const struct flash_area *fap;
54 int area_id;
55 int rc;
56
57 (void)bs;
58
59#if (BOOT_IMAGE_NUMBER == 1)
60 (void)state;
61#endif
62
63 area_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), slot);
64 rc = flash_area_open(area_id, &fap);
65 if (rc != 0) {
66 rc = BOOT_EFLASH;
67 goto done;
68 }
69
70 rc = flash_area_read(fap, 0, out_hdr, sizeof *out_hdr);
71 if (rc != 0) {
72 rc = BOOT_EFLASH;
73 goto done;
74 }
75
76 rc = 0;
77
78done:
79 flash_area_close(fap);
80 return rc;
81}
82
83/**
84 * Reads the status of a partially-completed swap, if any. This is necessary
85 * to recover in case the boot lodaer was reset in the middle of a swap
86 * operation.
87 */
88int
89swap_read_status_bytes(const struct flash_area *fap,
90 struct boot_loader_state *state, struct boot_status *bs)
91{
92 uint32_t off;
93 uint8_t status;
94 int max_entries;
95 int found;
96 int found_idx;
97 int invalid;
98 int rc;
99 int i;
100
101 off = boot_status_off(fap);
102 max_entries = boot_status_entries(BOOT_CURR_IMG(state), fap);
103 if (max_entries < 0) {
104 return BOOT_EBADARGS;
105 }
106
107 found = 0;
108 found_idx = 0;
109 invalid = 0;
110 for (i = 0; i < max_entries; i++) {
111 rc = flash_area_read_is_empty(fap, off + i * BOOT_WRITE_SZ(state),
112 &status, 1);
113 if (rc < 0) {
114 return BOOT_EFLASH;
115 }
116
117 if (rc == 1) {
118 if (found && !found_idx) {
119 found_idx = i;
120 }
121 } else if (!found) {
122 found = 1;
123 } else if (found_idx) {
124 invalid = 1;
125 break;
126 }
127 }
128
129 if (invalid) {
130 /* This means there was an error writing status on the last
131 * swap. Tell user and move on to validation!
132 */
133 BOOT_LOG_ERR("Detected inconsistent status!");
134
135#if !defined(MCUBOOT_VALIDATE_PRIMARY_SLOT)
136 /* With validation of the primary slot disabled, there is no way
137 * to be sure the swapped primary slot is OK, so abort!
138 */
139 assert(0);
140#endif
141 }
142
143 if (found) {
144 if (!found_idx) {
145 found_idx = i;
146 }
147 bs->idx = (found_idx / BOOT_STATUS_STATE_COUNT) + 1;
148 bs->state = (found_idx % BOOT_STATUS_STATE_COUNT) + 1;
149 }
150
151 return 0;
152}
153
154uint32_t
155boot_status_internal_off(const struct boot_status *bs, int elem_sz)
156{
157 int idx_sz;
158
159 idx_sz = elem_sz * BOOT_STATUS_STATE_COUNT;
160
161 return (bs->idx - BOOT_STATUS_IDX_0) * idx_sz +
162 (bs->state - BOOT_STATUS_STATE_0) * elem_sz;
163}
164
165/*
166 * Slots are compatible when all sectors that store up to to size of the image
167 * round up to sector size, in both slot's are able to fit in the scratch
168 * area, and have sizes that are a multiple of each other (powers of two
169 * presumably!).
170 */
171int
172boot_slots_compatible(struct boot_loader_state *state)
173{
174 size_t num_sectors_primary;
175 size_t num_sectors_secondary;
176 size_t sz0, sz1;
177 size_t primary_slot_sz, secondary_slot_sz;
178 size_t scratch_sz;
179 size_t i, j;
180 int8_t smaller;
181
182 num_sectors_primary = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT);
183 num_sectors_secondary = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT);
184 if ((num_sectors_primary > BOOT_MAX_IMG_SECTORS) ||
185 (num_sectors_secondary > BOOT_MAX_IMG_SECTORS)) {
186 BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed");
187 return 0;
188 }
189
190 scratch_sz = boot_scratch_area_size(state);
191
192 /*
193 * The following loop scans all sectors in a linear fashion, assuring that
194 * for each possible sector in each slot, it is able to fit in the other
195 * slot's sector or sectors. Slot's should be compatible as long as any
196 * number of a slot's sectors are able to fit into another, which only
197 * excludes cases where sector sizes are not a multiple of each other.
198 */
199 i = sz0 = primary_slot_sz = 0;
200 j = sz1 = secondary_slot_sz = 0;
201 smaller = 0;
202 while (i < num_sectors_primary || j < num_sectors_secondary) {
203 if (sz0 == sz1) {
204 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
205 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
206 i++;
207 j++;
208 } else if (sz0 < sz1) {
209 sz0 += boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
210 /* Guarantee that multiple sectors of the secondary slot
211 * fit into the primary slot.
212 */
213 if (smaller == 2) {
214 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
215 return 0;
216 }
217 smaller = 1;
218 i++;
219 } else {
220 sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j);
221 /* Guarantee that multiple sectors of the primary slot
222 * fit into the secondary slot.
223 */
224 if (smaller == 1) {
225 BOOT_LOG_WRN("Cannot upgrade: slots have non-compatible sectors");
226 return 0;
227 }
228 smaller = 2;
229 j++;
230 }
231 if (sz0 == sz1) {
232 primary_slot_sz += sz0;
233 secondary_slot_sz += sz1;
234 /* Scratch has to fit each swap operation to the size of the larger
235 * sector among the primary slot and the secondary slot.
236 */
237 if (sz0 > scratch_sz || sz1 > scratch_sz) {
238 BOOT_LOG_WRN("Cannot upgrade: not all sectors fit inside scratch");
239 return 0;
240 }
241 smaller = sz0 = sz1 = 0;
242 }
243 }
244
245 if ((i != num_sectors_primary) ||
246 (j != num_sectors_secondary) ||
247 (primary_slot_sz != secondary_slot_sz)) {
248 BOOT_LOG_WRN("Cannot upgrade: slots are not compatible");
249 return 0;
250 }
251
252 return 1;
253}
254
255#define BOOT_LOG_SWAP_STATE(area, state) \
256 BOOT_LOG_INF("%s: magic=%s, swap_type=0x%x, copy_done=0x%x, " \
257 "image_ok=0x%x", \
258 (area), \
259 ((state)->magic == BOOT_MAGIC_GOOD ? "good" : \
260 (state)->magic == BOOT_MAGIC_UNSET ? "unset" : \
261 "bad"), \
262 (state)->swap_type, \
263 (state)->copy_done, \
264 (state)->image_ok)
265
266struct boot_status_table {
267 uint8_t bst_magic_primary_slot;
268 uint8_t bst_magic_scratch;
269 uint8_t bst_copy_done_primary_slot;
270 uint8_t bst_status_source;
271};
272
273/**
274 * This set of tables maps swap state contents to boot status location.
275 * When searching for a match, these tables must be iterated in order.
276 */
277static const struct boot_status_table boot_status_tables[] = {
278 {
279 /* | primary slot | scratch |
280 * ----------+--------------+--------------|
281 * magic | Good | Any |
282 * copy-done | Set | N/A |
283 * ----------+--------------+--------------'
284 * source: none |
285 * ----------------------------------------'
286 */
287 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
288 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
289 .bst_copy_done_primary_slot = BOOT_FLAG_SET,
290 .bst_status_source = BOOT_STATUS_SOURCE_NONE,
291 },
292
293 {
294 /* | primary slot | scratch |
295 * ----------+--------------+--------------|
296 * magic | Good | Any |
297 * copy-done | Unset | N/A |
298 * ----------+--------------+--------------'
299 * source: primary slot |
300 * ----------------------------------------'
301 */
302 .bst_magic_primary_slot = BOOT_MAGIC_GOOD,
303 .bst_magic_scratch = BOOT_MAGIC_NOTGOOD,
304 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
305 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
306 },
307
308 {
309 /* | primary slot | scratch |
310 * ----------+--------------+--------------|
311 * magic | Any | Good |
312 * copy-done | Any | N/A |
313 * ----------+--------------+--------------'
314 * source: scratch |
315 * ----------------------------------------'
316 */
317 .bst_magic_primary_slot = BOOT_MAGIC_ANY,
318 .bst_magic_scratch = BOOT_MAGIC_GOOD,
319 .bst_copy_done_primary_slot = BOOT_FLAG_ANY,
320 .bst_status_source = BOOT_STATUS_SOURCE_SCRATCH,
321 },
322 {
323 /* | primary slot | scratch |
324 * ----------+--------------+--------------|
325 * magic | Unset | Any |
326 * copy-done | Unset | N/A |
327 * ----------+--------------+--------------|
328 * source: varies |
329 * ----------------------------------------+--------------------------+
330 * This represents one of two cases: |
331 * o No swaps ever (no status to read, so no harm in checking). |
332 * o Mid-revert; status in primary slot. |
333 * -------------------------------------------------------------------'
334 */
335 .bst_magic_primary_slot = BOOT_MAGIC_UNSET,
336 .bst_magic_scratch = BOOT_MAGIC_ANY,
337 .bst_copy_done_primary_slot = BOOT_FLAG_UNSET,
338 .bst_status_source = BOOT_STATUS_SOURCE_PRIMARY_SLOT,
339 },
340};
341
342#define BOOT_STATUS_TABLES_COUNT \
343 (sizeof boot_status_tables / sizeof boot_status_tables[0])
344
345/**
346 * Determines where in flash the most recent boot status is stored. The boot
347 * status is necessary for completing a swap that was interrupted by a boot
348 * loader reset.
349 *
350 * @return A BOOT_STATUS_SOURCE_[...] code indicating where status should
351 * be read from.
352 */
353int
354swap_status_source(struct boot_loader_state *state)
355{
356 const struct boot_status_table *table;
357 struct boot_swap_state state_scratch;
358 struct boot_swap_state state_primary_slot;
359 int rc;
360 size_t i;
361 uint8_t source;
362 uint8_t image_index;
363
364#if (BOOT_IMAGE_NUMBER == 1)
365 (void)state;
366#endif
367
368 image_index = BOOT_CURR_IMG(state);
369 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_PRIMARY(image_index),
370 &state_primary_slot);
371 assert(rc == 0);
372
373 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH, &state_scratch);
374 assert(rc == 0);
375
376 BOOT_LOG_SWAP_STATE("Primary image", &state_primary_slot);
377 BOOT_LOG_SWAP_STATE("Scratch", &state_scratch);
378
379 for (i = 0; i < BOOT_STATUS_TABLES_COUNT; i++) {
380 table = &boot_status_tables[i];
381
382 if (boot_magic_compatible_check(table->bst_magic_primary_slot,
383 state_primary_slot.magic) &&
384 boot_magic_compatible_check(table->bst_magic_scratch,
385 state_scratch.magic) &&
386 (table->bst_copy_done_primary_slot == BOOT_FLAG_ANY ||
387 table->bst_copy_done_primary_slot == state_primary_slot.copy_done))
388 {
389 source = table->bst_status_source;
390
391#if (BOOT_IMAGE_NUMBER > 1)
392 /* In case of multi-image boot it can happen that if boot status
393 * info is found on scratch area then it does not belong to the
394 * currently examined image.
395 */
396 if (source == BOOT_STATUS_SOURCE_SCRATCH &&
397 state_scratch.image_num != BOOT_CURR_IMG(state)) {
398 source = BOOT_STATUS_SOURCE_NONE;
399 }
400#endif
401
402 BOOT_LOG_INF("Boot source: %s",
403 source == BOOT_STATUS_SOURCE_NONE ? "none" :
404 source == BOOT_STATUS_SOURCE_SCRATCH ? "scratch" :
405 source == BOOT_STATUS_SOURCE_PRIMARY_SLOT ?
406 "primary slot" : "BUG; can't happen");
407 return source;
408 }
409 }
410
411 BOOT_LOG_INF("Boot source: none");
412 return BOOT_STATUS_SOURCE_NONE;
413}
414
415/**
416 * Calculates the number of sectors the scratch area can contain. A "last"
417 * source sector is specified because images are copied backwards in flash
418 * (final index to index number 0).
419 *
420 * @param last_sector_idx The index of the last source sector
421 * (inclusive).
422 * @param out_first_sector_idx The index of the first source sector
423 * (inclusive) gets written here.
424 *
425 * @return The number of bytes comprised by the
426 * [first-sector, last-sector] range.
427 */
428static uint32_t
429boot_copy_sz(const struct boot_loader_state *state, int last_sector_idx,
430 int *out_first_sector_idx)
431{
432 size_t scratch_sz;
433 uint32_t new_sz;
434 uint32_t sz;
435 int i;
436
437 sz = 0;
438
439 scratch_sz = boot_scratch_area_size(state);
440 for (i = last_sector_idx; i >= 0; i--) {
441 new_sz = sz + boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i);
442 /*
443 * The secondary slot is not being checked here, because
444 * `boot_slots_compatible` already provides assurance that the copy size
445 * will be compatible with the primary slot and scratch.
446 */
447 if (new_sz > scratch_sz) {
448 break;
449 }
450 sz = new_sz;
451 }
452
453 /* i currently refers to a sector that doesn't fit or it is -1 because all
454 * sectors have been processed. In both cases, exclude sector i.
455 */
456 *out_first_sector_idx = i + 1;
457 return sz;
458}
459
460/**
461 * Swaps the contents of two flash regions within the two image slots.
462 *
463 * @param idx The index of the first sector in the range of
464 * sectors being swapped.
465 * @param sz The number of bytes to swap.
466 * @param bs The current boot status. This struct gets
467 * updated according to the outcome.
468 *
469 * @return 0 on success; nonzero on failure.
470 */
471static void
472boot_swap_sectors(int idx, uint32_t sz, struct boot_loader_state *state,
473 struct boot_status *bs)
474{
475 const struct flash_area *fap_primary_slot;
476 const struct flash_area *fap_secondary_slot;
477 const struct flash_area *fap_scratch;
478 uint32_t copy_sz;
479 uint32_t trailer_sz;
480 uint32_t img_off;
481 uint32_t scratch_trailer_off;
482 struct boot_swap_state swap_state;
483 size_t last_sector;
484 bool erase_scratch;
485 uint8_t image_index;
486 int rc;
487
488 /* Calculate offset from start of image area. */
489 img_off = boot_img_sector_off(state, BOOT_PRIMARY_SLOT, idx);
490
491 copy_sz = sz;
492 trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state));
493
494 /* sz in this function is always sized on a multiple of the sector size.
495 * The check against the start offset of the last sector
496 * is to determine if we're swapping the last sector. The last sector
497 * needs special handling because it's where the trailer lives. If we're
498 * copying it, we need to use scratch to write the trailer temporarily.
499 *
500 * NOTE: `use_scratch` is a temporary flag (never written to flash) which
501 * controls if special handling is needed (swapping last sector).
502 */
503 last_sector = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 1;
504 if ((img_off + sz) >
505 boot_img_sector_off(state, BOOT_PRIMARY_SLOT, last_sector)) {
506 copy_sz -= trailer_sz;
507 }
508
509 bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
510
511 image_index = BOOT_CURR_IMG(state);
512
513 rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index),
514 &fap_primary_slot);
515 assert (rc == 0);
516
517 rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index),
518 &fap_secondary_slot);
519 assert (rc == 0);
520
521 rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
522 assert (rc == 0);
523
524 if (bs->state == BOOT_STATUS_STATE_0) {
525 BOOT_LOG_DBG("erasing scratch area");
526 rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
527 assert(rc == 0);
528
529 if (bs->idx == BOOT_STATUS_IDX_0) {
530 /* Write a trailer to the scratch area, even if we don't need the
531 * scratch area for status. We need a temporary place to store the
532 * `swap-type` while we erase the primary trailer.
533 */
534 rc = swap_status_init(state, fap_scratch, bs);
535 assert(rc == 0);
536
537 if (!bs->use_scratch) {
538 /* Prepare the primary status area... here it is known that the
539 * last sector is not being used by the image data so it's safe
540 * to erase.
541 */
542 rc = swap_erase_trailer_sectors(state, fap_primary_slot);
543 assert(rc == 0);
544
545 rc = swap_status_init(state, fap_primary_slot, bs);
546 assert(rc == 0);
547
548 /* Erase the temporary trailer from the scratch area. */
549 rc = boot_erase_region(fap_scratch, 0, fap_scratch->fa_size);
550 assert(rc == 0);
551 }
552 }
553
554 rc = boot_copy_region(state, fap_secondary_slot, fap_scratch,
555 img_off, 0, copy_sz);
556 assert(rc == 0);
557
558 rc = boot_write_status(state, bs);
559 bs->state = BOOT_STATUS_STATE_1;
560 BOOT_STATUS_ASSERT(rc == 0);
561 }
562
563 if (bs->state == BOOT_STATUS_STATE_1) {
564 rc = boot_erase_region(fap_secondary_slot, img_off, sz);
565 assert(rc == 0);
566
567 rc = boot_copy_region(state, fap_primary_slot, fap_secondary_slot,
568 img_off, img_off, copy_sz);
569 assert(rc == 0);
570
571 if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
572 /* If not all sectors of the slot are being swapped,
573 * guarantee here that only the primary slot will have the state.
574 */
575 rc = swap_erase_trailer_sectors(state, fap_secondary_slot);
576 assert(rc == 0);
577 }
578
579 rc = boot_write_status(state, bs);
580 bs->state = BOOT_STATUS_STATE_2;
581 BOOT_STATUS_ASSERT(rc == 0);
582 }
583
584 if (bs->state == BOOT_STATUS_STATE_2) {
585 rc = boot_erase_region(fap_primary_slot, img_off, sz);
586 assert(rc == 0);
587
588 /* NOTE: If this is the final sector, we exclude the image trailer from
589 * this copy (copy_sz was truncated earlier).
590 */
591 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
592 0, img_off, copy_sz);
593 assert(rc == 0);
594
595 if (bs->use_scratch) {
596 scratch_trailer_off = boot_status_off(fap_scratch);
597
598 /* copy current status that is being maintained in scratch */
599 rc = boot_copy_region(state, fap_scratch, fap_primary_slot,
600 scratch_trailer_off, img_off + copy_sz,
601 (BOOT_STATUS_STATE_COUNT - 1) * BOOT_WRITE_SZ(state));
602 BOOT_STATUS_ASSERT(rc == 0);
603
604 rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_SCRATCH,
605 &swap_state);
606 assert(rc == 0);
607
608 if (swap_state.image_ok == BOOT_FLAG_SET) {
609 rc = boot_write_image_ok(fap_primary_slot);
610 assert(rc == 0);
611 }
612
613 if (swap_state.swap_type != BOOT_SWAP_TYPE_NONE) {
614 rc = boot_write_swap_info(fap_primary_slot,
615 swap_state.swap_type, image_index);
616 assert(rc == 0);
617 }
618
619 rc = boot_write_swap_size(fap_primary_slot, bs->swap_size);
620 assert(rc == 0);
621
622#ifdef MCUBOOT_ENC_IMAGES
623 rc = boot_write_enc_key(fap_primary_slot, 0, bs->enckey[0]);
624 assert(rc == 0);
625
626 rc = boot_write_enc_key(fap_primary_slot, 1, bs->enckey[1]);
627 assert(rc == 0);
628#endif
629 rc = boot_write_magic(fap_primary_slot);
630 assert(rc == 0);
631 }
632
633 /* If we wrote a trailer to the scratch area, erase it after we persist
634 * a trailer to the primary slot. We do this to prevent mcuboot from
635 * reading a stale status from the scratch area in case of immediate
636 * reset.
637 */
638 erase_scratch = bs->use_scratch;
639 bs->use_scratch = 0;
640
641 rc = boot_write_status(state, bs);
642 bs->idx++;
643 bs->state = BOOT_STATUS_STATE_0;
644 BOOT_STATUS_ASSERT(rc == 0);
645
646 if (erase_scratch) {
647 rc = boot_erase_region(fap_scratch, 0, sz);
648 assert(rc == 0);
649 }
650 }
651
652 flash_area_close(fap_primary_slot);
653 flash_area_close(fap_secondary_slot);
654 flash_area_close(fap_scratch);
655}
656
657void
658swap_run(struct boot_loader_state *state, struct boot_status *bs,
659 uint32_t copy_size)
660{
661 uint32_t sz;
662 int first_sector_idx;
663 int last_sector_idx;
664 uint32_t swap_idx;
665 int last_idx_secondary_slot;
666 uint32_t primary_slot_size;
667 uint32_t secondary_slot_size;
668 primary_slot_size = 0;
669 secondary_slot_size = 0;
670 last_sector_idx = 0;
671 last_idx_secondary_slot = 0;
672
673 /*
674 * Knowing the size of the largest image between both slots, here we
675 * find what is the last sector in the primary slot that needs swapping.
676 * Since we already know that both slots are compatible, the secondary
677 * slot's last sector is not really required after this check is finished.
678 */
679 while (1) {
680 if ((primary_slot_size < copy_size) ||
681 (primary_slot_size < secondary_slot_size)) {
682 primary_slot_size += boot_img_sector_size(state,
683 BOOT_PRIMARY_SLOT,
684 last_sector_idx);
685 }
686 if ((secondary_slot_size < copy_size) ||
687 (secondary_slot_size < primary_slot_size)) {
688 secondary_slot_size += boot_img_sector_size(state,
689 BOOT_SECONDARY_SLOT,
690 last_idx_secondary_slot);
691 }
692 if (primary_slot_size >= copy_size &&
693 secondary_slot_size >= copy_size &&
694 primary_slot_size == secondary_slot_size) {
695 break;
696 }
697 last_sector_idx++;
698 last_idx_secondary_slot++;
699 }
700
701 swap_idx = 0;
702 while (last_sector_idx >= 0) {
703 sz = boot_copy_sz(state, last_sector_idx, &first_sector_idx);
704 if (swap_idx >= (bs->idx - BOOT_STATUS_IDX_0)) {
705 boot_swap_sectors(first_sector_idx, sz, state, bs);
706 }
707
708 last_sector_idx = first_sector_idx - 1;
709 swap_idx++;
710 }
711
712}
713
714#endif