This document gets you started with Trusted Firmware M (TF-M for short) for Nuvoton's platform. It walks you through:
- Constructing one reference TF-M build environment on Windows
- Building first TF-M for Nuvoton's platform
- Customizing TF-M code and updating into Mbed OS for Nuvoton's platform
Currently, Nuvoton supports two TF-M platforms: M2351 and M2354. Only M2354 enables TF-M/Mbed integration due to memory limitation. In the following, M2354 is chosen for demo.
To build TF-M, a POSIX-like build environment is needed. In the following, Windows/MSYS2 is chosen as reference for its easy setup. Below list necessary tools and their versions against which TF-M can build successfully with this document:
NOTE: For detailed TF-M software requirements, navigate TF-M. Then go through DOCS → Getting Started Guides → Software requirements.
NOTE: Native Windows emphasizes this tool is installed through normal Windows installer. DO NOT use MSYS2 Pacman version.
NOTE: MSYS2 Pacman means this tool is installed through MSYS2 Pacman package manager. Their native Windows versions may not be easy to find.
-
Host operating system: Windows 10 64-bit
-
MSYS2 20210604
NOTE: Running MSYS2 installer from Windows File Browser, if MSYS2 install window fails to pop up, try running this installer from Windows Command Prompt instead.
-
Git 2.30.1 (Native Windows)
NOTE: If you have
.gitconfig
inC:/Users/<WINDOWS-USER>
, also copy it toC:/msys64/home/<WINDOWS-USER>
for consistent behavior. Thie is because Git, when running in MSYS shell, seesC:/msys64/home/<WINDOWS-USER>
instead ofC:/Users/<WINDOWS-USER>
as home directory. -
CMake 3.19.3 (Native Windows)
-
Python3 3.8.7 (Native Windows)
- Python3 packages: Listed in
trusted-firmware-m/tools/requirements.txt
- Python3 packages: Listed in
-
Cross compiler: GNU Arm Embedded Toolchain 10.3-2021.07 (Native Windows) or Arm Compiler 6.12 (Native Windows)
NOTE: GNU Arm Embedded Toolchain 9 2020-q2-update and earlier built code FAILS to run with
-Os
. Avoid these toolchain versions. Check their bug report. NOTE: GNU Arm Embedded Toolchain 10-2020-q4-major built code FAILS to run. Avoid this toolchain version. Check its bug report. -
GNU make (MSYS2 Pacman)
-
SRecord (MSYS2 Pacman)
-
Host compiler: GCC (MSYS2 Pacman)
NOTE: This is needed for building PSA compliance test.
If your network is behind the firewall, configure proxy for the tools which need to communicate with outside. If not, just skip this step.
Edit the lines below to meet your environment, and add them at the end of C:/msys64/home/<WINDOWS-USER>/.bashrc
:
# Configure proxy for MSYS2 Pacman
export http_proxy=http://<USER>:<PASSWORD>@<PROXY-SERVER>:<PORT>/
export https_proxy=$http_proxy
export ftp_proxy=$http_proxy
export rsync_proxy=$http_proxy
# Configuring proxy for Python3 pip
#
# Luckily, Python3 pip also honors the environmental variables http_proxy/https_proxy, which have been set appropriately above.
#
# Configuring proxy for Git
#
# Luckily, Git also honors the environmental variables http_proxy/https_proxy, which have been set appropriately above.
#
MSYS2 strips the environmental variable PATH
for clean POSIX-like environment.
We need to add paths of the tools into MSYS2 shell manually.
Edit the lines below to meet your environment, and add them at the end of C:/msys64/home/<WINDOWS-USER>/.bashrc
:
# Arm Compiler 6
PATH="/C/Keil_v5/ARM/ARMCLANG/bin":$PATH
# GNU Arm Embedded Toolchain
PATH="/C/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.07/bin":$PATH
# CMake
PATH="/C/Program Files/CMake/bin":$PATH
# Python3
PATH="/C/Python38":"/C/Python38/Scripts":$PATH
# Git
PATH="/C/Program Files/Git/bin":$PATH
(Re-)run MSYS2 MinGW 64-bit shell from Windows Start menu. Install GNU make, SRecord, and x86_64 GCC through MSYS2 Pacman package manager:
$ pacman -S --needed make mingw-w64-x86_64-srecord mingw-w64-x86_64-gcc
This step is to confirm all tools are ready. If you are confident, just skip this step. In MSYS2 shell, check if all tools are present and of right versions:
$ which git; git version
/C/Program Files/Git/mingw64/bin
git version 2.30.1.windows.1
$ which cmake; cmake --version
/C/Program Files/CMake/bin/cmake
cmake version 3.19.3
$ which python; python --version; which pip; pip --version
/C/Python38/python
Python 3.8.7
/C/Python38/Scripts/pip
pip 21.1.2 from c:\python38\lib\site-packages\pip (python 3.8)
$ which arm-none-eabi-gcc; arm-none-eabi-gcc --version
/C/Program Files (x86)/GNU Arm Embedded Toolchain/10 2021.07/bin/arm-none-eabi-gcc
arm-none-eabi-gcc.exe (GNU Arm Embedded Toolchain 10.3-2021.07) 10.3.1 20210621 (release)
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ which armclang; armclang --version
/C/Keil_v5/ARM/ARMCLANG/bin/armclang
Product: MDK Plus 5.28
Component: ARM Compiler 6.12
Tool: armclang [5d624a00]
$ which make; make --version
/usr/bin/make
GNU Make 4.3
$ which srec_cat; srec_cat --version
/mingw64/bin/srec_cat
srec_cat version 1.64.D001
$ which gcc; gcc --version
/mingw64/bin/gcc
gcc.exe (Rev2, Built by MSYS2 project) 10.3.0
NOTE:
git-apply
patch could fail with line ending. Recommend enabling automatic line ending conversion if not.
$ git config --global core.autocrlf true
In MSYS2 shell, follow the instructions below to start building TF-M.
First, clone Nuvoton-forked TF-M source code and switch to the branch enabling Mbed integration:
$ git clone https://github.com/OpenNuvoton/trusted-firmware-m
$ cd trusted-firmware-m
$ git checkout -f nuvoton_mbed_m2354_tfm-1.3
Install necessary Python packages. This finalizes software requirements:
$ pip install -r tools/requirements.txt
In the example, we build TF-M enabling regression tests for M2354, using GCC.
NOTE: Run
rm -rdf cmake_build
first for fresh on rebuild.
NOTE: For detailed TF-M build instructions, navigate TF-M. Then go through DOCS → Getting Started Guides → Build instructions.
Build TF-M:
$ cmake -S . \
-B cmake_build \
-DTFM_PLATFORM=nuvoton/m2354 \
-DTFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake \
-DTFM_PSA_API=ON \
-DTFM_ISOLATION_LEVEL=2 \
-DTEST_S=ON \
-DTEST_NS=ON \
-G"Unix Makefiles"
$ cmake --build cmake_build -- install
We will get the following images in the directory cmake_build/bin
:
- bl2.bin: MCUboot bootloader binary
- tfm_s.bin: TF-M secure binary
- tfm_ns.bin: TF-M non-secure binary
- tfm_s_signed.bin: Signed TF-M secure binary
- tfm_ns_signed.bin: Signed TF-M non-secure binary
Combine cmake_build/bin/bl2.bin
, cmake_build/bin/tfm_s_signed.bin
, and cmake_build/bin/tfm_ns_signed.bin
into one, using SRecord:
$ srec_cat \
cmake_build/bin/bl2.bin -Binary -offset 0x0 \
cmake_build/bin/tfm_s_signed.bin -Binary -offset 0x20000 \
cmake_build/bin/tfm_ns_signed.bin -Binary -offset 0x70000 \
-o cmake_build/bin/bl2-tfm_s_signed-tfm_ns_signed.hex -Intel
Drag-n-drop cmake_build/bin/bl2-tfm_s_signed-tfm_ns_signed.hex
onto M2354 board to flash the image.
Configure terminal program with 115200/8-N-1, and you would see console log with:
[INF] Starting bootloader
[INF] Swap type: none
[INF] Swap type: none
[INF] Bootloader chainload address offset: 0x20000
[INF] Jumping to the first image slot
[Sec Thread] Secure image initializing!
Booting TFM v1.3.0
Non-Secure system starting...
In the section, we guide how to customize TF-M code and then update into Mbed for M2354.
First, clone Mbed OS source code and switch to the branch master
:
$ git clone https://github.com/ARMmbed/mbed-os
$ cd mbed-os
$ git checkout -f master
Since Mbed OS 6.1, it changes its TF-M integration mechanism and pre-imports TF-M stuff of specific version, possibly latest release.
The Mbed pre-imported TF-M version can be found at: mbed-os/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/VERSION.txt
.
For customizing TF-M code for M2354, the patched TF-M branch and the Mbed pre-imported TF-M version must match.
For example, the patched TF-M branch nuvoton_mbed_m2354_tfm-1.3
matches the Mbed pre-imported TF-M version TF-Mv1.3.0
.
If you find no such branch in Nuvoton forked trusted-firmware-m
repository, it means Nuvoton hasn't yet supported the TF-M version enabling integration with Mbed.
Currently, we support the following customization.
To define memory spec of Flash for TF-M/Mbed, search/change the line in
trusted-firmware-m/platform/ext/target/nuvoton/m2354/partition/flash_layout.h
:
/* Max Flash size for TF-M + bootloader header/trailer */
#define FLASH_S_PARTITION_SIZE (0x50000)
/* Max Flash size for Mbed + bootloader header/trailer */
#define FLASH_NS_PARTITION_SIZE (0x90000)
M2354 has 1024KiB Flash in total, among which 128KiB have been allocated for bootloader code and ITS/PS storage.
896KiB are left for FLASH_S_PARTITION_SIZE
and FLASH_NS_PARTITION_SIZE
.
FLASH_S_PARTITION_SIZE
for TF-M (secure) and FLASH_NS_PARTITION_SIZE
for Mbed (non-secure) respectively.
NOTE:
FLASH_S_PARTITION_SIZE
andFLASH_NS_PARTITION_SIZE
must be sector size (2KiB)-aligned.
To define memory spec of SRAM for TF-M/Mbed, search/change the line in
trusted-firmware-m/platform/ext/target/nuvoton/m2354/partition/region_defs.h
:
/* Max SRAM size for TF-M */
#define S_DATA_SIZE (96 * 1024)
/* Max SRAM size for Mbed = Total - Max SRAM size for TF-M */
#define NS_DATA_SIZE (TOTAL_RAM_SIZE - S_DATA_SIZE)
S_DATA_SIZE
for TF-M (secure) and NS_DATA_SIZE
for Mbed (non-secure) respectively.
NOTE:
S_DATA_SIZE
andNS_DATA_SIZE
must be 16KiB-aligned required by M2354 Security Configuration Unit (SCU).
After finished changing TF-M code for M2354, we can rebuild TF-M for integration with Mbed OS:
$ cmake -S . \
-B cmake_build \
-DTFM_PLATFORM=nuvoton/m2354 \
-DTFM_TOOLCHAIN_FILE=toolchain_GNUARM.cmake \
-DTFM_PSA_API=ON \
-DTFM_ISOLATION_LEVEL=2 \
-G"Unix Makefiles"
$ cmake --build cmake_build -- install
NOTE: For integration with Mbed, we don't enable TF-M regression tests (
TEST_S
/TEST_NS
).
In the path mbed-os/targets/TARGET_NUVOTON/TARGET_M2354/TARGET_TFM/TARGET_NU_M2354/COMPONENT_TFM_S_FW
(MBED_M2354_TFM_IMPORT
for short),
the stuffs listed below are exported by TF-M and are to update into Mbed for M2354:
-
bl2.bin: MCUboot bootloader binary
-
tfm_s.bin: TF-M secure binary
-
s_veneers.o: TF-M secure gateway library
-
partition/: Flash layout for image signing and concatenating in post-build process
-
signing_key/: TF-M secure boot signing key
Below summarize the copy paths from TF-M into Mbed:
trusted-firmware-m/cmake_build/install/outputs/NUVOTON/M2354/bl2.bin
→MBED_M2354_TFM_IMPORT/bl2.bin
trusted-firmware-m/cmake_build/install/outputs/NUVOTON/M2354/tfm_s.bin
→MBED_M2354_TFM_IMPORT/tfm_s.bin
trusted-firmware-m/cmake_build/install/interface/lib/s_veneers.o
→MBED_M2354_TFM_IMPORT/s_veneers.o
trusted-firmware-m/platform/ext/target/nuvoton/m2354/partition/flash_layout.h
→MBED_M2354_TFM_IMPORT/partition/flash_layout.h
.trusted-firmware-m/platform/ext/target/nuvoton/m2354/partition/partition_M2354.h
→MBED_M2354_TFM_IMPORT/partition/partition_M2354_im.h
trusted-firmware-m/platform/ext/target/nuvoton/m2354/partition/region_defs.h
→MBED_M2354_TFM_IMPORT/partition/region_defs.h
For single image boot:
trusted-firmware-m/cmake_build/install/image_signing/layout_files/signing_layout_s_ns.o
→MBED_M2354_TFM_IMPORT/partition/signing_layout_preprocessed.h
trusted-firmware-m/cmake_build/install/image_signing/keys/root-RSA-3072.pem
→MBED_M2354_TFM_IMPORT/signing_key/nuvoton_m2354-root-rsa-3072.pem
→mbed-os/tools/targets/nuvoton_m2354-root-rsa-3072.pem
For multiple image boot:
trusted-firmware-m/cmake_build/install/image_signing/layout_files/signing_layout_s.o
→MBED_M2354_TFM_IMPORT/partition/signing_layout_s_preprocessed.h
trusted-firmware-m/cmake_build/install/image_signing/layout_files/signing_layout_ns.o
→MBED_M2354_TFM_IMPORT/partition/signing_layout_ns_preprocessed.h
trusted-firmware-m/cmake_build/install/image_signing/keys/root-RSA-3072.pem
→MBED_M2354_TFM_IMPORT/signing_key/nuvoton_m2354-root-rsa-3072.pem
→mbed-os/tools/targets/nuvoton_m2354-root-rsa-3072.pem
trusted-firmware-m/cmake_build/install/image_signing/keys/root-RSA-3072.pem_1
→MBED_M2354_TFM_IMPORT/signing_key/nuvoton_m2354-root-rsa-3072_1.pem
→mbed-os/tools/targets/nuvoton_m2354-root-rsa-3072_1.pem
NOTE: For single image boot, TF-M secure binary and Mbed non-secure binary are concatenated first and then signed together; for multiple image boot, TF-M secure binary and Mbed non-secure binary are signed separately.
NOTE: Prior to Mbed OS 6.14, single image boot is the default. Since Mbed OS 6.14, multiple image boot becomes the default.
NOTE: Some files are renamed.
NOTE: The
mbed-os/tools/targets
path is for legacy Mbed CLI build tool.
NOTE:
trusted-firmware-m/cmake_build/install/image_signing/keys/root-RSA-3072.pem
can be missing due to TF-M build tool issue. Try to get it fromtrusted-firmware-m/bl2/ext/mcuboot/root-RSA-3072.pem
instead if it is just the original source.
Now, we can compile Mbed programs for M2354 normally, specifying target name NU_M2354
.
In the section, advanced topics are addressed here.
TF-M supports secure boot using MCUboot.
Default RSA key pairs are placed in trusted-firmware-m/bl2/ext/mcuboot/root-RSA-*.pem
.
They are exclusively for development and cannot for production.
In the following, we guide how to change the signing keys, assuming default MCUboot configurations for M2354 below:
- MCUBOOT_SIGNATURE_TYPE: RSA
- MCUBOOT_SIGNATURE_KEY_LEN: 3072
- MCUBOOT_IMAGE_NUMBER: 2 (that is, multiple image boot)
- MCUBOOT_HW_KEY: True
NOTE: For detailed TF-M secure boot, navigate TF-M. Then go through DOCS → References → Secure boot.
NOTE: The above MCUboot configurations are the default and must be consistent across TF-M and Mbed. Don't change them rashly.
First generate two new RSA key pairs and override:
trusted-firmware-m/bl2/ext/mcuboot/root-RSA-3072.pem
trusted-firmware-m/bl2/ext/mcuboot/root-RSA-3072_1.pem
$ cd trusted-firmware-m
$ imgtool keygen -t rsa-3072 -k bl2/ext/mcuboot/root-RSA-3072.pem
$ imgtool keygen -t rsa-3072 -k bl2/ext/mcuboot/root-RSA-3072_1.pem
For MCUBOOT_HW_KEY
(True
), dump public key hash from the generated key pairs above:
$ openssl rsa -in bl2/ext/mcuboot/root-RSA-3072.pem -RSAPublicKey_out -outform DER |\
openssl dgst -sha256 -binary |\
hexdump -e '8/1 "0x%02x, " "\n"'
$ openssl rsa -in bl2/ext/mcuboot/root-RSA-3072_1.pem -RSAPublicKey_out -outform DER |\
openssl dgst -sha256 -binary |\
hexdump -e '8/1 "0x%02x, " "\n"'
And then override specific code in trusted-firmware-m/platform/ext/common/template/tfm_rotpk.c
:
#elif (MCUBOOT_SIGN_RSA_LEN == 3072)
/* Hash of public key: bl2/ext/mcuboot/root-rsa-3072.pem */
uint8_t rotpk_hash_0[ROTPK_HASH_LEN] = {
/* OVERRIDE ME */
};
/* Hash of public key: bl2/ext/mcuboot/root-rsa-3072_1.pem */
#if (MCUBOOT_IMAGE_NUMBER == 2)
uint8_t rotpk_hash_1[ROTPK_HASH_LEN] = {
/* OVERRIDE ME */
};
#endif /* MCUBOOT_IMAGE_NUMBER */
NOTE: The public key embedded into MCUboot is in PKCS1 format, so we specify
-RSAPublicKey_out
.
For MCUBOOT_HW_KEY
(False
), dump public key from the generated key pairs above:
$ imgtool getpub -k bl2/ext/mcuboot/root-RSA-3072.pem
$ imgtool getpub -k bl2/ext/mcuboot/root-RSA-3072_1.pem
And then override specific code in trusted-firmware-m/bl2/ext/mcuboot/keys.c
:
#elif MCUBOOT_SIGN_RSA_LEN == 3072
#define HAVE_KEYS
const unsigned char rsa_pub_key[] = {
/* OVERRIDE ME */
};
const unsigned int rsa_pub_key_len = /* OVERRIDE ME */;
#if (MCUBOOT_IMAGE_NUMBER == 2)
const unsigned char rsa_pub_key_1[] = {
/* OVERRIDE ME */
};
const unsigned int rsa_pub_key_len_1 = /* OVERRIDE ME */;
#endif
NOTE: For
MCUBOOT_SIGNATURE_KEY_LEN
(2048
), override thetrusted-firmware-m/bl2/ext/mcuboot/root-RSA-2048*.pem
file and theMCUBOOT_SIGN_RSA_LEN == 2048
code instead.
NOTE: For
MCUBOOT_IMAGE_NUMBER
(1
), skip the steps above with theroot-RSA-*_1.pem
file and theMCUBOOT_IMAGE_NUMBER == 2
code.
NOTE: Depending on
MCUBOOT_HW_KEY
, go dump public key hash or dump public key.
NOTE: For the change to take effect, we need to re-build TF-M and re-integrate with Mbed. Check above examples.