Skip to content

Latest commit

 

History

History
345 lines (220 loc) · 16.6 KB

compile.md

File metadata and controls

345 lines (220 loc) · 16.6 KB

Compiling wolfBoot

WolfBoot is portable across different types of embedded systems. The platform-specific code is contained in a single file under the hal directory, and implements the hardware-specific functions.

To enable specific compile options, use environment variables while calling make, e.g.

make CORTEX_M0=1

As an alternative, you can provide a .config file in the root directory of wolfBoot. Command line options have priority on .config options, as long as .config options are defined using the ?= operator, e.g.:

WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x14000

Generate a new configuration

A new .config file with a set of default parameters can be generated by running make config. The build script will ask to enter a default value for each configuration parameter. Enter confirm the current value, indicated in between [].

Once a .config file is in place, it will change the default compile-time options when running make without parameters.

.config can be modified with a text editor to alter the default options later on.

Platform selection

If supported natively, the target platform can be specified using the TARGET variable. Make will automatically select the correct compile option, and include the corresponding HAL for the selected target.

For a list of the platforms currently supported, see the HAL documentation.

To add a new platform, simply create the corresponding HAL driver and linker script file in the hal directory.

Default option if none specified: TARGET=stm32f4

Some platforms will require extra options, specific for the architecture. By default, wolfBoot is compiled for ARM Cortex-M3/4/7. To compile for Cortex-M0, use:

CORTEX_M0=1

Speed vs. size

Some targets support assembly optimizations by default. To disable assembly optimizations, use NO_ASM=1. This option will produce smaller code, but will also impact on the boot time.

ARM-specific ARM optimizations affecting hash and symmetric key ciphers can be disabled with the option NO_ARM_ASM=1. This is useful for example when you want to use SP math optimizations for key verification, but exclude SHA2/AES optimizations to save some space.

Example: ECC256 + SHA256 on STM32H7

Benchmark footprint vs. boot time SHA of 100KB image + signature verification

Description Selected options wolfBoot size (B) Boot time (s)
Full ECC256 assembly optimizations. Fastest. SIGN=ECC256 21836 .583
Optimize ECC only (SP math assembly only) SIGN=ECC256 NO_ARM_ASM=1 18624 .760
No assembly optimizations (smallest) SIGN=ECC256 NO_ASM=1 14416 3.356

Flash partitions

The file include/target.h is generated according to the configured flash geometry, partitions size and offset of the target system. The following values must be set to provide the desired flash configuration, either via the command line, or using the .config file:

  • WOLFBOOT_SECTOR_SIZE

This variable determines the size of the physical sector on the flash memory. If areas with different block sizes are used for the two partitions (e.g. update partition on an external flash), this variable should indicate the size of the biggest sector shared between the two partitions.

WolfBoot uses this value as minimum unit when swapping the firmware images in place. For this reason, this value is also used to set the size of the SWAP partition.

  • WOLFBOOT_PARTITION_BOOT_ADDRESS

This is the start address of the boot partition, aligned to the beginning of a new flash sector. The application code starts after a further offset, equal to the partition header size (256B for Ed25519 and ECC signature headers).

  • WOLFBOOT_PARTITION_UPDATE_ADDRESS

This is the start address of the update partition. If an external memory is used via the EXT_FLASH option, this variable contains the offset of the update partition from the beginning of the external memory addressable space.

  • WOLFBOOT_PARTITION_SWAP_ADDRESS

The address for the swap spaced used by wolfBoot to swap the two firmware images in place, in order to perform a reversible update. The size of the SWAP partition is exactly one sector on the flash. If an external memory is used, the variable contains the offset of the SWAP area from the beginning of its addressable space.

  • WOLFBOOT_PARTITION_SIZE

The size of the BOOT and UPDATE partition. The size is the same for both partitions.

Bootloader features

A number of characteristics can be turned on/off during wolfBoot compilation. Bootloader size, performance and activated features are affected by compile-time flags.

Change DSA algorithm

By default, wolfBoot is compiled to use Ed25519 DSA. The implementation of ed25519 is smaller, while giving a good compromise in terms of boot-up time.

Better performance can be achieved using ECDSA with curve p-256. To activate ECC256, ECC384 or ECC521 support, use:

SIGN=ECC256 or SIGN=ECC384 or SIGN=ECC521 respectively.

when invoking make.

RSA is also supported, with different key length. To activate RSA2048, RSA3072 or RSA4096, use:

SIGN=RSA2048 or SIGN=RSA3072 or SIGN=RSA4096 respectively.

Ed448 is also supported via SIGN=ED448.

The default option, if no value is provided for the SIGN variable, is

SIGN=ED25519

Changing the DSA algorithm will also result in compiling a different set of tools for key generation and firmware signature.

Find the corresponding key generation and firmware signing tools in the tools directory.

It's possible to disable authentication of the firmware image by explicitly using:

SIGN=NONE

in the Makefile commandline. This will compile a minimal bootloader with no support for public-key authenticated secure boot.

Incremental updates

wolfBoot support incremental updates. To enable this feature, compile with DELTA_UPDATES=1.

An additional file is generated when the sign tool is invoked with the --delta option, containing only the differences between the old firmware to replace, currently running on the target, and the new version.

For more information and examples, see the firmware update section.

Enable debug symbols

To debug the bootloader, simply compile with DEBUG=1. The size of the bootloader will increase consistently, so ensure that you have enough space at the beginning of the flash before WOLFBOOT_PARTITION_BOOT_ADDRESS.

Disable interrupt vector relocation

On some platforms, it might be convenient to avoid the interrupt vector relocation before boot-up. This is required when a component on the system already manages the interrupt relocation at a different stage, or on these platform that do not support interrupt vector relocation.

To disable interrupt vector table relocation, compile with VTOR=0. By default, wolfBoot will relocate the interrupt vector by setting the offset in the vector relocation offset register (VTOR).

Limit stack usage

By default, wolfBoot does not require any memory allocation. It does this by performing all the operations using the stack. Although the stack space used by the algorithms can be predicted at compile time, the amount of stack space be relatively big, depending on the algorithm selected.

Some targets offer limited amount of RAM to use as stack space, either in general, or in a configuration dedicated for the bootloader stage.

In these cases, it might be useful to activate WOLFBOOT_SMALL_STACK=1. With this option, a fixed-size pool is created at compile time to assist the allocation of the object needed by the cryptography implementation. When compiled with WOLFBOOT_SMALL_STACK=1, wolfBoot reduces the stack usage considerably, and simulates dynamic memory allocations by assigning dedicated, statically allocated, pre-sized memory areas.

Allow bigger stack size allocation

Some combinations of authentication algorithms, key sizes and math configuration in wolfCrypt require a large amount of memory to be allocated in the stack at runtime. By default, if your configuration falls in one of these cases, wolfBoot compilation will terminate with an explicit error.

In some cases you might have enough memory available to allow large stack allocations. To circumvent the compile-time checks on the maximum allowed stack size, use WOLFBOOT_HUGE_STACK=1.

Disable Backup of current running firmware

Optionally, it is possible to disable the backup copy of the current running firmware upon the installation of the update. This implies that no fall-back mechanism is protecting the target from a faulty firmware installation, but may be useful in some cases where it is not possible to write on the update partition from the bootloader. The associated compile-time option is

DISABLE_BACKUP=1

Enable workaround for 'write once' flash memories

On some microcontrollers, the internal flash memory does not allow subsequent writes (adding zeroes) to a sector, after the entire sector has been erased. WolfBoot relies on the mechanism of adding zeroes to the 'flags' fields at the end of both partitions to provide a fail-safe swap mechanism.

To enable the workaround for 'write once' internal flash, compile with

NVM_FLASH_WRITEONCE=1

warning When this option is enabled, the fail-safe swap is not guaranteed, i.e. the microcontroller cannot be safely powered down or restarted during a swap operation.

Allow version roll-back

WolfBoot will not allow updates to a firmware with a version number smaller than the current one. To allow downgrades, compile with ALLOW_DOWNGRADE=1.

Warning: this option will disable version checking before the updates, thus exposing the system to potential forced downgrade attacks.

Enable optional support for external flash memory

WolfBoot can be compiled with the makefile option EXT_FLASH=1. When the external flash support is enabled, update and swap partitions can be associated to an external memory, and will use alternative HAL function for read/write/erase access.

To associate the update or the swap partition to an external memory, define PART_UPDATE_EXT and/or PART_SWAP_EXT, respectively. By default, the makefile assumes that if an external memory is present, both PART_UPDATE_EXT and PART_SWAP_EXT are defined.

If the NO_XIP=1 makefile option is present, PART_BOOT_EXT is assumed too, as no execute-in-place is available on the system. This is typically the case of MMU system (e.g. Cortex-A) where the operating system image(s) are position-independent ELF images stored in a non-executable non-volatile memory, and must be copied in RAM to boot after verification.

When external memory is used, the HAL API must be extended to define methods to access the custom memory. Refer to the HAL page for the description of the ext_flash_* API.

The EXT_FLASH option can also be used if the target device requires special handling for flash reads (e.g. word size requirements or other restrictions), regardless of whether the flash is internal or external.

Note that the EXT_FLASH option is incompatible with the NVM_FLASH_WRITEONCE option. Targets that need both these options must implement the sector-based read-modify-erase-write sequence at the HAL layer.

For an example of using EXT_FLASH to bypass read restrictions, (in this case, the inability to read from erased flash due to ECC errors) on a platform with write-once flash, see the infineon tricore port.

SPI devices

In combination with the EXT_FLASH=1 configuration parameter, it is possible to use a platform-specific SPI drivers, e.g. to access an external SPI flash memory. By compiling wolfBoot with the makefile option SPI_FLASH=1, the external memory is directly mapped to the additional SPI layer, so the user does not have to define the ext_flash_* functions.

SPI functions, instead, must be defined. Example SPI drivers are available for multiple platforms in the hal/spi directory.

UART bridge towards neighbor systems

Another alternative available to map external devices consists in enabling a UART bridge towards a neighbor system. The neighbor system must expose a service through the UART interface that is compatible with the wolfBoot protocol.

In the same way as for SPI devices, the ext_flash_* API is automatically defined by wolfBoot when the option UART_FLASH=1 is used.

For more details, see the manual page Remote External flash memory support via UART

Encryption support for external partitions

When update and swap partitions are mapped to an external device using EXT_FLASH=1, either in combination with SPI_FLASH, UART_FLASH, or any custom external mapping, it is possible to enable ChaCha20 encryption when accessing those partition from the bootloader. The update images must be pre-encrypted at the source using the key tools, and wolfBoot should be instructed to use a temporary ChaCha20 symmetric key to access the content of the updates.

For more details about this optional feature, please refer to the Encrypted external partitions manual page.

Executing flash access code from RAM

On some platform, flash access code requires to be executed from RAM, to avoid conflict e.g. when writing to the same device where wolfBoot is executing, or when changing the configuration of the flash itself.

To move all the code accessing the internal flash for writing, into a section in RAM, use the compile time option RAM_CODE=1 (on some hardware configurations this is required for the bootloader to access the flash for writing).

Enable Dual-bank hardware-assisted swapping

When supported by the target platform, hardware-assisted dual-bank swapping can be used to perform updates. To enable this functionality, use DUALBANK_SWAP=1. Currently, only STM32F76x and F77x support this feature.

Store UPDATE partition flags in a sector in the BOOT partition

By default, wolfBoot keeps track of the status of the update procedure to the single sectors in a specific area at the end of each partition, dedicated to store and retrieve a set of flags associated to the partition itself.

In some cases it might be helpful to store the status flags related to the UPDATE partition and its sectors in the internal flash, alongside with the same set of flags used for the BOOT partition. By compiling wolfBoot with the FLAGS_HOME=1 makefile option, the flags associated to the UPDATE partition are stored in the BOOT partition itself.

While on one hand this option slightly reduces the space available in the BOOT partition to store the firmware image, it keeps all the flags in the BOOT partition.

Flash Erase value / Flag logic inversion

By default, most NVMs set the content of erased pages to 0xFF (all ones).

Some FLASH memory models use inverted logic for erased page, setting the content to 0x00 (all zeroes) after erase.

For these special cases, the option FLAGS_INVERT = 1 can be used to modify the logic of the partition/sector flags used in wolfBoot.

You can also manually override the fill bytes using FILL_BYTE= at build-time. It default to 0xFF, but will use 0x00 if FLAGS_INVERT is set.

Note: if you are using an external FLASH (e.g. SPI) in combination with a flash with inverted logic, ensure that you store all the flags in one partition, by using the FLAGS_HOME=1 option described above.

Using One-time programmable (OTP) flash as keystore

By default, keys are directly incorporated in the firmware image. To store the keys in a separate, one-time programmable (OTP) flash memory, use the FLASH_OTP_KEYSTORE=1 option. For more information, see /docs/OTP-keystore.md.

Prefer multi-sector flash erase operations

wolfBoot HAL flash erase function must be able to handle erase lengths larger than WOLFBOOT_SECTOR_SIZE, even if the underlying flash controller does not. However, in some cases, wolfBoot defaults to iterating over a range of flash sectors and erasing them one at a time. Setting the FLASH_MULTI_SECTOR_ERASE=1 config option prevents this behavior when possible, configuring wolfBoot to instead prefer a single HAL flash erase invocation with a larger erase length versus the iterative approach. On targets where multi-sector erases are more performant, this option can be used to dramatically speed up the image swap procedure.

Using Mac OS/X

If you see 0xC3 0xBF (C3BF) repeated in your factory.bin then your OS is using Unicode characters.

The "tr" command for assembling the 0xFF padding between "bootloader" ... 0xFF ... "application" = factory.bin, which requires the "C" locale.

Set this in your terminal

LANG=
LC_COLLATE="C"
LC_CTYPE="C"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=

Then run the normal make steps.