STMicroelectronics delivers a TFM application to test and understand its implementation of the Trusted Firmware. Let’s see what the first run of the Trusted Firmware application provides.
The second article of the series on Trusted Firmware shows how to import the TFM application of STMicroelectronics and the first run of the application on a STM32L562E-DK.
The other articles of the series are:
- Security management with Trusted Firmware presents the theoretical part of Trusted Firmware and of Secure Boot and Secure Firmware Update
- (This article) First run of the Trusted Firmware (TFM) application presents the first run of the TFM application
- Focus on the Secure Storage service of Trusted Firmware (TFM) focuses on the Secure Storage service
Tools installation
The TFM application is available for STM32CubeIDE, Keil and IAR. In this example STM32CubeIDE is going to be used as it is provided for free, does not require any license and integrates STM32CubeMX and STM32CubeProgrammer which facilitates the configuration of the micro-controller and its programming.
STMicroelectronics provides, through its STM32Cube ecosystem, every tool necessary to compile the TFM application and to program a development kit.
To avoid any path issue, it is recommended to install these tools (at least the IDE) in a path not containing spaces, i.e. “C:\ST\”. The installation steps are:
- Download and install STM32CubeIDE (v1.7.0) which is the IDE of STMicroelectronics.
- Download and install STM32CubeProgrammer (v2.8.1) which is the programmer of STMicroelectronics. STM32CubeIDE already contains it but having it as stand-alone tool facilitates its use.
- If you use Microsoft Windows 7, 8 or 10, download and install STSW-LINK009 (v2.0.2) which is the USB driver for the embedded STLINK-V3.
- Download and install Tera Term (v4.106) which is used to display the TFM application output. This tool is accessible for free and understands the ymodem protocol that STMicroelectronics uses.
Import code in STM32CubeIDE
The TFM source code example contains three subprojects:
- TFM_Appli contains two applications:
- TFM_Appli_Secure is the secure application, combining the PSA updatable Root of Trust and the application updatable Root of Trust
- TFM_Appli_NonSecure is the non-secure application
- TFM_Loader contains an optional Ymodem local loader application that runs in non-secure environment and allows to load new image from Tera Term; it is not strictly required by TFM but is an add-on for testing the firmware update
- TFM_SBSFU_Boot is the PSA immutable Root of Trust and contains MCUboot
These subprojects can be imported in STM32CubeIDE after having installed the STM32Cube MCU & MPU Package for the STM32L5 (v1.4.0). To do so in STM32CubeIDE, open “Help > Manage Embedded Software Packages”, scroll down until “STM32L5” and select version 1.4.0. Finally, click on “Install Now”.
The import procedure of the code examples must be repeated for all three subprojects. In STM32CubeIDE:
- Open “File > Import…”
- Select “General > Import STM32Cube Example”
- In field “Name”, write “TFM”
- Select one of the three projects (the procedure must anyway be repeated for all three of them), e.g. TFM_Appli. On the right panel of the window, select the project example targeting the STM32L562E-DK and click Next.
- Select the location where to store the project (or leave the default location) and click Finish.
Repeat the same process for TFM_SBSFU_Boot and TFM_Loader. After importing the three subprojects, the Project Explorer of STM32CubeIDE should look like the following image.
Building and programming
Step 1: Initialize the STM32L5
The STM32L562E-DK must be initialized first before being able to load the TFM applications.
Connect the STM32L562E-DK via USB to the computer. Use the micro-USB port named “STLK USB” on the right side of the board. STMicroelectronics suggests using a script to do this automatically, which is a good idea to avoid any wrong configuration [UM2671, §10.1]. Simply drag and drop the script
C:\Users\…\STM32Cube\Repository\STM32Cube_FW_L5_V1.4.0\Projects\STM32L562E-DK\Applications\TFM\TFM_SBSFU_Boot\STM32CubeIDE\regression.sh
in STM32CubeIDE and verify that the script has been correctly executed!
The configuration can be manually verified by connecting the STM32CubeProgrammer. Select the Hot Plug Mode and click Connect. The programmer should show the following target information.
The configuration is present in the OB (Option Bytes) tabulation. The table below summarises the configuration present in [UM2671, §10.1] and that should now be applied to your board.
Category | Option | Value |
Read Out Protection | RDP | AA |
User Configuration | SWAP_BANK | Unchecked |
DBANK | Checked | |
SRAM2_RST | Unchecked | |
TZEN | Checked | |
HDP1EN | Unchecked | |
HDP1_PEND | Value 0x00, Address 0x08000000 | |
HDP2EN | Unchecked | |
HDP2_PEND | Value 0x00, Address 0x08000000 | |
SECBOOTADD0 | Value 0x180052, Address 0x0c002900 | |
Secure Area 1 | SECWM1_PSTRT | Value 0x00, Address 0x08000000 |
SECWM1_PEND | Value 0x7f, Address 0x0803f800 | |
Write Protection 1 | WRP1A_PSTRT | Value 0x7f, Address 0x0803f800 |
WRP1A_PEND | Value 0x00, Address 0x08000000 | |
WRP1B_PSTRT | Value 0x7f, Address 0x0803f800 | |
WRP1B_PEND | Value 0x00, Address 0x08000000 | |
Secure Area 2 | SECWM2_PSTRT | Value 0x00, Address 0x08040000 |
SECWM2_PEND | Value 0x7f, Address 0x0807f800 | |
Write Protection 2 | WRP2A_PSTRT | Value 0x7f, Address 0x0807f800 |
WRP2A_PEND | Value 0x00, Address 0x08040000 | |
WRP2B_PSTRT | Value 0x7f, Address 0x0807f800 | |
WRP2B_PEND | Value 0x00, Address 0x08040000 |
Step 2: Compile the software
The compilation procedure requires 4 sub steps [UM2671, §10.2] as depicted in Figure 24.
Build TFM_SBSFU_Boot application
The file boot_hal_cfg.h contains the configuration of the TFM_SBSFU_Boot application. However, when importing the project using STM32CubeIDE as we did earlier in this article, the configuration file is not added to the workspace. This can fortunately be fixed easily:
- Create a new subfolder to folder “Application”:
- Name this new subfolder “Include” and link it to folder “PROJECT_LOC/../Inc”:
Once done, open the configuration file. According to [UM2671, §10.1], the user should verify its content and adapt it based on his/her needs:
- Change the read protection option byte to level 0; this allows reading the secure or protected areas of the device with the JTAG debugger and deactivates the SRAM2 protection against intrusion.
In addition, make sure that flag TFM_DEV_MODE is present in the “Paths and Symbols”; this makes the software compile in development mode, allowing some automatic configurations of the static protections and logging of TFM_SBSFU_Boot on the terminal emulation. To do so, open the properties of the project and then “C/C++ General > Paths and Symbols > Symbols” as shown in figure below.
Of course, this configuration does not ensure a secured device, but let’s keep in mind that this is a first attempt to use and understand the trusted firmware. The configuration will be hardened later. The diff of the modifications is:
diff --git a/TFM_SBSFU_Boot/Inc/boot_hal_cfg.h b/TFM_SBSFU_Boot/Inc/boot_hal_cfg.h index c3e1ce2c5e26453330583333876737d20aa4eca0..720d97e458b3293c16049822530542fcc669190d 100644 --- a/TFM_SBSFU_Boot/Inc/boot_hal_cfg.h +++ b/TFM_SBSFU_Boot/Inc/boot_hal_cfg.h @@ -39,7 +39,7 @@ /* Static protection checking Flag */ #define TFM_WRP_PROTECT_ENABLE /*!< Write Protection */ #define TFM_HDP_PROTECT_ENABLE /*!< HDP protection */ -#define TFM_OB_RDP_LEVEL_VALUE OB_RDP_LEVEL_1 /*!< RDP level */ +#define TFM_OB_RDP_LEVEL_VALUE OB_RDP_LEVEL_0 /*!< RDP level */ #define TFM_SECURE_USER_SRAM2_ERASE_AT_RESET /*!< SRAM2 clear at Reset */ #ifdef TFM_DEV_MODE #define TFM_OB_BOOT_LOCK 0 /*!< BOOT Lock expected value */
An additional modification is still required because the postbuild script (postbuild.sh) contains some errors that must be corrected. Edit the file according to these changes:
diff --git a/TFM_SBSFU_Boot/STM32CubeIDE/postbuild.sh b/TFM_SBSFU_Boot/STM32CubeIDE/postbuild.sh index 58145eb8abe309eadff1d201579daa6385dc29ca..da3b5ba0bbb30c5074274d2633413da7cfe9448f 100644 --- a/TFM_SBSFU_Boot/STM32CubeIDE/postbuild.sh +++ b/TFM_SBSFU_Boot/STM32CubeIDE/postbuild.sh @@ -11,7 +11,7 @@ postbuild=$projectdir/../../TFM_Appli/STM32CubeIDE/postbuild.sh current_directory=`pwd` echo $current_directory -cd $projectdir"/../../Middlewares/Third_Party/mcuboot" +cd $projectdir"/../Middlewares/Third_Party/mcuboot" mcuboot=`pwd` cd $current_directory @@ -196,7 +196,6 @@ if [ $ret != 0 ]; then echo "postbuild.sh failed" exit 1 fi -echo $hardeningbat" updated" echo $hardening" updated" echo $postbuild" updated" exit 0
Finally, build the project by clicking on “Project > Build Project”:
The release folder should contain a binary file named TFM_SBSFU_Boot.bin.
Build TFM_Appli secure and TFM_Appli non-secure applications
The build of the TFM_Appli Secure and Non-Secure applications do not require any specific configuration. The Secure application must be built first and the Non-Secure application second (click on “Project > Build Project”, for each project, as shown in the previous section).
At the end of the build, the respective release folder should contain a binary file; TFM_Appli_Secure.bin for the Secure application and TFM_Appli_NonSecure.bin for the Non-Secure application. In addition, the postbuild script creates two new binaries per application, thus four in total: the first is a signed binary and the second is a signed and encrypted binary. The script saves them in a folder named “Binary” in the root directory of TFM_Appli. The script currently uses default signature and encryption keys. Let’s keep them to ease the test of TFM and change them on a second stage.
Build TFM_Loader application
Similar to the build of TFM_Appli applications, the build of TFM_Loader is quite easy; just click on “Project > Build Project”. The release folder should contain the binary file TFM_Loader.bin. Another similarity is that a postbuild script creates a derived binary (the loader image) and saves it in a folder named “Binary” in the root directory of TFM_Loader.
Step 3: Program the firmware into the STM32L5 microcontroller
The project provides a script (TFM_UPDATE.sh) that programs all binaries generated during the build process [UM2671, §10.3]. The script is updated during the postbuild of TFM_SBSFU_Boot application to ensure that binaries are programmed at the correct memory locations.
If you installed the tools in the non-default path, two little changes to the script are necessary as it contains the full path to STM32CubeProgrammer. Modify the value of PATH and external_loader as suggested in the patch below.
diff --git a/TFM_SBSFU_Boot/STM32CubeIDE/TFM_UPDATE.sh b/TFM_SBSFU_Boot/STM32CubeIDE/TFM_UPDATE.sh index fe5d7cbaef08284da59f010c188c14356a13223b..c58e9e0e86effddb2e307b14a0587e1a877da2f4 100644 --- a/TFM_SBSFU_Boot/STM32CubeIDE/TFM_UPDATE.sh +++ b/TFM_SBSFU_Boot/STM32CubeIDE/TFM_UPDATE.sh @@ -4,9 +4,9 @@ echo "TFM_UPDATE started" SCRIPT=$(readlink -f $0) # Absolute path this script SCRIPTPATH=`dirname $SCRIPT` -PATH="/C/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/":$PATH +PATH="/C/ST/STM32CubeProgrammer/bin/":$PATH stm32programmercli="STM32_Programmer_CLI" -external_loader="C:\PROGRA~1\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader\MX25LM51245G_STM32L562E-DK.stldr" +external_loader="C:\ST\STM32CubeProgrammer\bin\ExternalLoader\MX25LM51245G_STM32L562E-DK.stldr" connect_no_reset="-c port=SWD mode=UR -el $external_loader" connect="-c port=SWD mode=UR --hardRst -el $external_loader" slot0=0xc017000
Finally, connect the STM32F562E-DK and execute TFM_UPDATE.sh. Verify that the script was correctly executed, it must display:
[…] TFM_Appli Secure Written […] TFM_Appli NonSecure Written […] TFM_Loader Written […] TFM_SBSFU_Boot Written […] TFM_UPDATE script Done, press a key
First Run
Open Tera Term and configure the new connection [UM2671, §10.5] with:
- Serial
- Port: “COMx: STMicroelectronics STLink Virtual COM Port”
Then, click on menu “Setup”, choose “Serial port…” and configure the serial port with:
- Port: same as chosen previously
- Speed: 115’200
- Data: 8 bit
- Parity: none
- Stop bits: 1 bit
- Flow control: none
Click on “New setting” to validate.
Press the reset button on the board. As the security mechanisms have been configured on purpose to the lowest level, the board shouldn’t have any problem to boot because the RDP (read protection option bytes) was set to level 0. The TFM_SBSFU_Boot application starts, verifies the configuration, and corrects it if necessary as we set the flag TFM_DEV_MODE. The first boot should look like
[INF] BANK 1 secure flash [0, 117] : OB [0, 127] [ERR] Unexpected value for secure flash protection: set wmsec1 [INF] BANK 2 secure flash [127, 0] : OB [0, 127] [INF] BANK 1 flash write protection [4, 35] : OB [127, 0] [ERR] Unexpected value for write protection : set wrp1 [INF] BANK 2 flash write protection [116, 127] : OB [127, 0] [ERR] Unexpected value for write protection : set wrp2 [INF] BANK 1 secure user flash [0, 34] : OB [0, 0] [ERR] Unexpected value for secure user flash protection : set hdp1 [INF] Starting bootloader [INF] Initializing BL2 NV area : Power down/reset not supported... [INF] Init BL2 NV Header area: Done [INF] Initializing BL2 NV Counters [INF] Init BL2 NV counters to 0 : Done [INF] BL2 NV Area Initialized : Power Down/reset supported [INF] Checking BL2 NV area [INF] Checking BL2 NV area header [INF] Checking BL2 NV Counter consistency [INF] Consistent BL2 NV Counter 3 = 0x0 [INF] Consistent BL2 NV Counter 4 = 0x0 [INF] Swap type: none [INF] Swap type: none [INF] verify counter 0 1000000 0 [INF] counter 0 : ok [INF] verify sig key id 0 [INF] signature OK [INF] Counter 3 set to 0x1000000 [INF] verify counter 1 1000000 0 [INF] counter 1 : ok [INF] verify sig key id 1 [INF] signature OK [INF] 2f, fd, 66, 8e, b6, 87 , af ,8c, [INF] 62, e6, e3, f5, d1, b9 , f3 ,a3, [INF] Counter 4 set to 0x1000000 [INF] Bootloader chainload address offset: 0x17000 [INF] Jumping to the first image slot set to BL2 SHARED DATA2XX_HUK_CUSTOMIZATION_ [INF] Code c002900 c011bee [INF] hash TFM_SBSFU_Boot f27557 .. e8fc7444 [INF] otfdec key … [INF] otfdec key … [Sec Thread] Secure image initializing!
Then the non-secure application should take over and display
====================================================================== = (C) COPYRIGHT 2019 STMicroelectronics = = = = User App #A = ====================================================================== =================== Main Menu ============================ Test Protections -------------------------------------- 1 Test TFM ---------------------------------------------- 2 Selection :
Two options are available:
- “Test Protections” performs memory access in the secure area from the non-secure area. Of course, all attempts must fail for the test to succeed. Type 1 and 1 again on the next screen.
- “Test TFM” performs the test of some TF-M secure services. Type 2 and 0 on the next screen.
Follow the execution of Test AES-GCM
TFM has a very complex source code. Therefore, it is not the goal to understand it in this article. However, it may be interesting after this set up to understand some steps that TFM takes.
The short example below follows the execution of PSA_CONNECT()
for the test of AES-GCM:
- In main.c, function
main()
callstfm_app_menu()
to enter “Test TFM” after key 2 is pressed - In tfm_app.c, function
tfm_app_menu()
callspsa_aead_test()
to enter “AES GCM Test” after key 0 is pressed - In crypto_test_common.c, function
psa_aead_test()
callspsa_allocate_key()
to allocate a transient key - In tfm_crypto_ipc_api.c, function
psa_allocate_key()
callsPSA_CONNECT(TFM_CRYPTO)
to connect to PSA - In tfm_crypto_ipc_api.c, define
PSA_CONNECT()
callspsa_connect(TFM_CRYPTO_SID, TFM_CRYPTO_VERSION)
- In tfm_psa_ns_api.c, function
psa_connect()
callstfm_ns_interface_dispatch(tfm_psa_connect_veneer)
- In tfm_ns_lock.c, function
tfm_ns_interface_dispatch()
callstfm_psa_connect_veneer(…)
- In tfm_psa_api_veneers.c, function
tfm_psa_connect_veneer()
callsTFM_CORE_NS_IPC_REQUEST_VENEER(tfm_svcall_psa_connect)
- In tfm_psa_api_veneers.c, define
TFM_CORE_NS_IPC_REQUEST_VENEER
callstfm_core_ns_ipc_request(tfm_svcall_psa_connect)
- In tfm_psa_api_veneers.c, function
tfm_core_ns_ipc_request()
callstfm_core_ipc_request(tfm_svcall_psa_connect)
- In tfm_psa_api_veneers.c, function
tfm_core_ipc_request()
callsASM SVC TFM_SVC_IPC_REQUEST()
which is the Supervisor call: the “argument” is ignored by the processor but can be retrieved by the exception handler to determine which service is being calledASM BX LR
which is a branch and exchange instruction set operation: the address to jump to is held in register LR (Link Register) which is used to store the return address when a subroutine call is made
The Secure Application retrieves the supervisor call with:
- In tfm_core_svcalls_ipc.c, function
tfm_core_svc_handler()
callstfm_psa_ipc_request_handler(svc_args)
which handles IPC requests - In tfm_psa_api_veneers.c, function
tfm_psa_ipc_request_handler()
handles the request and calls function from SVC context
Conclusion
This article presented the set up of the STM32L562E-DK with the TFM application.
The set up itself was quite easy as it followed the steps given by the user manual. The good configuration and the use of TFM are, however, more difficult to master, and the code itself is quite complex to understand.
In the next article we will try to take advantage of TFM by using some secure services.
Pingback: Noser Blog Focus on the Secure Storage service of Trusted Firmware (TFM) - Noser Blog