MCUBoot
Bootloader Design
MCUBoot consists of two components:
- bootutil library (located in
boot/bootutil
) - boot application (platform-specific, located in
boot/
for each port)
Overview
The bootutil library implements most bootloader functions, with the exception of the final step—jumping to the main application. This final step is performed by the boot application. Separating functionality this way facilitates unit testing for the bootloader, as libraries can be tested while applications generally cannot. Consequently, the bootutil library includes as much functionality as possible.
Limitations
- Designed to run from flash memory.
- Operates with a fixed address and is not address-independent.
General Features of MCUBoot
- Provides a platform-independent bootloader by sharing the bootloader layer across multiple platforms.
- Divides flash into two slots and one scratch area.
- Validates images before boot using RSA or ECDSA signatures.
- Upgrades if Slot 1 contains a newer and valid image compared to Slot 0.
- Supports upgrade methods: overwrite or swap.
- Allows multiple images by partitioning flash, enabling support for systems with multiple MCUs, such as secure and non-secure MCUs, using a single bootloader.
- Enables version dependencies, such as requiring an update to be between specific versions (e.g.,
v1.1
andv2.3
). - Only boots cryptographically verified images, providing secure bootloader functionality.
- Can recover and resume operations after unexpected reboots and provides mechanisms to recover from corrupted images.
Image Slots
Flash memory is divided into multiple image areas, each consisting of two slots:
- Primary slot: Location where the binary is executed.
- Secondary slot: Temporary storage for a new image before it is loaded.
1. Overwrite Mode (MCUBOOT_OVERWRITE_ONLY
)
In this mode, when an update is requested, the primary slot is erased, and the image in the secondary slot is copied to the primary slot.
2. Swap Mode
Allows recovery to a previous image if the newly loaded image fails.
- The bootloader only executes images from the primary slot, requiring images to be built for execution from fixed addresses.
- If an image in the secondary slot needs to be executed, the bootloader must either copy it to the primary slot or swap it with the image in the primary slot. This choice is made at compile time.
- Swap Advantages: Ensures safety by enabling rollback to a previous version if something goes wrong.
- Swap Disadvantages: Adds complexity to the bootloader system, accelerates flash wear, and requires support for downgrades.
2.1 MCUBOOT_SWAP_USING_MOVE
This strategy relies on an extra sector. The bootloader shifts the binary in the primary slot by one sector and swaps sectors between the primary and secondary slots.
2.2 MCUBOOT_SWAP_USING_SCRATCH
This strategy relies on a scratch area in flash memory. Sectors are temporarily stored in the scratch area during swapping between the primary and secondary slots.
- Scratch size should at least accommodate the largest sector being swapped.
- Larger scratch areas distribute flash wear more evenly, reducing degradation over time.
Alternative Recovery
Instead of swapping, a simpler recovery mechanism involves using MCUBOOT_OVERWRITE_ONLY
and storing a recovery image elsewhere in flash. If an upgrade fails, the recovery image can be copied to the secondary slot for loading.
Cryptographic Backends
- MCUBoot does not include its own cryptographic library but links to an existing implementation.
- Supported libraries include:
- Tinycrypt (
MCUBOOT_USE_TINYCRYPT
) - Mbed TLS (
MCUBOOT_USE_MBED_TLS
)
- Tinycrypt (
- Using the
MCUBOOT_VALIDATE_PRIMARY_SLOT
flag, MCUBoot validates images during every boot. - For large images or low-frequency MCUs, using a cryptographic accelerator can improve boot times. Start with a software-based library to measure boot duration and decide if hardware acceleration is needed.
Image Binaries
- MCUBoot binary images wrap original binary files with a header and a trailer.
- Use the
imgtool.py
script to create MCUBoot image binaries. - Header: Contains metadata such as size and version (
include/bootutil/image.h
). - Trailer: Includes Type-Length-Value (TLV) pairs that indicate how the image is signed or encrypted. Users can also add custom information using TLVs, providing flexibility and avoiding unnecessary reserved fields.
Fault Tolerance
- Metadata for staged images and upgrade tracking is stored in the Image Trailer at the end of each slot.
- During boot, MCUBoot checks upgrade progress and swap status to resume or roll back as needed.
- Determines upgrade eligibility by checking swap info, copy done, and image OK statuses.
Porting MCUBoot
General Steps
- Add C source files and dependencies for your chosen library.
- Implement interface functions to link application code to the library. Expect undefined reference warnings during this step.
- Resolve link-time issues by creating a port file (e.g.,
xxx_port.c
) and writing stub functions for necessary dependencies. - Replace stub functions with implementations tailored to your system.
Specific Steps for MCUBoot
- Include configuration files (e.g.,
mcuboot_config/mcuboot_config.h
) with options prefixed byMCUBOOT_
. - Implement a flash API to perform operations like reading, writing, and erasing sectors.
- Provide a cryptographic library. Supported options include Mbed TLS and Tinycrypt.
Application Integration
MCUBoot is viewed as a library by the target OS. The entry point is the following function:
int boot_go(struct boot_rsp *rsp);
- Defined in
boot/bootutil/loader.c
. - The
struct boot_rsp
parameter includes image header and flash offset details.
- Defined in
After bootloader operations, the
boot_go
function initializesboot_rsp
and jumps to the firmware.
Configuration File
- Essential for boot settings, defined in
mcuboot_config/mcuboot_config.h
. - A template is available at
samples/mcuboot_config/mcuboot_config.template.h
.
Flash Map
Describes flash partitioning to the bootloader.
Consists of
struct flash_area
entries:struct flash_area { uint8_t fa_id; // Slot/scratch identifier uint8_t fa_device_id; // Device ID uint16_t pad16; uint32_t fa_off; // Flash offset uint32_t fa_size; // Sector size };
Example
fa_id
definitions:#define FLASH_AREA_BOOTLOADER 0 #define FLASH_AREA_IMAGE_PRIMARY 1 #define FLASH_AREA_IMAGE_SECONDARY 2 #define FLASH_AREA_IMAGE_SCRATCH 3
Flash area API functions to implement:
int flash_area_open(uint8_t id, const struct flash_area **); void flash_area_close(const struct flash_area *); int flash_area_read(const struct flash_area *, uint32_t off, void *dst, uint32_t len); int flash_area_write(const struct flash_area *, uint32_t off, const void *src, uint32_t len); int flash_area_erase(const struct flash_area *, uint32_t off, uint32_t len); uint8_t flash_area_align(const struct flash_area *); uint8_t flash_area_erased_val(const struct flash_area *); int flash_area_get_sectors(int fa_id, uint32_t *count, struct flash_sector *sectors);
Memory Management for Mbed TLS
- Mbed TLS uses
calloc
andfree
for dynamic memory allocation. - Ensure your system provides these functions if Mbed TLS is used for cryptography.
mbed TLS for Memory Management
mbed TLS uses the calloc
and free
functions for dynamic memory allocation. If you intend to use mbed TLS for cryptographic purposes, your system must provide implementations of these functions. To configure which functions are called for memory allocation in mbed TLS, you can use the following function:
int mbedtls_platform_set_calloc_free(void *(*calloc_func)(size_t, size_t), void (*free_func)(void *));
If your platform already provides calloc
and free
functions, you can use them directly without additional configuration.
Triggering Firmware Upgrades from Main Application
- boot_set_pending(int permanent):
Requests an upgrade of the primary slot with the data from the secondary slot (i.e., a swap operation). The swap can be either permanent or temporary, determined by thepermanent
parameter.
permanent = 1
: The swap is permanent.permanent = 0
: The swap is temporary.
- If you set
permanent
to0
(temporary) and do not call theboot_set_confirmed()
function later, the system will revert to the previous image on the next reset.
Building and Flashing MCUboot
To build and flash MCUboot, use the following commands:
$ west build -b <board> -d build_mcuboot bootloader/mcuboot/boot/zephyr west flash -d build_mcuboot
MCUboot Configuration
The best wrapper example for MCUboot configuration is the Kconfig
file (written for Zephyr RTOS). You can find it here:
To customize the configuration, use the following commands:
$ ninja menuconfig
$ ninja flash
$ ninja debug