Mistake on this page? Email us

Porting a new target for Mbed OS using external storage with secure storage (KVStore)

This guide explains how to port Device Management Client using external storage to store client credentials and firmware update images. The K64F board is used as an example.

Configuring the bootloader to support Device Management Client

How the bootloader works with Device Management Client

The bootloader needs to:

  • Know the location of the active application and its header.
  • Know the location of firmware candidates and their headers.

The bootloader and Update client need to perform security checks and version validations for the firmware update. This means that they need to exchange metadata for the stored and active firmware. The metadata header is the bootloader update interface. Each stage of the boot sequence leading up to and including the application (except the root bootloader) is paired with a metadata header (containing data such as version, size, and hash).

There are two header formats:

  • Internal headers.
  • External headers.

The external format is meant for firmware on external storage, which is assumed to be insecure. Therefore, the external header format contains extra security information to prevent tampering.

The steps to add a new target are:

  1. Choose the locations of the KVStore (internal + external).
  2. Choose the location of active header and firmware.
  3. Choose the firmware candidate storage area location and format.

Choose the location of active header and firmware on the target board

In this configuration:

  • The firmware storage is on an external SD card block device.
  • Root of Trust is obtained from an internal flash in KVStore.

The flash regions are as follows:

    +--------------------------+
    |                          |
    |                          |
    |                          |
    |        Active App        |
    |                          |
    |                          |
    |                          |
    +--------------------------+ <-+ mbed-bootloader.application-start-address
    |Active App Metadata Header|
    +--------------------------+ <-+ update-client.application-details
    |                          |
    |         KVSTORE          |
    |                          |
    +--------------------------+ <-+ storage_tdb_internal.internal_base_address
    |                          |
    |        Bootloader        |
    |                          |
    +--------------------------+ <-+ 0

  • The bootloader is allocated in the first 32 KiB of space at the start of the flash.
  • The KVStore is allocated after the bootloader. It needs to occupy at least two consecutive erase sectors on the internal flash.
    • In this example, 2 x 4 KiB is allocated for KVStore. KVStore internally allocates two equal-sized banks on the reserved area.
  • The active app metadata header must start on a flash erase boundary. Here, it starts from 40 KiB. This header size is always 1 KiB.
  • The active application must start on a vector table size aligned after the metadata header. Here, it starts from 41 KiB.

Firmware candidate storage area location and format

A storage using a block device is the default implementation for Device Management Client. This uses the Mbed OS block device API to store the candidate firmware and header. This can be on any block device storage supported by Mbed OS. The expected layout is:

    +--------------------------+<-+ End of block device
    |                          |
    |                          |
    |                          |
    |                          |
    |                          |
    +--------------------------+<-+ update-client.storage-size + update-client.storage-address
    |                          |
    |   Firmware candidate 0   |
    |                          |
    +--------------------------+
    |   Firmware candidate 0   |
    |     metadata header      |
    +--------------------------+ <-+ update-client.storage-address
    |    KVStore external      |
    |         storage          |
    +--------------------------+ <-+ Start of block device (such as 0x0), storage_filesystem.internal_base_address

Checking target support by Mbed OS

Check whether Mbed OS already supports the target. See the targets.json file in the mbed-os code base. The file is at mbed-os/targets/targets.json. If your target is not listed in the file, follow the instructions in the Mbed OS porting guide. If the bootloader_supported key is set to false or does not exist, enable bootloader support.

If Mbed OS already supports the target with the bootloader functionality, continue to Creating the bootloader binary for a new target.

Add bootloader support for Mbed OS

Read the instructions for creating and using the bootloader. Detailed information on the bootloader parameters is also available in the bootloader configuration documentation.

For comparison, you can find an example configuration change for NUCLEO-F411RE target file.

Creating the bootloader binary for a new target

Here is how to create the bootloader binary:

  1. Clone the mbed-bootloader repository.
  2. Add the new target and macro definitions to the mbed_app.json file. This requires mbed-bootloader v4.0.0 or later.

Configuration for K64F bootloader:

"macros": [
        "ARM_UC_USE_PAL_BLOCKDEVICE=1",
        "MBED_CLOUD_CLIENT_UPDATE_STORAGE=ARM_UCP_FLASHIAP_BLOCKDEVICE",
        "MBED_BOOTLOADER_SIZE=(32*1024)",
        "MBED_BOOTLOADER_ACTIVE_HEADER_REGION_SIZE=1024",
    ],
    "target_overrides": {
        "*": {
            "platform.use-mpu"                         : false,
            "platform.stdio-baud-rate"                 : 115200,
            "platform.stdio-flush-at-exit"             : false,
            "update-client.storage-address"            : "(1024*1024*64)",
            "update-client.storage-size"               : "((MBED_ROM_START + MBED_ROM_SIZE - MBED_CONF_MBED_BOOTLOADER_APPLICATION_START_ADDRESS) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)",
            "update-client.storage-locations"          : 1,
            "update-client.firmware-header-version"    : "2",
            "target.components_add"                    : ["SD"],
            "sd.CRC_ENABLED"                           : 0,
            "storage.storage_type"                     : "FILESYSTEM",
            "storage_filesystem.internal_base_address" : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
            "update-client.application-details"        : "(MBED_CONF_STORAGE_FILESYSTEM_INTERNAL_BASE_ADDRESS + MBED_CONF_STORAGE_FILESYSTEM_RBP_INTERNAL_SIZE)",
            "mbed-bootloader.application-start-address": "(MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS + MBED_BOOTLOADER_ACTIVE_HEADER_REGION_SIZE)"
        },
        "K64F": {
            "storage_filesystem.rbp_internal_size"     : "(2*4*1024)"
        }
    }
}
  1. Compile the project:
mbed compile -m K64F -t GCC_ARM --profile tiny.json
  1. Create a new target configuration for the application:
   "macros": [
        "MBED_BOOTLOADER_SIZE=(32*1024)",
        "ARM_UC_USE_PAL_BLOCKDEVICE=1"
    ],
        "K64F": {
            "target.features_add"                       : ["BOOTLOADER", "STORAGE"],
            "target.bootloader_img"                     : "tools/mbed-bootloader-k64f-block_device-kvstore-v4.0.0.bin",
            "target.header_offset"                      : "0xa000",
            "target.app_offset"                         : "0xa400",
            "mbed-cloud-client.external-sst-support"    : 1,
            "target.components_add"                     : ["SD"],
            "update-client.bootloader-details"          : "0x00007188",
            "update-client.application-details"         : "(40*1024)",
            "update-client.storage-address"             : "(1024*1024*64)",
            "update-client.storage-size"                : "((MBED_ROM_START + MBED_ROM_SIZE - APPLICATION_ADDR) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)",
            "update-client.storage-locations"           : 1,
            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP_BLOCKDEVICE",
            "storage_filesystem.internal_base_address"  : "(MBED_BOOTLOADER_SIZE)",
            "storage_filesystem.rbp_internal_size"      : "(8*1024)",
            "storage_filesystem.external_base_address"  : "(0x0)",
            "storage_filesystem.external_size"          : "(1024*1024*64)",
            "storage.storage_type"                      : "FILESYSTEM",
            "storage_filesystem.filesystem"             : "LITTLE",
            "storage_filesystem.blockdevice"            : "SD"
        }

The configuration options:

Option in example Description
"target.header_offset" : "0xa000" Starting address for update header.
"target.app_offset" : "0xa400" Starting address for application.
"target.bootloader_img" : "tools/mbed-bootloader-k64f-block_device-kvstore-v4.0.0.bin" Bootloader image provided via application.
"storage_filesystem.internal_base_address" : "(MBED_BOOTLOADER_SIZE)" The address where the internal FlashIAP block device starts.
"storage_filesystem.rbp_internal_size" : "(8*1024)" The size in bytes for the internal FlashIAP block device.
"storage.storage_type" : "FILESYSTEM" Use filesystem for KVStore.
"update-client.application-details" : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)" Address to which the metadata header of the active firmware is written. Must align to flash erase boundary.
"update-client.storage-address" : "(MBED_ROM_START + FLASH_BANK_SIZE + KVSTORE_SIZE)" Start address for update image candidate storage.
"update-client.storage-size" : "(FLASH_BANK_SIZE - KVSTORE_SIZE)" The total size for the area used for storing all the update candidate images.
"update-client.storage-locations" : 1 Number of update candidate images that can be stored.
"mbed-bootloader.application-start-address" : "(MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS + MBED_BOOTLOADER_ACTIVE_HEADER_REGION_SIZE)" Address at which the application starts. Must align to vector table size boundary and flash write page boundary.

Read the KVStore documentation and API reference for more information.