Mistake on this page? Email us

Porting a new target for Mbed OS using internal flash for secure storage (KVStore)

This guide explains how to port Device Management Client using the internal flash only for persistent storage. The NUCLEO-F429ZI board is used as an example.

Requirements

  • Dual-bank flash layout is mandatory.

  • Some boards default to single-bank mode. See the board documentation on how to change the configurations.

    For example, the STM32F7 series of boards default to single-bank mode. You can use ST-Link utility to change the board to dual-bank mode by enabling the nDBANK byte option and compiling the bootloader and application with FLASH_DUAL_BANK flags. Always ensure compatibility and instructions from the board-specific documentation. The flash layout also differs between single- and dual-bank configurations.

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 location of the KVStore (internal).
  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

This configuration takes advantage of the fact that most MCUs have 2 banks of flash which can be read from and written to independently of each other. Hence we put all executables on the first bank, and all storage on the second bank so that reading and writing storage does not halt execution.

For Nucleo F429ZI, it is important to consider the varied sector sizing. Both banks have following layout: 4 x 16 KiB, 1 x 64 KiB, 7 x 128 KiB. For net storage of 1 MB per bank.

The regions are as follows:

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

Bank1:

  • The bootloader is allocated in the first 32 KiB of space at the start of the flash.
  • The active app metadata header must start on a flash erase boundary. Here, it starts from 32 KiB. This header is 1 KiB.
  • The active application must start after the metadata header 1 KiB section. Here, it starts from 33 KiB.

Bank2:

  • The KVStore is allocated to the beginning of the second memory bank. It needs to occupy at least two consecutive erase sectors on the internal flash.
    • The first five sections on the board (4 x 16 KiB + 64 KiB = 128 KiB) are allocated for KVStore. KVStore internally allocates two equal-sized banks on the reserved area.
  • Firmware candidate storage - The rest of the second bank of flash is allocated for storing the firmware candidate image.

Checking target support by Mbed OS

Check whether the target is already supported by Mbed OS. 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 the 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.

See 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 configs/internal_flash_no_rot.json. This requires mbed-bootloader v4.0.0 or later.

Configuration for Nucleo F429ZI:

"macros": [
        "MBED_CLOUD_CLIENT_UPDATE_STORAGE=ARM_UCP_FLASHIAP",
        "MBED_BOOTLOADER_SIZE=APPLICATION_SIZE",
        "MBED_BOOTLOADER_ACTIVE_HEADER_REGION_SIZE=1024",
        "FLASH_BANK_SIZE=MBED_ROM_SIZE/2"
    ],
    "target_overrides": {
        "*": {
            "platform.stdio-baud-rate": 115200,
            "platform.stdio-flush-at-exit": false,
            "update-client.firmware-header-version": "2",
            "mbed-bootloader.use-kvstore-rot": 0,
            "target.restrict_size": "0x8000",
            "update-client.application-details"        : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
            "mbed-bootloader.application-start-address": "(MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS + MBED_BOOTLOADER_ACTIVE_HEADER_REGION_SIZE)",
            "mbed-bootloader.max-application-size"     : "(MBED_ROM_START + FLASH_BANK_SIZE - MBED_CONF_MBED_BOOTLOADER_APPLICATION_START_ADDRESS)",
            "update-client.storage-address"            : "(MBED_ROM_START + FLASH_BANK_SIZE + KVSTORE_SIZE)",
            "update-client.storage-size"               : "(FLASH_BANK_SIZE - KVSTORE_SIZE)",
            "update-client.storage-locations"          : 1
        },
        "NUCLEO_F429ZI": {
            "kvstore-size": "2*64*1024",
            "update-client.storage-page": 1
        }
    }
}
  1. Compile the project:
mbed compile -m NUCLEO_F429ZI -t GCC_ARM --profile tiny.json --app-config configs/internal_flash_no_rot.json
  1. Create a new target configuration for the application:
    "macros": [
        "MBED_BOOTLOADER_SIZE=(32*1024)",
        "ARM_UC_USE_PAL_BLOCKDEVICE=1"
    ],
      "NUCLEO_F429ZI": {
          "target.features_add"                       : ["BOOTLOADER", "STORAGE"],
          "target.bootloader_img"                     : "tools/mbed-bootloader-nucleo_f429zi-internal_flash-no_rot-v4.0.0.bin",
          "target.header_offset"                      : "0x8000",
          "target.app_offset"                         : "0x8400",
          "mbed-cloud-client.external-sst-support"    : 1,
          "update-client.bootloader-details"          : "0x080078CC",
          "update-client.application-details"         : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
          "update-client.storage-address"             : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
          "update-client.storage-size"                : "(1024*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
          "update-client.storage-page"                : 1,
          "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP",
          "storage_tdb_internal.internal_base_address": "(MBED_ROM_START+1024*1024)",
          "storage_tdb_internal.internal_size"        : "(128*1024)",
          "storage.storage_type"                      : "TDB_INTERNAL"
      }

Configuration options:

Option in example Description
"target.header_offset" : "0x8000" Starting address for update header.
"target.app_offset" : "0x8400" Starting address for application.
"target.bootloader_img" : "tools/mbed-bootloader-nucleo_f429zi-internal_flash-no_rot-v4.0.0.bin" Bootloader image provided via application.
"storage_tdb_internal.internal_base_address": "(MBED_ROM_START+1024*1024)" The address where the internal FlashIAP blockDevice starts.
"storage_tdb_internal.internal_size" : "(128*1024)" The size in bytes for the internal FlashIAP block device.
"storage.storage_type" : "TDB_INTERNAL" Use internal flash 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.
"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.
"max-application-size" : "(MBED_CONF_APP_FLASH_START_ADDRESS + MBED_CONF_APP_FLASH_SIZE - MBED_CONF_APP_APPLICATION_START_ADDRESS)" Maximum application size in bytes.

Read the KVStore documentation and API reference for more information.