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 and v2.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)
  • 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

  1. Add C source files and dependencies for your chosen library.
  2. Implement interface functions to link application code to the library. Expect undefined reference warnings during this step.
  3. Resolve link-time issues by creating a port file (e.g., xxx_port.c) and writing stub functions for necessary dependencies.
  4. 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 by MCUBOOT_.
  • 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.
  • After bootloader operations, the boot_go function initializes boot_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 and free 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

  1. 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 the permanent parameter.
  • permanent = 1: The swap is permanent.
  • permanent = 0: The swap is temporary.
  1. If you set permanent to 0 (temporary) and do not call the boot_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:

MCUboot Kconfig on GitHub

To customize the configuration, use the following commands:

$ ninja menuconfig 
$ ninja flash 
$ ninja debug