Mistake on this page?
Report an issue in GitHub or email us

Targets

Adding a new microcontroller to Arm Mbed OS 5 depends on CMSIS-CORE and CMSIS-Pack. Please make sure that the microcontroller already has these available.

Adding a new microcontroller and board

First fork the mbed-os repository on GitHub into your own user account. We will use the placeholder USERNAME to refer to your username in the following documentation, MCU_NAME to refer to the new microcontroller you are adding and BOARD_NAME to refer to the new board you are adding. Import an Mbed OS example, and add your fork of mbed-os using:

mbed import mbed-os-example-blinky
cd mbed-os-example-blinky\mbed-os
git checkout master
git pull
git checkout -b my-new-target
git remote add USERNAME https://github.com/USERNAME/mbed-os
git branch my-new-target -u USERNAME
cd ..

Target description

Add the target description to mbed-os\targets\targets.json using keys that the Adding and configuring targets section describes. We recommend that you run the targets lint script on your target hierarchy before submitting your pull request:

"MCU_NAME": {
    "inherits": ["Target"],
    "core": "Cortex-M3",
    "supported_toolchains": ["ARM", "GCC_ARM", "IAR"],
    "device_has": ["SERIAL", "STDIO_MESSAGES"]
},
"BOARD_NAME": {
    "inherits": ["MCU_NAME"],
    "macros_add": []
}

HAL porting

There are many more APIs to implement. You enable the following APIs by adding a device_has attribute to the MCU_NAME target definition in targets.json and providing an implementation of the API declared in the API header.

device_has API header
ANALOGIN analog_in.h
ANALOGOUT analog_out.h
CAN can_api.h
EMAC emac_api.h
INTERRUPTIN gpio_irq_api.h
I2C I2CSLAVE i2c_api.h
LOWPOWERTIMER lp_ticker_api.h
PORT_IN PORT_OUT port_api.h
PWMOUT pwmout_api.h
RTC rtc_api.h
SLEEP sleep_api.h
SPI SPISLAVE spi_api.h
TRNG trng_api.h
FLASH flash_api.h

Bootstrap

Bring in CMSIS-Core files

To work with Mbed OS, you need to implement CMSIS-Core support for your device as the CMSIS-Core documentation describes. CMSIS-Core files are usually in the mbed-os\targets\TARGET_<VENDOR>\TARGET_MCU_<FAMILY>\TARGET_<MCUNAME>\device directory.

Startup files

The startup file contains interrupt vectors and low-level core and platform initialization routines. You need to provide a version of this file for each Mbed OS supported toolchain.

For more information about startup files, please see the CMSIS documentation.

Linker scripts

After adding the core files, the next step is to add linker scripts for Mbed OS. To do this, you can either use the linker scripts below and change the defines for your target or you can modify an existing linker script to be compatible with Mbed OS. You need to provide a version of the linker script for each Mbed OS supported toolchain.

If you are updating your own linker script, you must:

  • Reserve space for the RAM vector table.
  • Define the start of the heap:
    • Arm - The heap starts immediately after the region RW_IRAM1.
    • GCC_ARM - The heap starts at the symbol __end__.
    • IAR - The heap is the HEAP region.
    • Add defines for a relocatable application - MBED_APP_START and MBED_APP_SIZE.
    • Add preprocessing directive #! armcc -E (ARM compiler only).

If you are using the below linker script, then you need to update all the defines in the /* Device specific values */ section for your target.

Arm linker script template:

#! armcc -E

/* Device specific values */

#define ROM_START   0x08000000
#define ROM_SIZE    0x200000
#define RAM_START   0x20000000
#define RAM_SIZE    0x30000
#define VECTORS     107   /* This value must match NVIC_NUM_VECTORS */

/* Common - Do not change */

#if !defined(MBED_APP_START)
  #define MBED_APP_START ROM_START
#endif

#if !defined(MBED_APP_SIZE)
  #define MBED_APP_SIZE ROM_SIZE
#endif

/* Round up VECTORS_SIZE to 8 bytes */
#define VECTORS_SIZE (((VECTORS * 4) + 7) & ~7)

LR_IROM1 MBED_APP_START MBED_APP_SIZE  {

  ER_IROM1 MBED_APP_START MBED_APP_SIZE  {
    *.o (RESET, +First)
    *(InRoot$$Sections)
    .ANY (+RO)
  }

  RW_IRAM1 (RAM_START + VECTORS_SIZE) (RAM_SIZE - VECTORS_SIZE)  {  ; RW data
    .ANY (+RW +ZI)
  }
}

IAR linker script template:

/* Device specific values */

define symbol ROM_START   = 0x08000000;
define symbol ROM_SIZE    = 0x200000;
define symbol RAM_START   = 0x20000000;
define symbol RAM_SIZE    = 0x30000;
define symbol VECTORS     = 107; /* This value must match NVIC_NUM_VECTORS */
define symbol HEAP_SIZE   = 0x10000;

/* Common - Do not change */

if (!isdefinedsymbol(MBED_APP_START)) {
    define symbol MBED_APP_START = ROM_START;
}

if (!isdefinedsymbol(MBED_APP_SIZE)) {
    define symbol MBED_APP_SIZE = ROM_SIZE;
}

/* Round up VECTORS_SIZE to 8 bytes */
define symbol VECTORS_SIZE = ((VECTORS * 4) + 7) & ~7;
define symbol RAM_REGION_START = RAM_START + VECTORS_SIZE;
define symbol RAM_REGION_SIZE = RAM_SIZE - VECTORS_SIZE;
define symbol ISR_STACK_SIZE = 0x400;

define memory mem with size = 4G;
define region ROM_region = mem:[from MBED_APP_START size MBED_APP_SIZE];
define region RAM_region = mem:[from RAM_REGION_START size RAM_REGION_SIZE];

define block CSTACK    with alignment = 8, size = ISR_STACK_SIZE   { };
define block HEAP      with alignment = 8, size = HEAP_SIZE     { };

initialize by copy { readwrite };
do not initialize  { section .noinit };

place at address mem: MBED_APP_START { readonly section .intvec };

place in ROM_region   { readonly };
place in RAM_region   { readwrite,
                        block CSTACK, block HEAP };

GCC linker script template:

/* Device specific values */

#define ROM_START   0x08000000
#define ROM_SIZE    0x200000
#define RAM_START   0x20000000
#define RAM_SIZE    0x30000
#define VECTORS     107   /* This value must match NVIC_NUM_VECTORS */

/* Common - Do not change */

#if !defined(MBED_APP_START)
  #define MBED_APP_START ROM_START
#endif

#if !defined(MBED_APP_SIZE)
  #define MBED_APP_SIZE ROM_SIZE
#endif

/* Round up VECTORS_SIZE to 8 bytes */
#define VECTORS_SIZE (((VECTORS * 4) + 7) & 0xFFFFFFF8)

MEMORY
{
    FLASH (rx)   : ORIGIN = MBED_APP_START, LENGTH = MBED_APP_SIZE
    RAM (rwx)    : ORIGIN = RAM_START + VECTORS_SIZE, LENGTH = RAM_SIZE - VECTORS_SIZE
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory regions FLASH and RAM.
 * It references following symbols, which must be defined in code:
 *   Reset_Handler : Entry of reset handler
 *
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 */
ENTRY(Reset_Handler)

SECTIONS
{
    .text :
    {
        KEEP(*(.isr_vector))
        *(.text*)

        KEEP(*(.init))
        KEEP(*(.fini))

        /* .ctors */
        *crtbegin.o(.ctors)
        *crtbegin?.o(.ctors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
        *(SORT(.ctors.*))
        *(.ctors)

        /* .dtors */
         *crtbegin.o(.dtors)
         *crtbegin?.o(.dtors)
         *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
         *(SORT(.dtors.*))
         *(.dtors)

        *(.rodata*)

        KEEP(*(.eh_frame*))
    } > FLASH

    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > FLASH

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > FLASH
    __exidx_end = .;

    /* Location counter can end up 2byte aligned with narrow Thumb code but
       __etext is assumed by startup code to be the LMA of a section in RAM
       which must be 4byte aligned */
    __etext = ALIGN (4);

    .data : AT (__etext)
    {
        __data_start__ = .;
        *(vtable)
        *(.data*)

        . = ALIGN(4);
        /* preinit data */
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP(*(.preinit_array))
        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(4);
        /* init data */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);


        . = ALIGN(4);
        /* finit data */
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP(*(SORT(.fini_array.*)))
        KEEP(*(.fini_array))
        PROVIDE_HIDDEN (__fini_array_end = .);

        KEEP(*(.jcr*))
        . = ALIGN(4);
        /* All data end */
        __data_end__ = .;

    } > RAM

    .bss :
    {
        . = ALIGN(4);
        __bss_start__ = .;
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        __bss_end__ = .;
    } > RAM

    .heap (COPY):
    {
        __end__ = .;
        PROVIDE(end = .);
        *(.heap*)
        __HeapLimit = .;
    } > RAM

    /* .stack_dummy section doesn't contains any symbols. It is only
     * used for linker to calculate size of stack sections, and assign
     * values to stack symbols later */
    .stack_dummy (COPY):
    {
        *(.stack*)
    } > RAM

    /* Set stack top to end of RAM, and stack limit move down by
     * size of stack_dummy section */
    __StackTop = ORIGIN(RAM) + LENGTH(RAM);
    __StackLimit = __StackTop - SIZEOF(.stack_dummy);
    PROVIDE(__stack = __StackTop);

    /* Check if data + heap + stack exceeds RAM limit */
    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}
Other required files
  • Make sure your CMSIS-Core implementation contains the device.h header.
  • Extend CMSIS-Core by adding the file mbed-os\targets\TARGET_VENDOR\TARGET_MCUNAME\cmsis.h. This header file includes device-specific headers that include CMSIS-Core. It must also include cmsic_nvic.h.
  • Add the mbed-os\targets\TARGET_VENDOR\TARGET_MCUNAME\cmsis_nvic.h header file. This contains the define NVIC_NUM_VECTORS, which is the number of vectors the devices has, and NVIC_RAM_VECTOR_ADDRESS, which is the address of the RAM vector table. Mbed OS relocates the vectors from the initial location in ROM to the provided address in RAM and updates the VTOR register. NOTE: For devices without the VTOR register, you need to make sure the vectors are in the read-write memory before execution reaches the main function. In this case, you may also need to provide visualization of NVIC access functions. For details, please see the CMSIS NVIC documentation.
  • Define the initial stack pointer, INITIAL_SP, in mbed_rtx.h. This file is typically in mbed-os\targets\TARGET_VENDOR\mbed_rtx.h.

Entry points

Except the reset vector, which is the standard entry point for Cortex-M cores, Mbed OS provides mbed_sdk_init, which the target can overload to perform higher level initialization. Mbed OS internals call this function later in the bootstrap process, after the basic initialization is done but before RTOS starts and before the main function is called.

Mbed OS provides another entry point that will be executed before main called mbed_main. This function is reserved for application use, and the target code should not define it.

Microsecond ticker

The microsecond ticker is a system resource that many APIs use. The microsecond ticker needs a one microsecond resolution and uses a free-running hardware counter or timer with match register. Implement the API declared in mbed-os\hal\us_ticker_api.h.

At this point, we should be able to compile a handful of tests:

mbed test -m BOARD_NAME --compile -t <toolchain>

To execute the tests, you need to support mbed-ls.

Serial

Implement the API declared in mbed-os/hal/serial_api.h. You must define the serial_t struct in objects.h. You may use the serial_t struct for referencing memory-mapped serial registers and passing related pin and peripheral operation information data that the HAL needs.

RTC

The RTC HAL API provides a low-level interface to the Real Time Counter (RTC) of a target.

Assumptions

Defined behavior
  • The function rtc_init is safe to call repeatedly.
  • RTC accuracy is at least 10%.
  • Init/free doesn't stop RTC from counting.
  • Software reset doesn't stop RTC from counting.
  • Sleep modes don't stop RTC from counting.
  • Shutdown mode doesn't stop RTC from counting.
Undefined behavior
  • Calling any function other than rtc_init before the initialization of the RTC.
Potential bugs
  • Incorrect overflow handling.
  • Glitches due to ripple counter.

Implementing the RTC API

RTC HAL API is in hal/rtc_api.h. You need to implement the following functions to support RTC:

To enable sleep support in Mbed OS, you need to add the RTC label in the device_has option of the target's section in the targets.json file.

Mbed TLS entropy

This document explains how to port Arm Mbed TLS to a new Arm Mbed development board.

Note: This part is critical for the security of your product, and you should consult a cryptography expert while considering the choices and implementing them.

Why Mbed TLS needs entropy

Almost every cryptographic protocol requires random values that no one should be able to predict. A striking example is their use as session keys: It is easy to see that if an adversary can predict the session key, then he can decrypt the whole session. Even if the adversary can't predict it exactly, just with a relatively high probability, he can still recover the contents of the session. For example, if the adversary has a 0.00001% chance of predicting the 256 bit AES session key, then he can break it as easily as if we had used a 23 bit key (that is - very easily).

Creating session keys is only one use for random values; they have far more complicated applications. In these more complex use cases, the connection between the predictability of the values and the security of the protocol is not as obvious, but it is still crucial.

Which entropy source to choose

  • If you have a target with a True Random Number Generator (TRNG), then follow Section 3 to allow Mbed TLS to use it.

  • If you have a target without a TRNG, but with a non-volatile (NV) storage, then read Section 4 for instructions on making Mbed TLS use a random seed as entropy. This seed should be separately initialized with a true random number for each device at manufacturing time.

  • If you just want to test Mbed TLS on your target without implementing either of the above, and having no security at all is acceptable, then go to Section 5.

How to provide Mbed TLS entropy from a hardware entropy source

What kind of a source you can add

It is important that you only add a TRNG as described in this section. For the purposes of this document a device is considered a TRNG only if:

  • It is dedicated to generating entropy to be used in cryptographic applications.

  • Careful consideration has been given to how much the data generated is subject to adversarial manipulation.

  • A thorough engineering study has been made to determine how much entropy it can actually provide.

For example, an integrated circuit extracting statistically random data from two oscillators of unknown frequencies and independent phases is considered a TRNG, but anything derived from a real time clock is NOT.

How to add an entropy source

Mbed TLS distinguishes between strong and weak entropy sources. Of the sources registered by default, two are strong: /dev/urandom and Windows CryptoAPI. However, these resources are not available on many embedded platforms, and the default behaviour of Mbed TLS is to refuse to work if there are no strong sources present. To get around this, Mbed TLS assumes that the hardware entropy source you register (as explained in this section) is a TRNG and thus treats it as strong.

The preferred way to provide a custom entropy source:

  1. Implement the functions declared in hal/trng_api.h to let Mbed TLS access the device's entropy source.
  2. Indicate that your target has an entropy source in targets/targets.json, by adding TRNG to your device's device_has section.

The next two sections explain how to do this.

How to implement the TRNG API

The implementation of this interface has to be located in the Arm Mbed OS directory specific to your target. The name of this directory is of the form targets/.../TARGET_<target name>. For example, in the case of K64F targets, it is targets/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_MCU_K64F/.

Data structure

You have to define a structure trng_s that holds all the information needed to operate the peripheral and describe its state.

Initialization and release

To enable initializing and releasing the peripheral, you must implement the following functions:

void trng_init(trng_t *obj);
void trng_free(trng_t *obj);
The entropy collector function

The function trng_get_bytes() serves as the primary interface to the entropy source. It is expected to load the collected entropy to the buffer and is declared as follows:

int trng_get_bytes(trng_t *obj, uint8_t *output, size_t length, size_t *output_length);
  • trng_t *obj: trng_t is an alias to trng_s, and it is the caller's responsibility to initialize it before passing it to this function and release it (with the help of trng_init() and trng_free(), respectively) when it is not required anymore.

  • uint8_t *output: a pointer to the output buffer. The function should write the entropy it collected to the buffer; Mbed TLS then uses this data as entropy. Please consult your board's manual, and write only the strongest entropy possible in this buffer.

    Warning: Although it is possible to fill this buffer without a strong hardware entropy source, we strongly advise against it because it will nullify any security provided by Mbed TLS.

  • size_t length: the length of the output buffer. The function shouldn't write more data than this to the output buffer under any circumstances.

  • size_t *output_length: the length of the data written into the output buffer. It tells the caller how much entropy has been collected and how many bytes of the output buffer it can use. It should always reflect the exact amount of entropy collected; setting it higher than the actual number of bytes collected is a serious security risk.

Indicating the presence of a TRNG

To indicate that the target has an entropy source, you have to add TRNG to the capabilities of the target in targets/targets.json:

"device_has": ["TRNG", etc.]

How to implement the non-volatile seed entropy source

If a hardware platform does not have a hardware entropy source to leverage into the entropy pool, alternatives have to be considered. As stated above, a strong entropy source is crucial for security of cryptographic and TLS operations. For platforms that support non-volatile memory, an option is to use the NV seed entropy source that Mbed TLS provides.

This makes Mbed TLS use a fixed amount of entropy as a seed and update this seed each time entropy is gathered with an Mbed TLS entropy collector for the first time. In a simple case it means that the seed is updated after reset at the start of the first TLS connection.

Note: To make this option a relatively strong compromize, the seed should be initialized separately for each device with true random data at manufacturing time. It has to be true random data, something dependant on, for example the serial number is not secure.

Enabling NV seed entropy source support

To enable the NV seed entropy source, you have to add MBEDTLS_ENTROPY_NV_SEED to your macros in targets.json:

"macros": ["MBEDTLS_ENTROPY_NV_SEED", etc.],

This ensures the entropy pool knows it can use the NV seed entropy source.

By default the platform adaptation functions write/read a seed file called seedfile. If you have a system that does not support regular POSIX file operations (Arm Mbed OS does not support them by default), the default platform-adaptation functions will not be useful to you, and you will need to provide platform-adaptation functions (see next section).

Providing platform-adaptation functions

The NV seed entropy source needs to know how to retrieve and store the seed in non-volatile memory. So in order to make the NV seed entropy source work, two platform-layer functions need to be provided.

The relevant read/write functions have the following prototypes:

int (*mbedtls_nv_seed_read)( unsigned char *buf, size_t buf_len );
int (*mbedtls_nv_seed_write)( unsigned char *buf, size_t buf_len );

Where buf is a pointer to the buffer to read/write a seed, and buf_len is the length of that seed.

There are three methods for setting those functions pointers (similar to all platform adaptation functions in Mbed TLS):

  • MBEDTLS_PLATFORM_NV_SEED_ALT. By enabling this macro, the mbedtls_platform_set_nv_seed(nv_seed_read_func *, nv_seed_write_func*) function becomes available and lets you set the pointers at runtime.
  • MBEDTLS_PLATFORM_STD_NV_SEED_READ and MBEDTLS_PLATFORM_STD_NV_SEED_WRITE (requires MBEDTLS_PLATFORM_NV_SEED_ALT). By setting these two macros to the relevant function names, the default read/write functions are replaced at compile-time, and you still have the option to replace them at runtime as well.
  • MBEDTLS_PLATFORM_NV_SEED_READ_MACRO and MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO. By setting these two macros to the relevant functions names, the read/write functions are replaced at compile-time.

How to test without entropy sources

Both of the above options are secure if done properly, and depending on the platform may need more or less development work. In some cases it may be necessary to test Mbed TLS on boards without entropy. For these kinds of scenarios, Mbed TLS provides a compile time switch to enable testing without entropy sources.

Setting the macros

This option is very dangerous because compiling with it results in a build that is not secure! You have to let Mbed TLS know that you are using it deliberately and you are aware of the consequences. That is why you have to turn off any entropy sources explicitly first.

Because it is a very dangerous option and no one should use it in production, we recommend you limit its scope as much as possible; you should apply these settings to the application specific configuration file, instead of the target related configuration as we did it above. You can read more about how to add a macro for your application.

To turn the unsafe testing mode on:

  1. Make sure that the macros MBEDTLS_HAVEGE_C, MBEDTLS_ENTROPY_HARDWARE_ALT, MBEDTLS_ENTROPY_NV_SEED are not defined.
  2. Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY to the macros in your mbed_app.json.
"macros": ["MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES","MBEDTLS_TEST_NULL_ENTROPY", etc.]

The MBEDTLS_TEST_NULL_ENTROPY option nullifies any security provided by Mbed TLS! It is there exclusively for testing purposes and should never be used in production. It cannot be stressed enough: a library built with this option does not provide any security whatsoever!

GPIO

Implement the api declared in mbed-os/hal/gpio_api.h. You must define the struct gpio_t. This struct is commonly defined in an objects.h file within the mbed-os/targets/TARGET_VENDOR/, mbed-os/targets/TARGET_VENDOR/TARGET_MCU_FAMILY or mbed-os/targets/TARGET_VENDOR/TARGET_MCU_FAMILY/TARGET_MCUNAME directories.

You should define the Physical LEDs and switches in the target's PinNames.h file. LED and switch names begin incrementing at 1 and follow the convention LED1...LEDn and BUTTON1...BUTTONn. You can see an example for more information.

Hardware Accelerated Crypto

This document explains how to add hardware acceleration support for a development board in Arm Mbed OS and integrate it with Arm Mbed TLS.

Introduction

Why should I add hardware acceleration?

Whether the application developer uses Mbed TLS as a cryptographic library or as a TLS stack, cryptographic operations can be expensive in time and can impact the overall performance of application software. Hardware accelerators improve performance of cryptographic operations, which improves overall performance and response time as well.

You may want to add hardware acceleration in the following cases:

  • Your processor has special instructions capable of accelerating cryptographic operations, and you can accelerate parts significantly with optimized assembly code.

  • Your processor has access to a co-processor with cryptographic acceleration capabilities.

  • Your platform has a dedicated crypto-module capable of executing cryptographic primitives, and possibly storing keys securely.

The Mbed TLS library was written in C and it has a small amount of hand-optimized assembly code, limited to arbitrary precision multiplication on some processors. You can find the list of supported platforms in the top comment in bn_mul.h.

What parts can I accelerate?

Mbed TLS has separate modules for the different cryptographic primitives. Hardware acceleration interface is available for the following modules and functions:

How can I make Mbed TLS use my hardware accelerator?

You have to provide an alternative implementation for the parts of Mbed TLS that you want to accelerate.

Mbed TLS has a variety of options to make use of your alternative implementation. These make it possible to easily replace functionality at various abstraction levels and to different extents. In other words, you can replace the least amount of code to reach the highest possible acceleration with the smallest amount of effort.

The easier and safer way to extend functionality is to override some or all of the functions in a particular module. Sometimes this won't be enough, usually because of a need to change the data structures or the higher level algorithms. If this is the case, you'll need to replace the whole module. Also, individual function replacement is only supported for function names listed above under each module; for modules listed without function names, Mbed TLS only supports replacing the whole module. Please note that in the case of ECP functions the override is only partial; Mbed TLS will fall back to the software implementation if the hardware cannot handle a particular group.

No matter which approach you choose, please note the considerations below.

Adding acceleration by replacing functions

Process overview
  1. First, you should consider what kind of functionality your hardware provides. Does the processor have some accelerated cryptographic subroutines? Or does your board have a full hardware cryptography module, securely storing keys and providing the functionality of high level cryptographic primitives?

  2. Identify the module or the functions you want to replace. For example, if you have a full hardware cryptography module, then you probably want to replace the whole Mbed TLS module. If you only have a couple of special instructions or a co-processor that accelerates some part of the cryptographic function, then you may want to replace only the relevant functions in the Mbed TLS module.

  3. If you want to replace functions in the ECP module, you need to implement the mandatory utility functions:

    These are functions that do not have a counterpart in the standard Mbed TLS implementation and their only purpose is to facilitate the integration of the accelerated functions: - mbedtls_internal_ecp_grp_capable: Implement it to tell Mbed TLS if the cryptographic hardware can handle the group. If the answer is no, then Mbed TLS will fall back to the software implementation to continue the operation. - mbedtls_internal_ecp_init and mbedtls_internal_ecp_free are optional. Use them to optimize if you are replacing a function in the ECP module. - For more information about the utility functions read the subsection about the ECP module.

  4. Implement the selected functions with the help of your hardware accelerator.

  5. Because Mbed TLS is implemented as a static link library in Arm Mbed OS, you also have to notify the compiler or linker that the alternative implementations are present. To do this, you have to set the macros corresponding to the selected functions. You can read more on this in the subsection about setting macros.

How to implement the functions

These functions have the same name as the ones they replace. There is a doxygen documentation for the original functions. The exception to the naming conventions is the ECP module and parts of the AES module, where an internal API is exposed to enable hardware acceleration. These functions too have a doxygen documentation, and you can find them in the ecp_internal.h and aes.h header files. The function declarations have to remain unchanged; otherwise, Mbed TLS can't use them.

Clone the Mbed OS repository and copy the source code of your function definitions to the features/mbedtls/targets/TARGET_XXXX directory specific to your target. Create a pull request when your code is finished and production ready. You may create a directory structure similar to the one you have for the HAL if you feel it appropriate.

How to implement ECP module functions

Mbed TLS supports only curves over prime fields and uses mostly curves of short Weierstrass form. The function mbedtls_internal_ecp_add_mixed and the functions having _jac_ in their names are related to point arithmetic on curves in short Weierstrass form. The only Montgomery curve supported is Curve25519. To accelerate operations on this curve, you have to replace the three functions with _mxz_ in their name. For more information on elliptic curves in Mbed TLS, see the corresponding Knowledge Base article.

The method of accelerating the ECP module may support different kinds of elliptic curves. If that acceleration is a hardware accelerator, you may need to indicate what kind of curve operation the accelerator has to perform by setting a register or executing a special instruction. If performing this takes significant amount of time or power, then you may not want Mbed TLS to do this step unnecessarily. The replaceable functions in this module are relatively low level, and therefore it may not be necessary to do this initialization and release in each of them.

To resolve this, you can move the setup of the hardware to the mbedtls_internal_ecp_init and mbedtls_internal_ecp_free functions and let Mbed TLS call them whenever it is necessary. Please keep in mind that mbedtls_internal_ecp_init should return 0 upon a successful setup and MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE otherwise.

How to set the macros

You will have to set some macros to notify Mbed TLS and the compiler or linker about the presence of your functions or module implementation.

The best way to do this is to supply a target-specific configuration file for your target. This configuration file won't replace the Mbed TLS configuration file - it is only an extension of it. Please note that the method described in this section is specific to hardware related macros - please don't use it for defining Mbed TLS macros.

First, you need to notify the build system that you to have a target-specific Mbed TLS configuration. In targets.json, add MBEDTLS_CONFIG_HW_SUPPORT to your target in the macros section:

"macros": ["MBEDTLS_CONFIG_HW_SUPPORT", etc.]

Now you can define your crypto hardware acceleration related macros in an mbedtls_device.h header, which is appended to the ordinary Mbed TLS configuration when compiling for your target.

Note: Place this header in the features/mbedtls/targets/TARGET_XXXX directory specific to your target. You may create a directory structure similar to the one you have for the HAL if you feel it appropriate.

Define the following macros in the header file:

  1. When replacing an entire module: <Module Name Allcaps>_ALT.

  2. When overriding a function: <Function Name Allcaps>_ALT.

For example, if you want to replace mbedtls_sha512_process() and the entire BLOWFISH module, then the contents of your mbedtls_device.h will look something like this:

#define MBEDTLS_SHA512_PROCESS_ALT
#define MBEDTLS_BLOWFISH_ALT

When overriding functions from the ECP module, please note:

  • ECP function names don't contain the _internal_ prefix. For example, to implement the mbedtls_internal_ecp_normalize_mxz function you need to define the MBEDTLS_ECP_NORMALIZE_MXZ_ALT macro.

  • The ECP interface requires the implementation of some utility functions (mbedtls_internal_ecp_grp_capable, mbedtls_internal_ecp_init and mbedtls_internal_ecp_free), therefore you need to notify the compiler or linker about these functions by defining the MBEDTLS_ECP_INTERNAL_ALT macro.

Adding acceleration by replacing modules

Replacing the whole module is the harder way, because it usually takes much more effort than providing alternatives for a handful of functions. It is also less safe, not just because taking this road can cause complications during the maintenance period, but also because it can lead to increased security risks. For example, if the alternative module implementation contains the duplicate of some Mbed TLS code, then keeping it up to date is an extra effort; not doing so may raise security risks.

To replace a module you have to:

  • Implement the functionality of the whole module. Your implementation has to leave unchanged the function prototypes, and the names of any global type, variable or macro.

  • Provide a header file for your implementation. The file name must be <Module Name>_alt.h.

  • Set the macro MBEDTLS_<Module Name>_ALT to notify Mbed TLS and the compiler or linker about the replacement. You can read more on this in the subsection about setting macros.

Where to find the default implementations

The default implementation of the modules are usually in the file feature/mbedtls/src/<Module Name>.c. The ECP module is split to two files: ecp.c and ecp_curves.c.

Considerations for alternative implementations

Concurrency

Note that functions in Mbed TLS can be called from multiple threads and from multiple processes at the same time. Because hardware accelerators are usually a unique resource, it is important to protect all functions against concurrent access.

For short actions, disabling interrupts for the duration of the operation may be enough. When it is not desirable to prevent context switches during the execution of the operation, you must protect the operation with a mutual exclusion primitive such as a mutex. Make sure to unlock the mutex or restore the interrupt status when returning from the function even if an error occurs.

Power management

The current framework does not provide an interface to initialize and shut down accelerator hardware. One approach is to perform any necessary hardware initialization during system startup (outside of Mbed TLS); however this may not be desirable for power consumption reasons. At the other end of the spectrum, it is possible to initialize the hardware at the beginning of each function and shut it down after reading the results. This is a viable strategy if initialization is cheap enough.

If it is neither desirable to leave the hardware powered on permanently nor to initialize it each time, you need to determine a power management strategy according to the expected application usage. Note that unconditionally shutting down the hardware in mbedtls_xxx_free functions is usually not a particularly useful strategy because there may be other live contexts that require the hardware. A more useful strategy is to keep a global use counter: increment the counter as part of context allocation and decrement it as part of freeing each context. When the global counter drops to 0, the hardware is no longer in use.

In specialized applications, it may be best to provide a custom function to switch the hardware on and off and let the application developer decide when to call it.

RTOS

CMSIS/RTX code is imported from the original CMSIS repository.

Memory considerations

Please note that Arm Mbed OS doesn't use any of the RTX memory models, which are based on static carveouts (memory pools). This approach is not ideal for generic systems, such as Mbed OS, because calculating required numbers of RTOS objects is impossible. To avoid declaring arbitrary large buffers carved out at compile time, limiting the amount of available memory, Mbed OS shifts the responsibility of supplying the backing memory to CMSIS-RTOS2 users.

Developers need to use the Mbed OS RTOS C++ API or supply backing memory for RTX objects to os*New calls when using CMSIS-RTOS2 APIs directly. (Please consult CMSIS-RTOS2 documentation for API details.) mbed_rtos_storage.h header provides handy wrappers that you can use to secure required memory without exposing the code to RTX implementation details.

Configuration

Mbed OS changes to RTX configuration all exist in a single file: mbed-os/rtos/rtx2/mbed_rtx_conf.h

Option Value Description
OS_STACK_SIZE 4K or 2K For a normal target, the thread stack size is set to 4K; for constrained targets, it's 2K.
OS_TIMER_THREAD_STACK_SIZE 768B Timer thread stack set to 768B that's necessary to support the C++ wrappers (4 instances), but it may require changing to support larger number of active timers.
OS_IDLE_THREAD_STACK_SIZE 512B Required to handle Mbed OS wrappers
OS_DYNAMIC_MEM_SIZE 0 RTX dynamic memory is disabled.
OS_MUTEX_OBJ_MEM 1 or 0 For ARMC, use 1; for other toolchains, it's 0. ARMC uses statically allocated mutexes internally.
OS_MUTEX_NUM 6 or 0 For ARMC, use 6; for other toolchains, it's 0. ARMC uses statically allocated mutexes internally.
OS_STACK_WATERMARK 0 or 1 Watermarking is enabled if MBED_STACK_STATS_ENABLED or MBED_STACK_STATS_ENABLED are set.
OS_PRIVILEGE_MODE 0 or 1 We set it for 0 if uVisor is enabled, 1 otherwise.

Code structure

Due to differences in how the Mbed OS and CMSIS directory structures look, you can't import the original code directly. Some directory changes are necessary:

CMSIS5 Mbed OS Explanation
CMSIS_5/CMSIS/Core/Include/core_*.h mbed-os/cmsis/ Core specific code
CMSIS_5/CMSIS/Core/Include/tz_context.h mbed-os/cmsis/ TrustZone code
CMSIS_5/CMSIS/Core/Include/cmsis_compiler.h mbed-os/cmsis/ Toolchain generic code
CMSIS_5/CMSIS/Core/Include/cmsis_{armcc,armclang,gcc}.h mbed-os/cmsis/TOOLCHAIN_{ARM,GCC}/ Toolchain specific code
CMSIS_5/CMSIS/RTOS2/Include/cmsis_os2.h mbed-os/rtos/TARGET_CORTEX/rtx5/ RTX main header
CMSIS_5/CMSIS/RTOS2/RTX/Config/ mbed-os/rtos/TARGET_CORTEX/rtx5 RTX configuration files
CMSIS_5/CMSIS/RTOS2/RTX/Include1/ mbed-os/rtos/TARGET_CORTEX/rtx4 RTOS1 compatibility layer
CMSIS_5/CMSIS/RTOS2/RTX/Include/ mbed-os/rtos/TARGET_CORTEX/rtx5 RTX definitions
CMSIS_5/CMSIS/RTOS2/RTX/Source/rtx_* mbed-os/rtos/TARGET_CORTEX/rtx5 RTX sources
CMSIS_5/CMSIS/RTOS2/RTX/Source/svc_user.c mbed-os/rtos/rtx2/TARGET_CORTEX_M/ RTX SVC user table
CMSIS_5/CMSIS/RTOS2/RTX/Source/{ARM,GCC,IAR}/ mbed-os/rtos/TARGET_CORTEX/rtx5/TARGET_{M0,M0P,M3,RTOS_M4_M7,M23,M33}/TOOLCHAIN_{ARM,GCC,IAR} Toolchain and core specific exception handlers

Modification

Due to different use cases between Mbed OS and CMSIS, we had to make some modifications to the source code. We've tried to upstream our changes to the CMSIS repository, but in cases where they aren't compatible with CMSIS requirements, we are forced to maintain a small set of changes.

CMSIS
Filename Description
cmsis_compiler.h Added IAR missing __ALIGNED attribute for earlier (less than 7.8.4) versions
cmain.S custom IAR non-RTOS boot sequence for Mbed
RTX
Filename Description
cmsis_os2.h Doxygen added; added per-thread uVisor context
cmsis_os1.h Change osThreadDef to accept 3 parameters rather than 4 and be not static as expected by Mbed OS
core_cm.h Doxygen added; included headers changed to match Mbed OS core selection; deferred priority setting of SVCall to uVisor when uVisor is enabled
RTX_Config.h Doxygen added; Mbed OS RTX config included
rtx_evr.c CMSIS component definition include removed
rtx_evr.h Doxygen added
rtx_thread.c Added per-thread uVisor context; notify uVisor of OS events
rtx_kernel.c Added per-thread uVisor context; notify uVisor of OS events
rtx_lib.h Doxygen added; added per-thread uVisor context
rtx_os.h Doxygen added; added per-thread uVisor context
irq_cm4.s For all toolchains: added case for Cortex M4 cores without VFP
svc_user.c Removed as its template file and should not be in our code base
rt_OsEventObserver.{c,h} Added an interface for uVisor to be notified about certain events from privileged code
irq_armv8mbl.S IAR toolchain: added file for Cortex M23 core
irq_armv8mml.S IAR toolchain: added file for Cortex M33 core

Note: For all toolchains, Mbed OS uses irq_cm0.s for both M0 and M0P cores.

Tickless

Tickless mode

Tickless mode is an optimization mechanism available in RTOS for suspending the SysTick. You can use it in situations when RTOS is idle for multiple ticks, so you can achieve power savings by entering uninterrupted sleep. Target implementing tickless mode disables the SysTick, sets up wake-up timers and enters sleep mode when idle. It then exits sleep mode and re-enables the SysTick when the timer expires or some event occurs (like external interrupt).

Enabling tickless mode

To support tickless mode in Mbed OS, your target needs to meet two requirements:

  • Support for Sleep HAL API
  • Support for Low Power Ticker HAL API

To enable tickless mode support in Mbed OS, you need to add the MBED_TICKLESS macro in the macros option of the target's section in the targets.json file.

Targets supporting tickless mode override the default SysTick mechanism and use RtosTimer implementation based on low power ticker. This change is necessary to avoid drift connected with using two different timers to measure time. It should be mostly invisible for users, except that users must not change the low power ticker interrupt handler when tickless mode is in use.

Testing

There are no dedicated tests validating tickless mode. Running all Mbed OS tests suits, with particular focus on HAL sleep and HAL low power ticker tests, provides sufficient coverage.

Sleep and deep sleep

Mbed OS defines two sleep modes for HAL:

  • Sleep.
  • Deep sleep.

Each target should document in their implementation:

  • The target's mode description for each mode (how the target's mode maps to the Mbed OS sleep modes).
  • Wake-up latency for each mode.
  • Wake-up sources for each mode.

Sleep

The core system clock is disabled, both the low and high precision clocks are enabled and RAM is retained.

  1. Wake-up sources - any interrupt source can wake up the MCU.
  2. Latency - can wake up within 10 us.

Deep sleep

The core system clock is disabled. The low precision clocks are enabled, and RAM is retained.

  1. Wake-up sources - RTC, low power ticker and GPIO can wake up the MCU.
  2. Latency - can wake up within 10 ms.

The deep sleep latency (10 ms) is the higher limit of the boards we support. Most of targets have wake-up latency for deep sleep within a few microseconds, but often, reinitializing clocks and other configurations require additional time to restore previous state.

Implementing the Sleep API

There are two functions that the target needs to implement to support sleep, Their prototypes are in hal/sleep_api.h:

  • Sleep.
void hal_sleep(void);
  • Deep sleep.
void hal_deepsleep(void);

To enable sleep support in Mbed OS, you need to add the SLEEP label in the device_has option of the target's section in the targets.json file.

Flash and bootloader

Update target to support bootloader:

  1. Update linker script.
  2. Add required metadata to targets.json.
  3. Implement mbed_start_application.
  4. Implement flash HAL API.
  5. Verify changes with tests.

Linker script updates

When building a bootloader application or an application that uses a bootloader, the Arm Mbed OS build system automatically defines values for the start of application flash, MBED_APP_START, and size of application flash, MBED_APP_SIZE, when preprocessing the linker script. When updating a target to support this functionality, linker scripts must place all flash data in a location starting at MBED_APP_START and must limit the size of that data to MBED_APP_SIZE. This change must occur for the linker scripts of all toolchains - GCC Arm (.ld), Arm (.sct) and IAR (.icf). You can find examples of this for the k64f, stm32f429, odin-w2.

Use these 2 defines in place of flash start and size for a target:

  • MBED_APP_START - defines an address where an application space starts.
  • MBED_APP_SIZE - the size of the application.

Note: When an application does not use any of the bootloader functionality, then MBED_APP_START and MBED_APP_SIZE are not defined. For this reason, the linker script must define default values that match flash start and flash size..

An example of how a target could define MBED_APP_START and MBED_APP_SIZE in the linker script file:

#if !defined(MBED_APP_START)
  #define MBED_APP_START 0
#endif

#if !defined(MBED_APP_SIZE)
  #define MBED_APP_SIZE 0x100000
#endif

Be careful with these defines because they move the application flash sections. Therefore, you should move any sections within flash sectors accordingly.

Note: The VTOR must be relative to the region in which it is placed. To confirm, search for NVIC_FLASH_VECTOR_ADDRESS and SCB->VTOR, and ensure the flash address is not hardcoded.

Problematic declaration of flash VTOR address:

#define NVIC_RAM_VECTOR_ADDRESS   (0x20000000)
#define NVIC_FLASH_VECTOR_ADDRESS (0x00000000)

Bootloader-ready declaration of flash VTOR address:

#define NVIC_RAM_VECTOR_ADDRESS   (0x20000000)
#if defined(__ICCARM__)
    #pragma section=".intvec"
    #define NVIC_FLASH_VECTOR_ADDRESS   ((uint32_t)__section_begin(".intvec"))
#elif defined(__CC_ARM)
    extern uint32_t Load$$LR$$LR_IROM1$$Base[];
    #define NVIC_FLASH_VECTOR_ADDRESS   ((uint32_t)Load$$LR$$LR_IROM1$$Base)
#elif defined(__GNUC__)
    extern uint32_t vectors[];
    #define NVIC_FLASH_VECTOR_ADDRESS   ((uint32_t)vectors)
#else
    #error "Flash vector address not set for this toolchain"
#endif

targets.json metadata

The managed and unmanaged bootloader builds require some target metadata from CMSIS Packs. Add a "device_name" attribute to your target as Adding and configuring targets describes.

Start application

The mbed_start_application implementation exists only for Cortex-M3, Cortex-M4 and Cortex-M7. You can find it in the Arm Mbed_application code file. If mbed_start_application does not support your target, you must implement this function in the target HAL.

Flash HAL

For a bootloader to perform updates, you must implement the flash API. This consists of implementing the function in flash_api.h and adding the correct fields to targets.json.

There are two options to implement flash HAL:

Option 1: CMSIS flash algorithm routines

These are quick to implement. They use CMSIS device packs and scripts to generate binary blobs. Because these flash algorithms do not have well-specified behavior, they might disable cache, reconfigure clocks and other actions you may not expect. Therefore, proper testing is required. First, make sure CMSIS device packs support your device. Run a script in mbed-os to generate flash blobs. Check the flash blobs into the target's HAL. Arm provies an example of how to do this.

To enable a CMSIS flash algorithm common layer, a target should define FLASH_CMSIS_ALGO. This macro enables the wrapper between CMSIS flash algorithm functions from the flash blobs and flash HAL.

"TARGET_NAME": {
    "extra_labels": [FLASH_CMSIS_ALGO]
}

The CMSIS algorithm common layer provides a trampoline, which uses a flash algorithm blob. It invokes CMSIS FLASH API, which the CMSIS-Pack Algorithm Functions page defines.

Option 2: Your own HAL driver

If CMSIS packs do not support a target, you can implement flash HAL by writing your own HAL driver.

Functions to implement:

int32_t flash_init(flash_t *obj);
int32_t flash_free(flash_t *obj);
int32_t flash_erase_sector(flash_t *obj, uint32_t address);
int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size);
uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address);
uint32_t flash_get_page_size(const flash_t *obj);
uint32_t flash_get_start_address(const flash_t *obj);
uint32_t flash_get_size(const flash_t *obj);

To enable flash HAL, define FLASH in targets.json file inside device_has:

"TARGET_NAME": {
   "device_has": ["FLASH"]
}

Finally, to indicate that your device fully supports bootloaders, set the field bootloader_supported to true for the target in the targets.json file:

"bootloader_supported": true

Tests

The following tests for the FlashIAP class and flash HAL are located in the mbed-os/TESTS folder.

  • Flash IAP unit tests: tests-mbed_drivers-flashiap.
  • Flash HAL unit tests: tests-mbed_hal-flash.

They test all flash API functionality. To run the tests, use these commands:

  • Flash IAP: mbed test -m TARGET_NAME -n tests-mbed_drivers-flashiap.
  • Flash HAL: mbed test -m TARGET_NAME -n tests-mbed_hal-flash.

Troubleshooting

  • For targets with VTOR, a target might have a VTOR address defined to a hardcoded address as mentioned in the Linker script updates section.

  • Using Flash IAP might introduce latency as it might disable interrupts for longer periods of time.

  • Program and erase functions might operate on different sized blocks - page size might not equal to a sector size. The function erase erases a sector, the program function programs a page. Use accessor methods to get the values for a sector or a page.

  • Sectors might have different sizes within a device.

Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.