399 lines
19 KiB
ReStructuredText
399 lines
19 KiB
ReStructuredText
##############################
|
|
Trusted Firmware M secure boot
|
|
##############################
|
|
For secure devices it is security critical to enforce firmware authenticity to
|
|
protect against execution of malicious software. This is implemented by building
|
|
a trust chain where each step in the execution chain authenticates the next
|
|
step before execution. The chain of trust in based on a "Root of Trust" which
|
|
is implemented using asymmetric cryptography. The Root of Trust is a combination
|
|
of an immutable bootloader and a public key (ROTPK).
|
|
|
|
*******************************
|
|
Second stage bootloader in TF-M
|
|
*******************************
|
|
To implement secure boot functionality an external project MCUBoot has been
|
|
integrated to TF-M. For further information please refer to the
|
|
`MCUBoot homepage <https://www.mcuboot.com/>`__. Original source-code is available at
|
|
`GitHub <https://github.com/runtimeco/mcuboot>`__. This document contains
|
|
information about MCUBoot modifications and how MCUBoot has been integrated to
|
|
TF-M.
|
|
|
|
Bootloader is started when CPU is released from reset. It runs in secure mode.
|
|
It authenticates the firmware image by hash (SHA-256) and digital signature
|
|
(RSA-2048) validation. Public key, that the checks happens against, is built
|
|
into the bootloader image. Metadata of the image is delivered together with the
|
|
image itself in a header and trailer section. In case of successful
|
|
authentication, bootloader passes execution to the secure image. Execution never
|
|
returns to bootloader until next reset.
|
|
|
|
A default RSA key pair is stored in the repository, public key is in ``keys.c``
|
|
and private key is in ``root-rsa-2048.pem``.
|
|
.. Warning::
|
|
|
|
``DO NOT use them in production code, they are exclusively for testing!``
|
|
|
|
Private key must be stored in a safe place outside of the repository.
|
|
``Imgtool.py`` can be used to generate new key pairs.
|
|
|
|
The bootloader handles the secure and non-secure images as a single blob which
|
|
is contiguous in the device memory. At compile time these images are
|
|
concatenated and signed with RSA-2048 digital signature. Preparation of payload
|
|
is done by Python scripts: ``bl2/ext/mcuboot/scripts/``. At the end of a
|
|
successful build signed TF-M payload can be found in::
|
|
|
|
<build_dir>/install/outputs/fvp/tfm_sign.bin
|
|
|
|
*********************
|
|
Integration with TF-M
|
|
*********************
|
|
MCUBoot assumes a predefined memory layout which is described below (applicable
|
|
for AN521). It is mandatory to define slot 0, slot 1 and scratch partitions, but
|
|
their size can be changed::
|
|
|
|
- 0x0000_0000 - 0x0007_FFFF: BL2 bootloader - MCUBoot
|
|
- 0x0008_0000 - 0x000F_FFFF: Slot 0 : Single binary blob: Secure + Non-Secure
|
|
image; Primary memory partition
|
|
- 0x0008_0000 - 0x0008_03FF: Common image header
|
|
- 0x0008_0400 - 0x0008_xxxx: Secure image
|
|
- 0x0008_xxxx - 0x0010_03FF: Padding (with 0xFF)
|
|
- 0x0010_0400 - 0x0010_xxxx: Non-secure image
|
|
- 0x0010_xxxx - 0x0010_xxxx: Hash value(SHA256) and RSA signature
|
|
of combined image
|
|
|
|
- 0x0018_0000 - 0x0027_FFFF: Slot 1 : Secure + Non-Secure image; Secondary
|
|
memory partition, structured identically to slot
|
|
0
|
|
- 0x0028_0000 - 0x0037_FFFF: Scratch area, used during image swapping
|
|
|
|
**************************
|
|
Firmware upgrade operation
|
|
**************************
|
|
MCUBoot handles only the firmware authenticity check after start-up and the
|
|
firmware switch part of the firmware update process. Downloading the new version
|
|
of the firmware is out-of-scope for MCUBoot. MCUBoot supports two different ways
|
|
to switch to the new firmware and it is assumed that firmware images are
|
|
executed-in-place (XIP). The default behaviour is the image swapping. In this
|
|
case active firmware is always executed from slot 0 and slot 1 is a staging area
|
|
for new images. Before executing the new firmware image, the content of the two
|
|
memory slots must be physically swapped. The other option is the non-swapping
|
|
version, which eliminates the complexity of image swapping and its
|
|
administration. Active image can be executed from either memory slot, but new
|
|
firmware must be linked to the address space of the proper (currently inactive)
|
|
memory slot.
|
|
|
|
Swapping operation
|
|
==================
|
|
Active image is stored in slot 0, and this image is started always by the
|
|
bootloader. Therefore images must be linked to slot 0. If the bootloader finds a
|
|
valid image in slot 1, which is marked for upgrade, then contents of slot 0 and
|
|
slot 1 will be swapped, before starting the new image from slot 0. Scratch area
|
|
is used as a temporary storage place during image swapping. Update mark from
|
|
slot 1 is removed when the swapping is successful. The boot loader can revert
|
|
the swapping as a fall-back mechanism to recover the previous working firmware
|
|
version after a faulty update. The swap operation is fail-safe and resistant to
|
|
power-cut failures. For more details please refer to the MCUBoot
|
|
`documentation <https://www.mcuboot.com/mcuboot/design.html>`__.
|
|
|
|
Non-swapping operation
|
|
======================
|
|
This operation can be turned on with ``MCUBOOT_NO_SWAP`` compile time switch
|
|
(see `Build time configuration`_). When enabling non-swapping operation then the
|
|
active image flag is moved between slots during firmware upgrade. If firmware is
|
|
executed-in-place (XIP), then two firmware images must be generated.
|
|
One of them is linked to be executed from slot 0 memory region and the other
|
|
from slot 1. The firmware upgrade client, which downloads the new image, must be
|
|
aware, which slot hosts the active firmware and which acts as a staging area and
|
|
it is responsible for downloading the proper firmware image. At boot time
|
|
MCUBoot inspects the version number in the image header and passes execution to
|
|
the newer firmware version. New image must be marked for upgrade which is
|
|
automatically done by Python scripts at compile time. Image verification is done
|
|
the same way in both operational modes. If new image fails during authentication
|
|
then MCUBoot erases the memory slot and starts the other image, after successful
|
|
authentication.
|
|
|
|
At build time automatically two binaries are generated::
|
|
|
|
<build_dir>/install/outputs/fvp/tfm_s_ns_signed_0.bin : Image linked for slot 0
|
|
memory partition
|
|
<build_dir>/install/outputs/fvp/tfm_s_ns_signed_1.bin : Image linked for slot 1
|
|
memory partition
|
|
|
|
RAM Loading firmware upgrade
|
|
============================
|
|
Musca A1 supports an image upgrade mode that is separate to both the swapping
|
|
and non-swapping modes. This is the ``RAM loading`` mode (please refer to the
|
|
table below). Like the non-swapping mode, this selects the newest image by
|
|
reading the image version numbers in the image headers, but instead of
|
|
executing it in place, the newest image is copied to RAM for execution. The
|
|
load address, the location in RAM where the image is copied to, is stored
|
|
in the image header.
|
|
|
|
Summary of different modes for image upgrade
|
|
============================================
|
|
Different implementations of the image upgrade operation (whether through
|
|
swapping, non-swapping, or loading into RAM and executing from there) are
|
|
supported by the platforms. The table below shows which of these
|
|
modes are supported by which platforms:
|
|
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
| | Without BL2 [1]_| With BL2 [2]_| With BL2 [2]_| With BL2 [2]_ |
|
|
+============+=================+==============+==============+=================+
|
|
| | XIP | XIP | XIP | Not XIP |
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
| | | Swap [3]_ | No-swap [4]_ | RAM loading [5]_|
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
| AN521 | Yes | Yes | Yes | No |
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
| AN519 | Yes | Yes | Yes | No |
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
| Musca-A1 | No | No | No | Yes |
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
| Musca-B1 | Yes | No | Yes | No |
|
|
+------------+-----------------+--------------+--------------+-----------------+
|
|
|
|
.. [1] To disable BL2, please turn off the ``BL2`` compiler switch in the
|
|
top-level configuration files or in the command line
|
|
|
|
.. [2] BL2 is enabled by default
|
|
|
|
.. [3] The image executes in-place (XIP) and is in swapping mode for image
|
|
update by default
|
|
|
|
.. [4] To enable XIP No-swap, set the configuration variable ``MCUBOOT_NO_SWAP``
|
|
to ``True`` in the top-level configuration files, or include the
|
|
``MCUBOOT_NO_SWAP`` macro in the command line
|
|
|
|
.. [5] To enable RAM loading, set the configuration variable
|
|
``MCUBOOT_RAM_LOADING`` to ``True`` in the top-level configuration files, or
|
|
include the ``MCUBOOT_RAM_LOADING`` macro in the command line
|
|
|
|
************************
|
|
Build time configuration
|
|
************************
|
|
MCUBoot related compile time switches can be set in the high level build
|
|
configuration files::
|
|
|
|
ConfigDefault.cmake
|
|
ConfigCoreTest.cmake
|
|
ConfigRegression.cmake
|
|
|
|
Compile time switches:
|
|
|
|
- BL2 (default: True):
|
|
- **True:** TF-M built together with bootloader. MCUBoot is executed after
|
|
reset and it authenticates TF-M and starts secure code.
|
|
- **False:** TF-M built without bootloader. Secure image linked to the
|
|
beginning of the device memory and executed after reset. If it is false
|
|
then using any of the further compile time switches are invalid.
|
|
- MCUBOOT_NO_SWAP (default: False):
|
|
- **True:** Activate non-swapping firmware upgrade operation.
|
|
- **False:** Original firmware upgrade operation with image swapping.
|
|
- MCUBOOT_RAM_LOADING (default: False):
|
|
- **True:** Activate RAM loading firmware upgrade operation, where latest
|
|
image is copied to RAM and runs from there instead of being executed
|
|
in-place.
|
|
- **False:** Original firmware upgrade operation with image swapping.
|
|
|
|
Image versioning
|
|
================
|
|
An image version number is written to its header by one of the python scripts,
|
|
and this number is used by the bootloader when the non-swapping mode is
|
|
enabled.
|
|
|
|
The version number of the image can manually be passed in through the command
|
|
line in the cmake configuration step::
|
|
|
|
cmake -G"Unix Makefiles" -DTARGET_PLATFORM=AN521 -DCOMPILER=ARMCLANG -DIMAGE_VERSION=1.2.3+4 ../
|
|
|
|
Alternatively, the version number can be less specific (e.g 1, 1.2, or 1.2.3),
|
|
where the missing numbers are automatically set to zero. The image version
|
|
number argument is optional, and if it is left out, then the version numbers of
|
|
the image(s) being built in the same directory will automatically change. In
|
|
this case, the last component (the build number) automatically increments from
|
|
the previous one: 0.0.0+1 -> 0.0.0+2, for as many times as the build is re-ran,
|
|
**until a number is explicitly provided**. If automatic versioning is in place
|
|
and then an image version number is provided for the first time, the new number
|
|
will take precedence and be used instead. All subsequent image versions are
|
|
then set to the last number that has been specified, and the build number would
|
|
stop incrementing. Any new version numbers that are provided will overwrite
|
|
the previous one: 0.0.0+1 -> 0.0.0+2. Note: To re-apply automatic image
|
|
versioning, please start a clean build without specifying the image version
|
|
number at all.
|
|
|
|
************************
|
|
Testing firmware upgrade
|
|
************************
|
|
As downloading the new firmware image is out of scope for MCUBoot, the update
|
|
process is started from a state where the original and the new image are already
|
|
programmed to the appropriate memory slots. To generate the original and a new
|
|
firmware package, TF-M is built twice with different build configurations.
|
|
|
|
Swapping firmware upgrade
|
|
=========================
|
|
Run TF-M build twice with two different build configuration: default and
|
|
regression. Save the artefacts between builds, because second run can overwrite
|
|
original binaries. Download default build to slot 0 and regression build to
|
|
slot 1.
|
|
|
|
Executing firmware upgrade on FVP_MPS2_AEMv8M
|
|
---------------------------------------------
|
|
.. code-block:: bash
|
|
|
|
<DS5_PATH>/sw/models/bin/FVP_MPS2_AEMv8M \
|
|
--parameter fvp_mps2.platform_type=2 \
|
|
--parameter cpu0.baseline=0 \
|
|
--parameter cpu0.INITVTOR_S=0x10000000 \
|
|
--parameter cpu0.semihosting-enable=0 \
|
|
--parameter fvp_mps2.DISABLE_GATING=0 \
|
|
--parameter fvp_mps2.telnetterminal0.start_telnet=1 \
|
|
--parameter fvp_mps2.telnetterminal1.start_telnet=0 \
|
|
--parameter fvp_mps2.telnetterminal2.start_telnet=0 \
|
|
--parameter fvp_mps2.telnetterminal0.quiet=0 \
|
|
--parameter fvp_mps2.telnetterminal1.quiet=1 \
|
|
--parameter fvp_mps2.telnetterminal2.quiet=1 \
|
|
--application cpu0=<build_dir>/bl2/ext/mcuboot/mcuboot.axf \
|
|
--data cpu0=<default_build_dir>/install/outputs/fvp/tfm_s_ns_signed.bin@0x10080000 \
|
|
--data cpu0=<regresssion_build_dir>/install/outputs/fvp/tfm_s_ns_signed.bin@0x10180000
|
|
|
|
Executing firmware upgrade on SSE 200 FPGA on MPS2 board
|
|
--------------------------------------------------------
|
|
|
|
::
|
|
|
|
TITLE: Versatile Express Images Configuration File
|
|
[IMAGES]
|
|
TOTALIMAGES: 3 ;Number of Images (Max: 32)
|
|
IMAGE0ADDRESS: 0x00000000
|
|
IMAGE0FILE: \Software\mcuboot.axf ; BL2 bootloader
|
|
IMAGE1ADDRESS: 0x10080000
|
|
IMAGE1FILE: \Software\tfm_sig1.bin ; TF-M example application binary blob
|
|
IMAGE2ADDRESS: 0x10180000
|
|
IMAGE2FILE: \Software\tfm_sig2.bin ; TF-M regression test binary blob
|
|
|
|
The following message will be shown in case of successful firmware upgrade,
|
|
``Swap type: test`` indicates that images were swapped:
|
|
|
|
::
|
|
|
|
[INF] Image 0: magic=good, copy_done=0xff, image_ok=0xff
|
|
[INF] Scratch: magic=bad, copy_done=0x5, image_ok=0xcf
|
|
[INF] Boot source: slot 0
|
|
[INF] Swap type: test
|
|
[INF] Bootloader chainload address offset: 0x80000
|
|
[INF] Jumping to the first image slot
|
|
[Sec Thread] Secure image initializing!
|
|
|
|
Execute test suites for the secure storage service
|
|
--------------------------------------------------
|
|
Running Test Suite SST secure interface tests (TFM_SST_TEST_2XXX)....
|
|
|
|
Non-swapping firmware upgrade
|
|
=============================
|
|
Follow the same instructions as in case of swapping build including these
|
|
changes:
|
|
|
|
- Set MCUBOOT_NO_SWAP compile time switch to true before build.
|
|
- Increase the image version number between the two build run.
|
|
|
|
Executing firmware upgrade on FVP_MPS2_AEMv8M
|
|
---------------------------------------------
|
|
|
|
.. code-block:: bash
|
|
|
|
<DS5_PATH>/sw/models/bin/FVP_MPS2_AEMv8M \
|
|
--parameter fvp_mps2.platform_type=2 \
|
|
--parameter cpu0.baseline=0 \
|
|
--parameter cpu0.INITVTOR_S=0x10000000 \
|
|
--parameter cpu0.semihosting-enable=0 \
|
|
--parameter fvp_mps2.DISABLE_GATING=0 \
|
|
--parameter fvp_mps2.telnetterminal0.start_telnet=1 \
|
|
--parameter fvp_mps2.telnetterminal1.start_telnet=0 \
|
|
--parameter fvp_mps2.telnetterminal2.start_telnet=0 \
|
|
--parameter fvp_mps2.telnetterminal0.quiet=0 \
|
|
--parameter fvp_mps2.telnetterminal1.quiet=1 \
|
|
--parameter fvp_mps2.telnetterminal2.quiet=1 \
|
|
--application cpu0=<build_dir>/bl2/ext/mcuboot/mcuboot.axf \
|
|
--data cpu0=<default_build_dir>/install/outputs/fvp/tfm_s_ns_signed_0.bin@0x10080000 \
|
|
--data cpu0=<regresssion_build_dir>/install/outputs/fvp/tfm_s_ns_signed_1.bin@0x10180000
|
|
|
|
Executing firmware upgrade on SSE 200 FPGA on MPS2 board
|
|
--------------------------------------------------------
|
|
|
|
::
|
|
|
|
TITLE: Versatile Express Images Configuration File
|
|
[IMAGES]
|
|
TOTALIMAGES: 3 ;Number of Images (Max: 32)
|
|
IMAGE0ADDRESS: 0x00000000
|
|
IMAGE0FILE: \Software\mcuboot.axf ; BL2 bootloader
|
|
IMAGE1ADDRESS: 0x10080000
|
|
IMAGE1FILE: \Software\tfm_sig0.bin ; TF-M example application binary blob
|
|
IMAGE2ADDRESS: 0x10180000
|
|
IMAGE2FILE: \Software\tfm_sig1.bin ; TF-M regression test binary blob
|
|
|
|
Executing firmware upgrade on Musca-B1 board
|
|
--------------------------------------------
|
|
After two images have been built, they can be concatenated to create the
|
|
combined image using ``srec_cat``:
|
|
|
|
- Linux::
|
|
srec_cat bl2/ext/mcuboot/mcuboot.bin -Binary -offset 0xA000000 tfm_sign_0.bin -Binary -offset 0xA020000 tfm_sign_1.bin -Binary -offset 0xA0A0000 -o tfm.hex -Intel
|
|
|
|
- Windows::
|
|
srec_cat.exe bl2\ext\mcuboot\mcuboot.bin -Binary -offset 0xA000000 tfm_sign_0.bin -Binary -offset 0xA020000 tfm_sign_1.bin -Binary -offset 0xA0A0000 -o tfm.hex -Intel
|
|
|
|
The following message will be shown in case of successful firmware upgrade,
|
|
notice that image with higher version number (``version=1.2.3.5``) is executed:
|
|
|
|
::
|
|
|
|
[INF] Starting bootloader
|
|
[INF] Image 0: version=1.2.3.4, magic= good, image_ok=0xff
|
|
[INF] Image 1: version=1.2.3.5, magic= good, image_ok=0xff
|
|
[INF] Booting image from slot 1
|
|
[INF] Bootloader chainload address offset: 0x180000
|
|
[INF] Jumping to the first image slot
|
|
[Sec Thread] Secure image initializing!
|
|
|
|
Execute test suites for the Secure area
|
|
---------------------------------------
|
|
Running Test Suite SST secure interface tests (TFM_SST_TEST_2XXX)...
|
|
|
|
RAM loading firmware upgrade
|
|
============================
|
|
To enable RAM loading, please set ``MCUBOOT_RAM_LOADING`` to True (either in the
|
|
configuration file or through the command line), and then specify a destination
|
|
load address in RAM where the image can be copied to and executed from. The
|
|
``IMAGE_LOAD_ADDRESS`` macro must be specified in the target dependent files,
|
|
for example with Musca A1, its ``flash_layout.h`` file in the ``platform``
|
|
folder should include ``#define IMAGE_LOAD_ADDRESS #0x10020000``
|
|
|
|
Executing firmware upgrade on Musca-A1 board
|
|
--------------------------------------------
|
|
After two images have been built, they can be concatenated to create the
|
|
combined image using ``srec_cat``:
|
|
|
|
- Linux:
|
|
srec_cat bl2/ext/mcuboot/mcuboot.bin -Binary -offset 0x200000 tfm_sign_old.bin -Binary -offset 0x220000 tfm_sign_new.bin -Binary -offset 0x320000 -o tfm.hex -Intel
|
|
|
|
- Windows::
|
|
srec_cat.exe bl2\ext\mcuboot\mcuboot.bin -Binary -offset 0x200000 tfm_sign_old.bin -Binary -offset 0x220000 tfm_sign_new.bin -Binary -offset 0x320000 -o tfm.hex -Intel
|
|
|
|
The following message will be shown in case of successful firmware upgrade when,
|
|
RAM loading is enabled, notice that image with higher version number
|
|
(``version=0.0.0.2``) is executed:
|
|
|
|
::
|
|
|
|
[INF] Image 0: version=0.0.0.1, magic= good, image_ok=0xff
|
|
[INF] Image 1: version=0.0.0.2, magic= good, image_ok=0xff
|
|
[INF] Image has been copied from slot 1 in flash to SRAM address 0x10020000
|
|
[INF] Booting image from SRAM at address 0x10020000
|
|
[INF] Bootloader chainload address offset: 0x20000
|
|
[INF] Jumping to the first image slot
|
|
[Sec Thread] Secure image initializing!
|
|
|
|
--------------
|
|
|
|
*Copyright (c) 2018-2019, Arm Limited. All rights reserved.*
|