mcuboot/docs/design.txt

629 lines
27 KiB
Plaintext

#
# 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.
#
****** BOOT LOADER
*** 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 boot loader. 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.
Boot loader functionality is separated in this manner to enable unit testing of
the boot loader. A library can be unit tested, but an application can't.
Therefore, functionality is delegated to the bootutil library when possible.
*** LIMITATIONS
The boot loader 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
The following definitions describe the image format.
#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 _pad2;
uint32_t ih_img_size; /* Does not include header. */
uint32_t ih_flags; /* IMAGE_F_[...]. */
struct image_version ih_ver;
uint32_t _pad3;
};
/** 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_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 */
#define IMAGE_TLV_ECDSA256 0x22 /* ECDSA of hash output */
Optional type-length-value records (TLVs) containing image metadata are placed
after the end of the image.
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
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 boot loader uses the following flash area IDs:
#define FLASH_AREA_BOOTLOADER 0
#define FLASH_AREA_IMAGE_0 1
#define FLASH_AREA_IMAGE_1 2
#define FLASH_AREA_IMAGE_SCRATCH 3
The bootloader area contains the bootloader image itself. The other areas are
described in subsequent sections.
*** IMAGE SLOTS
A portion of the flash memory is partitioned into two image slots: a primary
slot (0) and a secondary slot (1). The boot loader 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. If the boot loader 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.
In addition to the two image slots, the boot loader requires a scratch area to
allow for reliable image swapping.
The overwrite upgrade strategy is 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.
*** BOOT SWAP TYPES
When the device first boots under normal circumstances, there is an up-to-date
firmware image in slot 0, which mcuboot can validate and then chain-load. In
this case, no image swaps are necessary. During device upgrades, however, new
candidate images are present in slot 1, which mcuboot must swap into slot 0
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. 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 images back into their original locations, and attempting to boot the old
image.
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 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 slot 0.
- BOOT_SWAP_TYPE_TEST: Boot the contents of slot 1 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 slot 1. 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.
*** 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 (128 * min-write-size * 3) ~
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Swap size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding (4 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Copy done | 0xff padding (7 octets) ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Image OK | 0xff padding (7 octets) ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ MAGIC (16 octets) ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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 slot 0 is copied into scratch, then erased
- sector data in slot 1 is copied into slot 0, then erased
- sector data in scratch is copied into slot 1
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
factor of 128 is the maximum number of sectors mcuboot supports for each image;
its value is a bootloader design decision. The factor of min-write-sz is due to
the behavior of flash hardware. The factor of 3 is explained below.
2. 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.
3. Copy done: A single byte indicating whether the image in this slot is
complete (0x01=done; 0xff=not done).
4. 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).
5. MAGIC: The following 16 bytes, written in host-byte-order:
const uint32_t boot_img_magic[4] = {
0xf395c277,
0x7fefd260,
0x0f505235,
0x8079b62c,
};
*** IMAGE TRAILERS
At startup, the boot loader 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.
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
| slot-0 | slot-1 |
-----------------+--------+--------|
magic | Any | Good |
image-ok | Any | Unset |
copy-done | Any | Any |
-----------------+--------+--------'
result: BOOT_SWAP_TYPE_TEST |
-----------------------------------'
State II
| slot-0 | slot-1 |
-----------------+--------+--------|
magic | Any | Good |
image-ok | Any | 0x01 |
copy-done | Any | Any |
-----------------+--------+--------'
result: BOOT_SWAP_TYPE_PERM |
-----------------------------------'
State III
| slot-0 | slot-1 |
-----------------+--------+--------|
magic | Good | Unset |
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 IV
| slot-0 | slot-1 |
-----------------+--------+--------|
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 IV, when no errors occur, mcuboot will attempt to boot the contents of
slot 0 directly, and the result is BOOT_SWAP_TYPE_NONE. If the image in slot 0
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 slot 1 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 slot 0 as "OK", to prevent further attempts to swap.
*** HIGH-LEVEL OPERATION
With the terms defined, we can now explore the boot loader'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:
A. Inspect swap status region; is an interrupted swap being resumed?
Yes: Complete the partial swap operation; skip to step C.
No: Proceed to step B.
B. 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 C.
No.
a. Erase invalid image.
b. Persist failure of swap procedure to image trailers.
c. Proceed to step C.
No: Proceed to step C.
C. Boot into image in slot 0.
*** IMAGE SWAPPING
The boot loader swaps the contents of the two image slots for two reasons:
* User has issued a "set pending" operation; the image in slot-1 should be
run once (state II) or repeatedly (state III), depending on whether a
permanent swap was specified.
* Test image rebooted without being confirmed; the boot loader should
revert to the original image currently in slot-1 (state IV).
If the image trailers indicates that the image in the secondary slot should be
run, the boot loader 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 boot loader
resets in the middle of the swap operation. The two images are swapped
according to the following procedure:
1. Determine how many flash sectors each image slot consists of. This
number must be the same for both slots.
2. Iterate the list of sector indices in descending order (i.e., starting
with the greatest index); current element = "index".
b. Erase scratch area.
c. Copy slot0[index] to scratch area.
- If these are the last sectors (i.e., first swap being perfomed),
copy the full sector *except* the image trailer.
- Else, copy entire sector contents.
d. Write updated swap status (i).
e. Erase slot1[index]
f. Copy slot0[index] to slot1[index]
- If these are the last sectors (i.e., first swap being perfomed),
copy the full sector *except* the image trailer.
- Else, copy entire sector contents.
g. Write updated swap status (ii).
h. Erase slot0[index].
i. Copy scratch area slot0[index].
- If these are the last sectors (i.e., first swap being perfomed),
copy the full sector *except* the image trailer.
- Else, copy entire sector contents.
j. Write updated swap status (iii).
3. Persist completion of swap procedure to slot 0 image trailer.
The additional caveats in step 2f are necessary so that the slot 1 image
trailer can be written by the user at a later time. With the image trailer
unwritten, the user can test the image in slot 1 (i.e., transition to state
II).
Note1: If the sector being copied is the last sector, then swap status is
temporarily maintained on scratch for the duration of this operation, always
using slot0's area otherwise.
Note2: 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 slot 1 happened when a
swap was requested:
* test:
o Write slot0.copy_done = 1
(swap caused the following values to be written:
slot0.magic = BOOT_MAGIC
slot0.image_ok = Unset)
* permanent:
o Write slot0.copy_done = 1
(swap caused the following values to be written:
slot0.magic = BOOT_MAGIC
slot0.image_ok = 0x01)
* revert:
o Write slot0.copy_done = 1
o Write slot0.image_ok = 1
(swap caused the following values to be written:
slot0.magic = BOOT_MAGIC)
* failure to validate slot 1:
o Write slot0.image_ok = 1
After completing the operations as described above the image in slot 0 should
be booted.
*** SWAP STATUS
The swap status region allows the boot loader to recover in case it restarts in
the middle of an image swap operation. The swap status region consists of a
series of single-byte records. These records are written independently, and
therefore must be padded according to the minimum write size imposed by the
flash hardware. In the below figure, a min-write-size of 1 is assumed for
simplicity. The structure of the swap status region is illustrated below. In
this figure, a min-write-size of 1 is assumed for simplicity.
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec127,state 0 |sec127,state 1 |sec127,state 2 |sec126,state 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec126,state 1 |sec126,state 2 |sec125,state 0 |sec125,state 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec125,state 2 | |
+-+-+-+-+-+-+-+-+ +
~ ~
~ [Records for indices 124 through 1 ~
~ ~
~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ |sec000,state 0 |sec000,state 1 |sec000,state 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The above is probably not helpful at all; here is a description in English.
Each image slot is partitioned into a sequence of flash sectors. If we were to
enumerate the sectors in a single slot, starting at 0, we would have a list of
sector indices. Since there are two image slots, each sector index would
correspond to a pair of sectors. For example, sector index 0 corresponds to
the first sector in slot 0 and the first sector in slot 1. Furthermore, we
impose a limit of 128 indices. If an image slot consists of more than 128
sectors, the flash layout is not compatible with this boot loader. Finally,
reverse the list of indices such that the list starts with index 127 and ends
with 0. The swap status region is a representation of this reversed list.
During a swap operation, each sector index transitions through four separate
states:
0. slot 0: image 0, slot 1: image 1, scratch: N/A
1. slot 0: image 0, slot 1: N/A, scratch: image 1 (1->s, erase 1)
2. slot 0: N/A, slot 1: image 0, scratch: image 1 (0->1, erase 0)
3. slot 0: image 1, slot 1: image 0, scratch: N/A (s->0)
Each time a sector index transitions to a new state, the boot loader writes a
record to the swap status region. Logically, the boot loader only needs one
record per sector index to keep track of the current swap state. However, due
to limitations imposed by flash hardware, a record cannot be overwritten when
an index's state changes. To solve this problem, the boot loader uses three
records per sector index rather than just one.
Each sector-state pair is represented as a set of three records. The record
values map to the above four states as follows
| rec0 | rec1 | rec2
--------+------+------+------
state 0 | 0xff | 0xff | 0xff
state 1 | 0x01 | 0xff | 0xff
state 2 | 0x01 | 0x02 | 0xff
state 3 | 0x01 | 0x02 | 0x03
The swap status region can accommodate 128 sector indices. Hence, the size of
the region, in bytes, is 128 * min-write-size * 3. The number 128 is chosen
somewhat arbitrarily and will likely be made configurable. The only
requirement for the index count is that is is great enough to account for a
maximum-sized image (i.e., at least as great as the total sector count in an
image slot). If a device's image slots use less than 128 sectors, the first
record that gets written will be somewhere in the middle of the region. For
example, if a slot uses 64 sectors, the first sector index that gets swapped is
63, which corresponds to the exact halfway point within the region.
Note: since the scratch area only ever needs to record swapping of the last
sector, it uses at most min-write-size * 3 bytes for its own status area.
*** RESET RECOVERY
If the boot loader resets in the middle of a swap operation, the two images may
be discontiguous in flash. Bootutil recovers from this condition by using the
image trailers to determine how the image parts are distributed in flash.
The first step is determine where the relevant swap status region is located.
Because this region is embedded within the image slots, its location in flash
changes during a swap operation. The below set of tables map image trailers
contents to swap status location. In these tables, the "source" field
indicates where the swap status region is located.
| slot-0 | scratch |
----------+------------+------------|
magic | Good | Any |
copy-done | 0x01 | N/A |
----------+------------+------------'
source: none |
------------------------------------'
| slot-0 | scratch |
----------+------------+------------|
magic | Good | Any |
copy-done | 0xff | N/A |
----------+------------+------------'
source: slot 0 |
------------------------------------'
| slot-0 | scratch |
----------+------------+------------|
magic | Any | Good |
copy-done | Any | N/A |
----------+------------+------------'
source: scratch |
------------------------------------'
| slot-0 | scratch |
----------+------------+------------|
magic | Unset | Any |
copy-done | 0xff | N/A |
----------+------------+------------|
source: slot 0 |
------------------------------------+------------------------------+
This represents one of two cases: |
o No swaps ever (no status to read, so no harm in checking). |
o Mid-revert; status in slot 0. |
For this reason we assume slot 0 as source, to trigger a check |
of the status area and find out if there was swapping under way. |
-------------------------------------------------------------------'
If the swap status region indicates that the images are not contiguous,
bootutil completes the swap operation that was in progress when the system was
reset. In other words, it applies the procedure defined in the previous
section, moving image 1 into slot 0 and image 0 into slot 1. If the boot
status file indicates that an image part is present in the scratch area, this
part is copied into the correct location by starting at step e or step h in the
area-swap procedure, depending on whether the part belongs to image 0 or image
1.
After the swap operation has been completed, the boot loader proceeds as though
it had just been started.
*** INTEGRITY CHECK
An image is checked for integrity immediately before it gets copied into the
primary slot. If the boot loader doesn't perform an image swap, then it can
perform an optional integrity check of the image in slot0 if
MCUBOOT_VALIDATE_SLOT0 is set, otherwise it doesn't perform an integrity check.
During the integrity check, the boot loader verifies the following aspects of
an image:
* 32-bit magic number must be correct (0x96f3b83d).
* Image must contain an image_tlv_info struct, identified by its magic
(0x6907) exactly following the firmware (hdr_size + img_size).
* Image must contain a SHA256 TLV.
* Calculated SHA256 must match SHA256 TLV contents.
* Image *may* contain a signature TLV. If it does, it must also have a
KEYHASH TLV with the hash of the key that was used to sign. The list of
keys will then be iterated over looking for the matching key, which then
will then be used to verify the image contents.
*** SECURITY
As indicated above, the final step of the integrity check is signature
verification. The boot loader can have one or more public keys embedded in it
at build time. During signature verification, the boot loader verifies that an
image was signed with a private key that corresponds to the embedded keyhash
TLV.
For information on embedding public keys in the boot loader, as well as
producing signed images, see: doc/signed_images.md