-
Notifications
You must be signed in to change notification settings - Fork 722
/
Copy pathdesign.md
executable file
·1570 lines (1303 loc) · 70.4 KB
/
design.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!--
- SPDX-License-Identifier: Apache-2.0
- Copyright (c) 2017-2020 Linaro LTD
- Copyright (c) 2017-2019 JUUL Labs
- Copyright (c) 2019-2024 Arm Limited
- Original license:
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
-->
# Bootloader
## [Summary](#summary)
MCUboot comprises two packages:
* The bootutil library (boot/bootutil)
* The boot application (each port has its own at boot/<port>)
The bootutil library performs most of the functions of a bootloader. In
particular, the piece that is missing is the final step of actually jumping to
the main image. This last step is instead implemented by the boot application.
Bootloader functionality is separated in this manner to enable unit testing of
the bootloader. A library can be unit tested, but an application can't.
Therefore, functionality is delegated to the bootutil library when possible.
## [Limitations](#limitations)
The bootloader currently only supports images with the following
characteristics:
* Built to run from flash.
* Built to run from a fixed location (i.e., not position-independent).
## [Image format](#image-format)
The following definitions describe the image format.
``` c
#define IMAGE_MAGIC 0x96f3b83d
#define IMAGE_HEADER_SIZE 32
struct image_version {
uint8_t iv_major;
uint8_t iv_minor;
uint16_t iv_revision;
uint32_t iv_build_num;
};
/** Image header. All fields are in little endian byte order. */
struct image_header {
uint32_t ih_magic;
uint32_t ih_load_addr;
uint16_t ih_hdr_size; /* Size of image header (bytes). */
uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */
uint32_t ih_img_size; /* Does not include header. */
uint32_t ih_flags; /* IMAGE_F_[...]. */
struct image_version ih_ver;
uint32_t _pad1;
};
#define IMAGE_TLV_INFO_MAGIC 0x6907
#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908
/** Image TLV header. All fields in little endian. */
struct image_tlv_info {
uint16_t it_magic;
uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */
};
/** Image trailer TLV format. All fields in little endian. */
struct image_tlv {
uint8_t it_type; /* IMAGE_TLV_[...]. */
uint8_t _pad;
uint16_t it_len; /* Data length (not including TLV header). */
};
/*
* Image header flags.
*/
#define IMAGE_F_PIC 0x00000001 /* Not supported. */
#define IMAGE_F_ENCRYPTED_AES128 0x00000004 /* Encrypted using AES128. */
#define IMAGE_F_ENCRYPTED_AES256 0x00000008 /* Encrypted using AES256. */
#define IMAGE_F_NON_BOOTABLE 0x00000010 /* Split image app. */
#define IMAGE_F_RAM_LOAD 0x00000020
/*
* Image trailer TLV types.
*/
#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */
#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */
#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */
#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output - Not supported anymore */
#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */
#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */
#define IMAGE_TLV_ED25519 0x24 /* ED25519 of hash output */
#define IMAGE_TLV_SIG_PURE 0x25 /* If true then any signature found has been
calculated over image directly. */
#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */
#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW-128 or
256 */
#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-P256 */
#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */
#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */
#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */
```
Optional type-length-value records (TLVs) containing image metadata are placed
after the end of the image.
The `ih_protect_tlv_size` field indicates the length of the protected TLV area.
If protected TLVs are present then a TLV info header with magic equal to
`IMAGE_TLV_PROT_INFO_MAGIC` must be present and the protected TLVs (plus the
info header itself) have to be included in the hash calculation. Otherwise the
hash is only calculated over the image header and the image itself. In this
case the value of the `ih_protect_tlv_size` field is 0.
The `ih_hdr_size` field indicates the length of the header, and therefore the
offset of the image itself. This field provides for backwards compatibility in
case of changes to the format of the image header.
## [Flash map](#flash-map)
A device's flash is partitioned according to its _flash map_. At a high
level, the flash map maps numeric IDs to _flash areas_. A flash area is a
region of disk with the following properties:
1. An area can be fully erased without affecting any other areas.
2. A write to one area does not restrict writes to other areas.
The bootloader uses the following flash area IDs:
```c
/* Independent from multiple image boot */
#define FLASH_AREA_BOOTLOADER 0
#define FLASH_AREA_IMAGE_SCRATCH 3
```
```c
/* If the bootloader is working with the first image */
#define FLASH_AREA_IMAGE_PRIMARY 1
#define FLASH_AREA_IMAGE_SECONDARY 2
```
```c
/* If the bootloader is working with the second image */
#define FLASH_AREA_IMAGE_PRIMARY 5
#define FLASH_AREA_IMAGE_SECONDARY 6
```
The bootloader area contains the bootloader image itself. The other areas are
described in subsequent sections. The flash could contain multiple executable
images therefore the flash area IDs of primary and secondary areas are mapped
based on the number of the active image (on which the bootloader is currently
working).
## [Image slots](#image-slots)
A portion of the flash memory can be partitioned into multiple image areas, each
contains two image slots: a primary slot and a secondary slot.
Normally, the bootloader will only run an image from the primary slot, so
images must be built such that they can run from that fixed location in flash
(the exception to this is the [direct-xip](#direct-xip) and the
[ram-load](#ram-load) upgrade mode). If the bootloader needs to run the
image resident in the secondary slot, it must copy its contents into the primary
slot before doing so, either by swapping the two images or by overwriting the
contents of the primary slot. The bootloader supports either swap- or
overwrite-based image upgrades, but must be configured at build time to choose
one of these two strategies.
### [Swap using scratch](#image-swap-using-scratch)
When swap-using-scratch algorithm is used, in addition to the slots of
image areas, the bootloader requires a scratch area to allow for reliable
image swapping. The scratch area must have a size
that is enough to store at least the largest sector that is going to be swapped.
Many devices have small equally sized flash sectors, eg 4K, while others have
variable sized sectors where the largest sectors might be 128K or 256K, so the
scratch must be big enough to store that. The scratch is only ever used when
swapping firmware, which means only when doing an upgrade. Given that, the main
reason for using a larger size for the scratch is that flash wear will be more
evenly distributed, because a single sector would be written twice the number of
times than using two sectors, for example. To evaluate the ideal size of the
scratch for your use case the following parameters are relevant:
* the ratio of image size / scratch size
* the number of erase cycles supported by the flash hardware
The image size is used (instead of slot size) because only the slot's sectors
that are actually used for storing the image are copied. The image/scratch ratio
is the number of times the scratch will be erased on every upgrade. The number
of erase cycles divided by the image/scratch ratio will give you the number of
times an upgrade can be performed before the device goes out of spec.
```
num_upgrades = number_of_erase_cycles / (image_size / scratch_size)
```
Let's assume, for example, a device with 10000 erase cycles, an image size of
150K and a scratch of 4K (usual minimum size of 4K sector devices). This would
result in a total of:
`10000 / (150 / 4) ~ 267`
Increasing the scratch to 16K would give us:
`10000 / (150 / 16) ~ 1067`
There is no *best* ratio, as the right size is use-case dependent. Factors to
consider include the number of times a device will be upgraded both in the field
and during development, as well as any desired safety margin on the
manufacturer's specified number of erase cycles. In general, using a ratio that
allows hundreds to thousands of field upgrades in production is recommended.
swap-using scratch algorithm assumes that the primary and the secondary image
slot areas sizes are equal.
The maximum image size available for the application
will be:
```
maximum-image-size = image-slot-size - image-trailer-size
```
Where:
`image-slot-size` is the size of the image slot.
`image-trailer-size` is the size of the image trailer.
### [Swap using offset (without using scratch)](#image-swap-offset-no-scratch)
This algorithm is an alternative to the swap-using-scratch algorithm and an
enhancement of the swap using move algorithm.
It uses an additional sector in the secondary slot to make swap possible.
The algorithm works as follows:
1. Update image must be placed starting at the second sector in the secondary slot
2. Copies the N-th sector from the primary slot to the N-th sector of the
secondary slot.
3. Copies the (N+1)-th sector from the secondary slot to the N-th sector of the
primary slot.
4. Repeats steps 2. and 3. until all the slots' sectors are swapped.
This algorithm is designed so that the lower sector of the secondary slot is
used only for allowing sectors to move down during a swap. Therefore the most
memory-size-effective slot layout is when the secondary slot is larger than
the primary slot by exactly one sector, although same-sized slots are allowed
as well. The algorithm is limited to support sectors of the same sector layout.
All slot's sectors should be of the same size. This algorithm uses 2 flags per
sector update rather than the 3 flags used by swap using move, which requires
a smaller swap status area size.
When using this algorithm the maximum image size available for the application
will be:
```
maximum-image-size1 = N * slot-sector-size - image-trailer-sectors-size
```
Where:
`N` is the number of sectors in the primary slot.
`image-trailer-sectors-size` is the size of the image trailer rounded up to
the total size of sectors its occupied. For instance if the image-trailer-size
is equal to 1056 B and the sector size is equal to 1024 B, then
`image-trailer-sectors-size` will be equal to 2048 B.
The algorithm does one erase cycle on both the primary and secondary slots
during each swap.
The algorithm is enabled using the `MCUBOOT_SWAP_USING_OFFSET` option.
### [Swap using move (without using scratch)](#image-swap-no-scratch)
This algorithm is an alternative to the swap-using-scratch algorithm.
It uses an additional sector in the primary slot to make swap possible.
The algorithm works as follows:
1. Moves all sectors of the primary slot up by one sector.
Beginning from N=0:
2. Copies the N-th sector from the secondary slot to the N-th sector of the
primary slot.
3. Copies the (N+1)-th sector from the primary slot to the N-th sector of the
secondary slot.
4. Repeats steps 2. and 3. until all the slots' sectors are swapped.
This algorithm is designed so that the higher sector of the primary slot is
used only for allowing sectors to move up. Therefore the most
memory-size-effective slot layout is when the primary slot is larger than
the secondary slot by exactly one sector plus the size of the swap status area,
rounded up to the total size of the sectors it occupies,
although same-sized slots are allowed as well.
The algorithm is limited to support sectors of the same
sector layout. All slot's sectors should be of the same size.
When using this algorithm the maximum image size available for the application
will be:
```
maximum-image-size = (N-1) * slot-sector-size - image-trailer-sectors-size
```
Where:
`N` is the number of sectors in the primary slot.
`image-trailer-sectors-size` is the size of the image trailer rounded up to
the total size of sectors its occupied. For instance if the image-trailer-size
is equal to 1056 B and the sector size is equal to 1024 B, then
`image-trailer-sectors-size` will be equal to 2048 B.
The algorithm does two erase cycles on the primary slot and one on the secondary
slot during each swap. Assuming that receiving a new image by the DFU
application requires 1 erase cycle on the secondary slot, this should result in
leveling the flash wear between the slots.
The algorithm is enabled using the `MCUBOOT_SWAP_USING_MOVE` option.
### [Equal slots (direct-xip)](#direct-xip)
When the direct-xip mode is enabled the active image flag is "moved" between the
slots during image upgrade and in contrast to the above, the bootloader can
run an image directly from either the primary or the secondary slot (without
having to move/copy it into the primary slot). Therefore the image update
client, which downloads the new images must be aware, which slot contains the
active image and which acts as a staging area and it is responsible for loading
the proper images into the proper slot. All this requires that the images be
built to be executed from the corresponding slot. At boot time the bootloader
first looks for images in the slots and then inspects the version numbers in the
image headers. It selects the newest image (with the highest version number) and
then checks its validity (integrity check, signature verification etc.). If the
image is invalid MCUboot erases its memory slot and starts to validate the other
image. After a successful validation of the selected image the bootloader
chain-loads it.
An additional "revert" mechanism is also supported. For more information, please
read the [corresponding section](#direct-xip-revert).
Handling the primary and secondary slots as equals has its drawbacks. Since the
images are not moved between the slots, the on-the-fly image
encryption/decryption can't be supported (it only applies to storing the image
in an external flash on the device, the transport of encrypted image data is
still feasible).
The overwrite and the direct-xip upgrade strategies are substantially simpler to
implement than the image swapping strategy, especially since the bootloader must
work properly even when it is reset during the middle of an image swap. For this
reason, the rest of the document describes its behavior when configured to swap
images during an upgrade.
### [RAM loading](#ram-load)
In ram-load mode the slots are equal. Like the direct-xip mode, this mode
also selects the newest image by reading the image version numbers in the image
headers. But instead of executing it in place, the newest image is copied to the
RAM for execution. The load address, the location in RAM where the image is
copied to, is stored in the image header. The ram-load upgrade mode can be
useful when there is no internal flash in the SoC, but there is a big enough
internal RAM to hold the images. Usually in this case the images are stored
in an external storage device. Execution from external storage has some
drawbacks (lower execution speed, image is exposed to attacks) therefore the
image is always copied to the internal RAM before the authentication and
execution. Ram-load mode requires the image to be built to be executed from
the RAM address range instead of the storage device address range. If
ram-load is enabled then platform must define the following parameters:
```c
#define IMAGE_EXECUTABLE_RAM_START <area_base_addr>
#define IMAGE_EXECUTABLE_RAM_SIZE <area_size_in_bytes>
```
For multiple image load if multiple ram regions are used platform must define
the `MULTIPLE_EXECUTABLE_RAM_REGIONS` flag instead and implement the following
function:
```c
int boot_get_image_exec_ram_info(uint32_t image_id,
uint32_t *exec_ram_start,
uint32_t *exec_ram_size)
```
When ram-load is enabled, the `--load-addr <addr>` option of the `imgtool`
script must also be used when signing the images. This option set the `RAM_LOAD`
flag in the image header which indicates that the image should be loaded to the
RAM and also set the load address in the image header.
When the encryption option is enabled (`MCUBOOT_ENC_IMAGES`) along with ram-load
the image is checked for encryption. If the image is not encrypted, RAM loading
happens as described above. If the image is encrypted, it is copied in RAM at
the provided address and then decrypted. Finally, the decrypted image is
authenticated in RAM and executed.
## [Boot swap types](#boot-swap-types)
When the device first boots under normal circumstances, there is an up-to-date
firmware image in each primary slot, which MCUboot can validate and then
chain-load. In this case, no image swaps are necessary. During device upgrades,
however, new candidate image(s) is present in the secondary slot(s), which
MCUboot must swap into the primary slot(s) before booting as discussed above.
Upgrading an old image with a new one by swapping can be a two-step process. In
this process, MCUboot performs a "test" swap of image data in flash and boots
the new image or it will be executed during operation. The new image can then
update the contents of flash at runtime to mark itself "OK", and MCUboot will
then still choose to run it during the next boot. When this happens, the swap is
made "permanent". If this doesn't happen, MCUboot will perform a "revert" swap
during the next boot by swapping the image(s) back into its original location(s)
, and attempting to boot the old image(s).
Depending on the use case, the first swap can also be made permanent directly.
In this case, MCUboot will never attempt to revert the images on the next reset.
Test swaps are supported to provide a rollback mechanism to prevent devices
from becoming "bricked" by bad firmware. If the device crashes immediately
upon booting a new (bad) image, MCUboot will revert to the old (working) image
at the next device reset, rather than booting the bad image again. This allows
device firmware to make test swaps permanent only after performing a self-test
routine.
On startup, MCUboot inspects the contents of flash to decide for each images
which of these "swap types" to perform; this decision determines how it
proceeds.
The possible swap types, and their meanings, are:
- `BOOT_SWAP_TYPE_NONE`: The "usual" or "no upgrade" case; attempt to boot the
contents of the primary slot.
- `BOOT_SWAP_TYPE_TEST`: Boot the contents of the secondary slot by swapping
images. Unless the swap is made permanent, revert back on the next boot.
- `BOOT_SWAP_TYPE_PERM`: Permanently swap images, and boot the upgraded image
firmware.
- `BOOT_SWAP_TYPE_REVERT`: A previous test swap was not made permanent;
swap back to the old image whose data are now in the secondary slot. If the
old image marks itself "OK" when it boots, the next boot will have swap type
`BOOT_SWAP_TYPE_NONE`.
- `BOOT_SWAP_TYPE_FAIL`: Swap failed because image to be run is not valid.
- `BOOT_SWAP_TYPE_PANIC`: Swapping encountered an unrecoverable error.
The "swap type" is a high-level representation of the outcome of the
boot. Subsequent sections describe how MCUboot determines the swap type from
the bit-level contents of flash.
### [Revert mechanism in direct-xip mode](#direct-xip-revert)
The direct-xip mode also supports a "revert" mechanism which is the equivalent
of the swap mode's "revert" swap. When the direct-xip mode is selected it can be
enabled with the MCUBOOT_DIRECT_XIP_REVERT config option and an image trailer
must also be added to the signed images (the "--pad" option of the `imgtool`
script must be used). For more information on this please read the
[Image Trailer](#image-trailer) section and the [imgtool](imgtool.md)
documentation. Making the images permanent (marking them as confirmed in
advance) is also supported just like in swap mode. The individual steps of the
direct-xip mode's "revert" mechanism are the following:
1. Select the slot which holds the newest potential image.
2. Was the image previously selected to run (during a previous boot)?
+ Yes: Did the image mark itself "OK" (was the self-test successful)?
+ Yes.
- Proceed to step 3.
+ No.
- Erase the image from the slot to prevent it from being selected
again during the next boot.
- Return to step 1 (the bootloader will attempt to select and
possibly boot the previous image if there is one).
+ No.
- Mark the image as "selected" (set the copy_done flag in the trailer).
- Proceed to step 3.
3. Proceed to image validation ...
## [Image trailer](#image-trailer)
For the bootloader to be able to determine the current state and what actions
should be taken during the current boot operation, it uses metadata stored in
the image flash areas. While swapping, some of this metadata is temporarily
copied into and out of the scratch area.
This metadata is located at the end of the image flash areas, and is called an
image trailer. An image trailer has the following structure:
```
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ ~
~ Swap status (BOOT_MAX_IMG_SECTORS * min-write-size * 3) ~
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Encryption key 0 (16 octets) [*] |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 16 octets from Encryption key 0) [*] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Encryption key 1 (16 octets) [*] |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 16 octets from Encryption key 1) [*] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Swap size (4 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 4 octets from Swap size) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Swap info | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Copy done | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Image OK | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 16 octets from MAGIC) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAGIC (16 octets) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
```
[*]: Only present if the encryption option is enabled (`MCUBOOT_ENC_IMAGES`).
The offset immediately following such a record represents the start of the next
flash area.
---
***Note***
*"min-write-size" is a property of the flash hardware. If the hardware*
*allows individual bytes to be written at arbitrary addresses, then*
*min-write-size is 1. If the hardware only allows writes at even addresses,*
*then min-write-size is 2, and so on.*
---
An image trailer contains the following fields:
1. Swap status: A series of records which records the progress of an image
swap. To swap entire images, data are swapped between the two image areas
one or more sectors at a time, like this:
- sector data in the primary slot is copied into scratch, then erased
- sector data in the secondary slot is copied into the primary slot,
then erased
- sector data in scratch is copied into the secondary slot
As it swaps images, the bootloader updates the swap status field in a way that
allows it to compute how far this swap operation has progressed for each
sector. The swap status field can thus used to resume a swap operation if the
bootloader is halted while a swap operation is ongoing and later reset. The
`BOOT_MAX_IMG_SECTORS` value is the configurable maximum number of sectors
MCUboot supports for each image; its value defaults to 128, but allows for
either decreasing this size, to limit RAM usage, or to increase it in devices
that have massive amounts of Flash or very small sized sectors and thus require
a bigger configuration to allow for the handling of all slot's sectors.
The factor of min-write-size is due to the behavior of flash hardware. The factor
of 3 is explained below.
2. Encryption keys: key-encrypting keys (KEKs). These keys are needed for
image encryption and decryption. See the
[encrypted images](encrypted_images.md) document for more information.
3. Swap size: When beginning a new swap operation, the total size that needs
to be swapped (based on the slot with largest image + TLVs) is written to
this location for easier recovery in case of a reset while performing the
swap.
4. Swap info: A single byte which encodes the following information:
- Swap type: Stored in bits 0-3. Indicating the type of swap operation in
progress. When MCUboot resumes an interrupted swap, it uses this field to
determine the type of operation to perform. This field contains one of the
following values in the table below.
- Image number: Stored in bits 4-7. It has always 0 value at single image
boot. In case of multi image boot it indicates, which image was swapped when
interrupt happened. The same scratch area is used during in case of all
image swap operation. Therefore this field is used to determine which image
the trailer belongs to if boot status is found on scratch area when the swap
operation is resumed.
| Name | Value |
| ------------------------- | ----- |
| `BOOT_SWAP_TYPE_TEST` | 2 |
| `BOOT_SWAP_TYPE_PERM` | 3 |
| `BOOT_SWAP_TYPE_REVERT` | 4 |
5. Copy done: A single byte indicating whether the image in this slot is
complete (0x01=done; 0xff=not done).
6. Image OK: A single byte indicating whether the image in this slot has been
confirmed as good by the user (0x01=confirmed; 0xff=not confirmed).
7. MAGIC: A 16-byte field identifying the image trailer layout. It may assume
distinct values depending on the maximum supported write alignment
(`BOOT_MAX_ALIGN`) of the image, as defined by the following construct:
``` c
union boot_img_magic_t
{
struct {
uint16_t align;
uint8_t magic[14];
};
uint8_t val[16];
};
```
If `BOOT_MAX_ALIGN` is **8 bytes**, then MAGIC contains the following 16 bytes:
``` c
const union boot_img_magic_t boot_img_magic = {
.val = {
0x77, 0xc2, 0x95, 0xf3,
0x60, 0xd2, 0xef, 0x7f,
0x35, 0x52, 0x50, 0x0f,
0x2c, 0xb6, 0x79, 0x80
}
};
```
In case `BOOT_MAX_ALIGN` is defined to any value different than **8**, then the maximum
supported write alignment value is encoded in the MAGIC field, followed by a fixed
14-byte pattern:
``` c
const union boot_img_magic_t boot_img_magic = {
.align = BOOT_MAX_ALIGN,
.magic = {
0x2d, 0xe1,
0x5d, 0x29, 0x41, 0x0b,
0x8d, 0x77, 0x67, 0x9c,
0x11, 0x0f, 0x1f, 0x8a
}
};
```
---
***Note***
Be aware that the image trailers make the ending area of the image slot
unavailable for carrying the image data. In particular, the swap status size
could be huge. For example, for 128 slot sectors with a 4-byte alignment,
it would become 1536 B.
---
## [Image trailers](#image-trailers)
At startup, the bootloader determines the boot swap type by inspecting the
image trailers. When using the term "image trailers" what is meant is the
aggregate information provided by both image slot's trailers.
### [New swaps (non-resumes)](#new-swaps-non-resumes)
For new swaps, MCUboot must inspect a collection of fields to determine which
swap operation to perform.
The image trailers records are structured around the limitations imposed by
flash hardware. As a consequence, they do not have a very intuitive design, and
it is difficult to get a sense of the state of the device just by looking at the
image trailers. It is better to map all the possible trailer states to the swap
types described above via a set of tables. These tables are reproduced below.
---
***Note***
*An important caveat about the tables described below is that they must*
*be evaluated in the order presented here. Lower state numbers must have a*
*higher priority when testing the image trailers.*
---
```
State I (swap using offset only)
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | Unset |
copy-done | Any | Set |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_REVERT |
-------------------------------------------------'
State II
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | Unset |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_TEST |
-------------------------------------------------'
State III
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | 0x01 |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_PERM |
-------------------------------------------------'
State IV
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Good | Any |
image-ok | 0xff | Any |
copy-done | 0x01 | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_REVERT |
-------------------------------------------------'
```
Any of the above three states results in MCUboot attempting to swap images.
Otherwise, MCUboot does not attempt to swap images, resulting in one of the
other three swap types, as illustrated by State IV.
```
State V
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Any |
image-ok | Any | Any |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_NONE, |
BOOT_SWAP_TYPE_FAIL, or |
BOOT_SWAP_TYPE_PANIC |
-------------------------------------------------'
```
In State V, when no errors occur, MCUboot will attempt to boot the contents of
the primary slot directly, and the result is `BOOT_SWAP_TYPE_NONE`. If the image
in the primary slot is not valid, the result is `BOOT_SWAP_TYPE_FAIL`. If a
fatal error occurs during boot, the result is `BOOT_SWAP_TYPE_PANIC`. If the
result is either `BOOT_SWAP_TYPE_FAIL` or `BOOT_SWAP_TYPE_PANIC`, MCUboot hangs
rather than booting an invalid or compromised image.
---
***Note***
*An important caveat to the above is the result when a swap is requested*
*and the image in the secondary slot fails to validate, due to a hashing or*
*signing error. This state behaves as State IV with the extra action of*
*marking the image in the primary slot as "OK", to prevent further attempts*
*to swap.*
---
### [Resumed swaps](#resumed-swaps)
If MCUboot determines that it is resuming an interrupted swap (i.e., a reset
occurred mid-swap), it fully determines the operation to resume by reading the
`swap info` field from the active trailer and extracting the swap type from bits
0-3. The set of tables in the previous section are not necessary in the resume
case.
## [High-level operation](#high-level-operation)
With the terms defined, we can now explore the bootloader's operation. First,
a high-level overview of the boot process is presented. Then, the following
sections describe each step of the process in more detail.
Procedure:
1. Inspect swap status region; is an interrupted swap being resumed?
+ Yes: Complete the partial swap operation; skip to step 3.
+ No: Proceed to step 2.
2. Inspect image trailers; is a swap requested?
+ Yes:
1. Is the requested image valid (integrity and security check)?
+ Yes.
a. Perform swap operation.
b. Persist completion of swap procedure to image trailers.
c. Proceed to step 3.
+ No.
a. Erase invalid image.
b. Persist failure of swap procedure to image trailers.
c. Proceed to step 3.
+ No: Proceed to step 3.
3. Boot into image in primary slot.
### [Multiple image boot](#multiple-image-boot)
When the flash contains multiple executable images the bootloader's operation
is a bit more complex but similar to the previously described procedure with
one image. Every image can be updated independently therefore the flash is
partitioned further to arrange two slots for each image.
```
+--------------------+
| MCUboot |
+--------------------+
~~~~~ <- memory might be not contiguous
+--------------------+
| Image 0 |
| primary slot |
+--------------------+
| Image 0 |
| secondary slot |
+--------------------+
~~~~~ <- memory might be not contiguous
+--------------------+
| Image N |
| primary slot |
+--------------------+
| Image N |
| secondary slot |
+--------------------+
| Scratch |
+--------------------+
```
MCUboot is also capable of handling dependencies between images. For example
if an image needs to be reverted it might be necessary to revert another one too
(e.g. due to API incompatibilities) or simply to prevent from being updated
because of an unsatisfied dependency. Therefore all aborted swaps have to be
completed and all the swap types have to be determined for each image before
the dependency checks. Dependency handling is described in more detail in a
following section. The multiple image boot procedure is organized in loops which
iterate over all the firmware images. The high-level overview of the boot
process is presented below.
+ Loop 1. Iterate over all images
1. Inspect swap status region of current image; is an interrupted swap being
resumed?
+ Yes:
+ Review the validity of previously determined swap types
of other images.
+ Complete the partial swap operation.
+ Mark the swap type as `None`.
+ Skip to next image.
+ No: Proceed to step 2.
2. Inspect image trailers in the primary and secondary slot; is an image
swap requested?
+ Yes: Review the validity of previously determined swap types of other
images. Is the requested image valid (integrity and security
check)?
+ Yes:
+ Set the previously determined swap type for the current image.
+ Skip to next image.
+ No:
+ Erase invalid image.
+ Persist failure of swap procedure to image trailers.
+ Mark the swap type as `Fail`.
+ Skip to next image.
+ No:
+ Mark the swap type as `None`.
+ Skip to next image.
+ Loop 2. Iterate over all images
1. Does the current image depend on other image(s)?
+ Yes: Are all the image dependencies satisfied?
+ Yes: Skip to next image.
+ No:
+ Modify swap type depending on what the previous type was.
+ Restart dependency check from the first image.
+ No: Skip to next image.
+ Loop 3. Iterate over all images
1. Is an image swap requested?
+ Yes:
+ Perform image update operation.
+ Persist completion of swap procedure to image trailers.
+ Skip to next image.
+ No: Skip to next image.
+ Loop 4. Iterate over all images
1. Validate image in the primary slot (integrity and security check) or
at least do a basic sanity check to avoid booting into an empty flash
area.
+ Boot into image in the primary slot of the 0th image position\
(other image in the boot chain is started by another image).
### [Multiple image boot for RAM loading and direct-xip](#multiple-image-boot-for-ram-loading-and-direct-xip)
The operation of the bootloader is different when the ram-load or the
direct-xip strategy is chosen. The flash map is very similar to the swap
strategy but there is no need for Scratch area.
+ Loop 1. Until all images are loaded and all dependencies are satisfied
1. Subloop 1. Iterate over all images
+ Does any of the slots contain an image?
+ Yes:
+ Choose the newer image.
+ Copy it to RAM in case of ram-load strategy.
+ Validate the image (integrity and security check).
+ If validation fails delete the image from flash and try the other
slot. (Image must be deleted from RAM too in case of ram-load
strategy.)
+ No: Return with failure.
2. Subloop 2. Iterate over all images
+ Does the current image depend on other image(s)?
+ Yes: Are all the image dependencies satisfied?
+ Yes: Skip to next image.
+ No:
+ Delete the image from RAM in case of ram-load strategy, but
do not delete it from flash.
+ Try to load the image from the other slot.
+ Restart dependency check from the first image.
+ No: Skip to next image.
+ Loop 2. Iterate over all images
+ Increase the security counter if needed.
+ Do the measured boot and the data sharing if needed.
+ Boot the loaded slot of image 0.
## [Image swapping](#image-swapping)
The bootloader swaps the contents of the two image slots for two reasons:
* User has issued a "set pending" operation; the image in the secondary slot
should be run once (state I) or repeatedly (state II), depending on
whether a permanent swap was specified.
* Test image rebooted without being confirmed; the bootloader should
revert to the original image currently in the secondary slot (state III).
If the image trailers indicates that the image in the secondary slot should be
run, the bootloader needs to copy it to the primary slot. The image currently
in the primary slot also needs to be retained in flash so that it can be used
later. Furthermore, both images need to be recoverable if the bootloader
resets in the middle of the swap operation. The two images are swapped
according to the following procedure:
1. Determine if both slots are compatible enough to have their images swapped.
To be compatible, both have to have only sectors that can fit into the
scratch area and if one of them has larger sectors than the other, it must
be able to entirely fit some rounded number of sectors from the other slot.
In the next steps we'll use the terminology "region" for the total amount of
data copied/erased because this can be any amount of sectors depending on
how many the scratch is able to fit for some swap operation.
2. Iterate the list of region indices in descending order (i.e., starting
with the greatest index); only regions that are predetermined to be part of
the image are copied; current element = "index".
+ a. Erase scratch area.
+ b. Copy secondary_slot[index] to scratch area.
- If this is the last region in the slot, scratch area has a temporary
status area initialized to store the initial state, because the
primary slot's last region will have to be erased. In this case,
only the data that was calculated to amount to the image is copied.
- Else if this is the first swapped region but not the last region in
the slot, initialize the status area in primary slot and copy the
full region contents.
- Else, copy entire region contents.
+ c. Write updated swap status (i).
+ d. Erase secondary_slot[index]
+ e. Copy primary_slot[index] to secondary_slot[index] according to amount
previosly copied at step b.
- If this is not the last region in the slot, erase the trailer in the
secondary slot, to always use the one in the primary slot.
+ f. Write updated swap status (ii).
+ g. Erase primary_slot[index].
+ h. Copy scratch area to primary_slot[index] according to amount
previously copied at step b.
- If this is the last region in the slot, the status is read from
scratch (where it was stored temporarily) and written anew in the
primary slot.
+ i. Write updated swap status (iii).
3. Persist completion of swap procedure to the primary slot image trailer.
The additional caveats in step 2f are necessary so that the secondary slot image
trailer can be written by the user at a later time. With the image trailer
unwritten, the user can test the image in the secondary slot
(i.e., transition to state I).
---
***Note***
*If the region being copied contains the last sector, then swap status is*
*temporarily maintained on scratch for the duration of this operation, always*
*using the primary slot's area otherwise.*
---
***Note***
*The bootloader tries to copy only used sectors (based on largest image*
*installed on any of the slots), minimizing the amount of sectors copied and*
*reducing the amount of time required for a swap operation.*
---
The particulars of step 3 vary depending on whether an image is being tested,
permanently used, reverted or a validation failure of the secondary slot
happened when a swap was requested:
* test: