Documentation

Mistake on this page? Email us

Porting a new target for Mbed OS using SOTP

Note: This section describes the EFSF-SOTP configuration which is legacy used by Device Management Client versions 1.3.0 -> 2.1.1. Device Management Client versions 2.2.0 or later use secure storage via KVStore by default.

This guide explains how to port Device Management Client using the NUCLEO-F411RE board as an example target.

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.
  • Have access to the unique device key if the firmware candidates are stored off-chip.

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 SOTP (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

    +--------------------------+ <-+ End of flash
    |                          |
    +--------------------------+
    |                          |
    |        Active app        |
    |                          |
    +--------------------------+ <-+ application-start-address
    |                          |
    |Active app metadata header|
    |                          |
    +--------------------------+ <-+ update-client.application-details
    |          SOTP_2          |
    +--------------------------+ <-+ sotp-section-2-address
    |          SOTP_1          |
    +--------------------------+ <-+ sotp-section-1-address
    |                          |
    |        Bootloader        |
    |                          |
    +--------------------------+ <-+ 0
  • The bootloader is allocated in the first 32 KiB of space at the start of the flash.
  • The Update client uses Software One Time Programming (SOTP) to store a device root of trust. It needs to occupy two erase sectors on the internal flash.
    • SOTP section 1 (16 KiB) starts from 32 KiB.
    • SOTP section 2 (16 KiB) starts from 48 KiB.
  • The active app metadata header must start on a flash erase boundary. Here, it starts from 64 KiB. This header is allocated 1 KiB.
  • The active application must start after the metadata header 1 KiB section. Here, it starts from 65 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
    |                          |
    |  Filesystem allocated    |
    |  by the application for  |
    |  ESFS for storing        |
    |  certificates and        |
    |  credentials.            |
    |                          |
    +--------------------------+ <-+ Start of block device (such as 0x0)

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

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 mbed_app.json:

    1. Set block device options:
    "macros": [
            "ARM_UC_USE_PAL_BLOCKDEVICE=1",
            "MBED_CLOUD_CLIENT_UPDATE_STORAGE=ARM_UCP_FLASHIAP_BLOCKDEVICE"        
            ...
        ],
        "target_overrides": {
            "*": {
                "target.features_remove"                : ["LWIP", "STORAGE"],
                "platform.stdio-baud-rate"              : 115200,
                "platform.stdio-flush-at-exit"          : false,
                "update-client.storage-address"         : "(1024*1024*64)",
                "update-client.storage-size"            : "(1024*1024*2)",
                "update-client.storage-locations"       : 1,
                "update-client.firmware-header-version" : "2"
            },
    
    1. Create a new target:
    "target_overrides": {
        "NUCLEO_F411RE": {
                "flash-start-address"               : "0x08000000",
                "flash-size"                        : "(512*1024)",
                "nvstore.area_1_address"            : "(MBED_CONF_APP_FLASH_START_ADDRESS+32*1024)",
                "nvstore.area_1_size"               : "(16*1024)",
                "nvstore.area_2_address"            : "(MBED_CONF_APP_FLASH_START_ADDRESS+48*1024)",
                "nvstore.area_2_size"               : "(16*1024)",
                "update-client.application-details" : "(MBED_CONF_APP_FLASH_START_ADDRESS+64*1024)",
                "application-start-address"         : "(MBED_CONF_APP_FLASH_START_ADDRESS+65*1024)",
                "max-application-size"              : "(MBED_CONF_APP_FLASH_START_ADDRESS + MBED_CONF_APP_FLASH_SIZE - MBED_CONF_APP_APPLICATION_START_ADDRESS)"
            }
    }
    
  3. Compile the project:

    mbed compile -m NUCLEO_F411RE -t GCC_ARM --profile tiny.json
    
  4. Create a new target configuration for the application:

    • mbed_lib.json file in the application repository:
    {
          "name": "client_app",
          "config": {
              "sotp-section-1-address": {
                  "help": "Flash sector address for SOTP sector 1",
                  "macro_name": "PAL_INTERNAL_FLASH_SECTION_1_ADDRESS",
                  "value": null
              },
              "sotp-section-1-size": {
                  "help": "Flash sector size for SOTP sector 1",
                  "macro_name": "PAL_INTERNAL_FLASH_SECTION_1_SIZE",
                  "value": null
              },
              "sotp-section-2-address": {
                  "help": "Flash sector address for SOTP sector 2",
                  "macro_name": "PAL_INTERNAL_FLASH_SECTION_2_ADDRESS",
                  "value": null
              },
              "sotp-section-2-size": {
                  "help": "Flash sector size for SOTP sector 2",
                  "macro_name": "PAL_INTERNAL_FLASH_SECTION_2_SIZE",
                  "value": null
              }
          }
      }
    
    • mbed_app.json file in the application repository:
    "macros": [
            "ARM_UC_USE_PAL_BLOCKDEVICE=1",
            "MBED_CLOUD_CLIENT_UPDATE_STORAGE=ARM_UCP_FLASHIAP_BLOCKDEVICE"
            ...
        ],
        "target_overrides": {
            "*": {
                "target.features_add"               : ["BOOTLOADER", "STORAGE"],
                "target.components_add"             : ["SD"],
                "update-client.storage-address"     : "(1024*1024*64)",
                "update-client.storage-size"        : "(1024*1024*2)",
                "update-client.storage-locations"   : 1
                }
            }
    
    "target_overrides": {
        "NUCLEO_F411RE": {
                "client_app.sotp-section-1-address" : "(0x08000000+32*1024)",
                "client_app.sotp-section-1-size"    : "(16*1024)",
                "client_app.sotp-section-2-address" : "(0x08000000+48*1024)",
                "client_app.sotp-section-2-size"    : "(16*1024)"
                "update-client.application-details" : "(MBED_CONF_APP_FLASH_START_ADDRESS+64*1024)",
                "application-start-address"         : "(MBED_CONF_APP_FLASH_START_ADDRESS+65*1024)"
            }
    }
    

Configuration options:

Option in example Description
"flash-start-address" : "0x08000000" Starting address for flash in the target MCU.
"flash-size" : "(512*1024)" Size of the flash in bytes.
"nvstore.area_1_address" : "(MBED_CONF_APP_FLASH_START_ADDRESS+32*1024)" Starting address for section 1 for bootloader NVStore. Minimum requirement size is 1 KiB. The address must align to flash erase boundary.
"nvstore.area_1_size" : "(16*1024)" Size in bytes for section 1.
"nvstore.area_2_address" : "(MBED_CONF_APP_FLASH_START_ADDRESS+48*1024)" Starting address for section 2 for bootloader NVStore. Minimum requirement size is 1 KiB. The address must align to flash erase boundary.
"nvstore.area_2_size" : "(16*1024)" Size in bytes for section 2.
"sotp-section-1-address" : "(0x08000000+32*1024)" Application start address for SOTP section 1.
"sotp-section-1-size" : "(16*1024)" Application section size for SOTP section 1.
"sotp-section-2-address" : "(0x08000000+48*1024)" Application start address for SOTP section 2.
"sotp-section-2-size" : "(16*1024)" Application section size for SOTP section 2.
"update-client.application-details" : "(MBED_CONF_APP_FLASH_START_ADDRESS+64*1024)" Address to which the metadata header of the active firmware is written. Must align to flash erase boundary.
"application-start-address" : "(MBED_CONF_APP_FLASH_START_ADDRESS+65*1024)" 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.

Configuration for storage

Configure the storage settings of Device Management Client for storing device credentials and the firmware update image.

The default configuration for this porting guide is external storage (SD card, external SPI flash) using the FAT filesystem. In this case, no changes are needed for the application configuration.

Configuration for non-TRNG targets

If there is no true random number generation (TRNG) available for your selected target, you need to provide entropy externally. This means providing entropy through factory provisioning for production devices, and using hardcoded entropy in developer mode through provisioning APIs as demonstrated in the reference example application. However, for a device to use externally provided entropy, you need to appropriately configure TLS. Device Management Client provides such configuration, but you need to enable it in the configuration file.

For example, NUCLEO-F411RE is a non-TRNG target, so to enable external entropy:

 "NUCLEO_F411RE": {
       "client_app.mbedtls-user-config-file"      : "\"mbedTLSConfig_mbedOS_SW_TRNG.h\"",
       "client_app.pal-user-defined-configuration": "\"sotp_non_trng_config_MbedOS.h\"",
        ...
 }

Porting to Client Lite

The only difference compared to porting to Device Management Client is that when porting to Client Lite, use internal flash instead of SD card or external flash. The following sections detail how to configure the device to use internal flash for Client Lite. All other porting steps are the same as for Device Management Client.

Flash layout using internal flash for Client Lite

The expected layout for Client Lite using internal flash is:

    +--------------------------+ <-+ update-client.storage-address
    |                          |                  +
    |                          |     update-client.storage-size
    |                          |
    |Firmware candidate storage|
    |                          |
    |                          |
    |                          |
    +--------------------------+ <-+ update-client.storage-address
    |                          |
    |                          |
    |                          |
    |        Active app        |
    |                          |
    |                          |
    |                          |
    +--------------------------+ <-+ application-start-address
    |                          |
    |Active app metadata header|
    |                          |
    +--------------------------+ <-+ update-client.application-details
    |        NVSTORE_2         |
    +--------------------------+ <-+ nvstore.area_2_address
    |        NVSTORE_1         |
    +--------------------------+ <-+ nvstore.area_1_address
    |                          |
    |        Bootloader        |
    |                          |
    +--------------------------+ <-+ 0 Start of internal flash

Configuring the bootloader for Client Lite

Choose the appropriate configuration(s) from the /configs folder to modify and add the configurations for the new target.

This configuration is done for 512 KiB of internal flash beginning at address 0, with 2 KiB erase page size and 4 KiB write page size:

    "NEW_TARGET": {
        "update-client.application-details" : "(36*1024)",
        "application-start-address"         : "(37*1024)",
        "max-application-size"              : "(MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS-MBED_CONF_APP_APPLICATION_START_ADDRESS)",
        "update-client.storage-address"     : "((256+18)*1024)",
        "update-client.storage-size"        : "((256-18)*1024)",
        "update-client.storage-locations"   : 1,
        "update-client.storage-page"        : 4
    }

Configuration options:

Option in example Description
"update-client.application-details" : "(36*1024)" 32 KiB is saved for bootloader. NVStore uses 4 KiB, so the application details are located at 36 KiB.
"application-start-address" : "(37*1024) 1 KiB is reserved for application details, so application-start-address is 37 KiB.
"max-application-size" : "(MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS-MBED_CONF_APP_APPLICATION_START_ADDRESS)" Calculate max-application-size using macros.
"update-client.storage-address" : "((256+18)*1024)" Allocate 237 KiB for the application and rest of the flash, 238 KiB, for update client to store new firmware.
"update-client.storage-page" : 4 This target has 4 B write page size.

Configuring the application for Client Lite

Choose the appropriate configuration(s) from the /configs folder to modify, and add configuration options for the new target.

The application flash configuration must be compatible with the bootloader flash configuration (see above). The NVStore configuration is similar to SOTP in Device Management Client configuration.

    "NEW_TARGET": {
        "target.bootloader_img"             : "</path/to/your/bootloader.bin>",
        "update-client.application-details" : "(36*1024)",
        "update-client.storage-address"     : "((256+18)*1024)",
        "update-client.storage-size"        : "((256-18)*1024)",
        "update-client.storage-locations"   : 1,
        "update-client.storage-page"        : 4,
        "nvstore.area_1_address"            : "(32*1024)",
        "nvstore.area_1_size"               : "(2048)",
        "nvstore.area_2_address"            : "(34*1024)",
        "nvstore.area_2_size"               : "(2048)"
    }

Configuration options:

Option in example Explanation
"target.bootloader_img" : "</path/to/your/bootloader.bin>" Path to your bootloader binary that you created earlier. Not mandatory if bootloader is already part of Mbed OS.
"update-client.application-details" : "(36*1024)" Allocate 32 KiB for bootloader. Nvstore uses 4 KiB, so the application details are located at 36 KiB.
"update-client.storage-address" : "((256+18)*1024)" Allocate 237 KiB for the application and rest of the flash, 238 KiB, for Update client to store new firmware.
"update-client.storage-page" : 4 This target has 4 B write page size.
"nvstore.area_1_address" : "(32*1024)" Allocate 4 KiB (2 * 2 KiB) for NVStore after the bootloader.