1. Article purpose[edit source]
This article explains how to protect the software loaded by main processor in the coprocessor. It will ensure the authentication of the loaded firmware.
2. Introduction[edit source]
The Linux OS through the remoteproc framework allows to load firmware and control remote processors. Thanks to Arm® Trustzone and resource isolation, it is possible to authenticate a firmware before loading it. It is required to ensure integrity of the running firmware. It could be required for a complete secure platform.
3. Principle[edit source]
The protection of the coprocessor firmware is relying on the signature of the firmware using hash computation and encryption key, but also the isolation of the execution memories.
- The firmware is signed using a OP-TEE signature script
- The Firmware is read from the file system by the remoteproc framework and the image is sent to the Arm® Trustzone.
- The firmware is authenticated and managed by OP-TEE in the Arm® Trustzone.
3.1. Firmware signature[edit source]
A basic strategy to sign a firmware consists in adding a header that will contain the signature required for authentication. The signature is generated by computing a hash of the binary with the header and encrypting this hash using a private key.
On the STM32MP1, the signature procedure is similar but optimized to minimize the memory need and accelerate the authentication process. The principle is to compute the hash of the segments to load instead of computing the hash of the full image.
The signature consists in:
- Parse the program table in the ELF file to identify the segments to load.
- Compute the hash of each segment and save the result in the hash table chunk of the header.
- Compute the hash of the header and sign the hash with the private key.
3.2. Authentication[edit source]
The Authentication is executed in the secure context by an OP-TEE trusted application.
- The header is copied in a secure memory to prevent hacking.
- The header signature is verified using the public key embedded in OP-TEE.
- The ELF segments are loaded in the MCU RAMs (assigned to secure context). Upon segment load, a hash is computed and compared to the hash stored in the header hash table during the firmware signature.
- The MCU RAMs memory protection is updated to restrict access to only Cortex-M.
3.3. Memory Management[edit source]
The MCU SRAM and the RETRAM memories can be dedicated to Cortex-M usage (isolated), shared with the the Cortex-A non secure context or protected for Cortex-A secure access only.
Using the [| ETZPC] mechanism:
- During the load and authentication phase: the memories can be locked for Cortex-A secure access only.
- During the execution phase: the memories can be isolated for MCU usage (e.g execution code and data) or shared with the Cortex-A non secure context.
Developer has to pay attention for Cortex-M firmware build to pack in a same memory bank data shared with the Cortex-A. This allows to isolate the rest of the memories banks for the Cortex-M context.
As the default implementation in OP-TEE respected mapping described in memory mapping section. Descriptions provided in this article respect this mapping too.
The following mapping is considered:
- The RETRAM, MCU SRAM1 and MCU SRAM2 are used for execution code and data. They are accessible by the Cortex-A secure only when no firmware is running and assigned to Cortex-M context during authenticated firmware execution.
- The MCU SRAM3 (IPC Buffers) is shared between the Cortex-A non secure context and the Cortex-M. No protection is applied on this memory bank.
- The MCU SRAM4 is reserved for the Cortex-A non secure context so free for other usage.
Of course this mapping can be customized depending on project need. In this case the code as to be updated in a synchronized manner in STM32CubeMP1 firmware, Linux Kernel device-tree and the OP-TEE rproc PTA code.
4. Firmware signature[edit source]
![Signature procedure](/stm32mpu-ecosystem-v3/nsfr_img_auth.php/thumb/4/4d/Rproc_fw_signature.png/900px-Rproc_fw_signature.png)
4.1. STM32CubeMP1 firmware generation[edit source]
Most of the STM32CubeMP1 firmware can be isolated. Developer has to pay attention to the memories shared with the Cortex-A7 non secure context:
- the resource table,
- the log buffer shared through the resource table,
- the shared buffers for RPMsg communication,
- other shared buffers defined by the develloper for its project.
4.1.1. The resource table[edit source]
In the STM32CubeMP1 examples, the resource table is linked by default with other firmware data. A specific memory region has to be defined in the linker script to isolate it in a new region. In following example we create a new memory region for the resource table at the end of the MCU_SRAM3.
/* Memories definition */ MEMORY { m_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000298 m_text (RX) : ORIGIN = 0x10000000, LENGTH = 0x00020000 m_data (RW) : ORIGIN = 0x10020000, LENGTH = 0x00020000 m_ipc_shm (RW) : ORIGIN = 0x10040000, LENGTH = 0x00008000 m_rsc_table (RW) : ORIGIN = 0x10048000, LENGTH = 0x00008000 }
Then we map the resource table and log buffer in this region
.resource_table :
{
. = ALIGN(4);
KEEP (*(.resource_table*))
. = ALIGN(4);
} > m_rsc_table
4.1.2. log buffer[edit source]
The log buffer can be enable in the resource table to get Cortex-M log from the Cortex-A non secure context. To define it:
- an attribute has to be added to the system_log_buf buffer:
#if defined(__GNUC__) #define __section_t(S) __attribute__((__section__(#S))) #define __resource __section_t(.sys_logs) #endif char __resource __attribute__((used)); system_log_buf[SYSTEM_TRACE_BUF_SZ];
- Update the linker script to add the trace buffer in the m_rsc_table memory region.
.sys_logs : { . = ALIGN(4); KEEP (*(.sys_logs*)) . = ALIGN(4); } > m_rsc_table
[edit source]
The m_ipc_shm memory region is already reserved for the RPMsg Virtio rings and buffers.
4.2. Generate private and public key[edit source]
To sign and authenticate the Cortex-M firmware a RSA key has to be generated. For instance the ssh-keygen command can be used.
ssh-keygen
Two files are created:
- id_rsa: the private key to sign the firmware.
- id_rsa.pub: the public key to authenticate the firmware.
4.3. Sign STM32CubeMP1 firmware[edit source]
The OP-TEE ST-Microelectronics distribution integrates the sign_rproc_fw.py that allows to sign the firmware binary ELF file.
This script:
- adds an header on top of the ELF binary,
- computes the hash of the program segments that will be loaded in the Cortex-M memories and fill the hash table included in the header,
- computes the hash of the header and sign it using the private key.
To sign the firmware :
- Use the default RSA key available in the OP-TEE repository (that can be updated) : keys/default_rproc.pem
./scripts/sign_rproc_fw.py sign --in <firmware name>
- Or specify the key to use as parameter:
./scripts/sign_rproc_fw.py sign --in <firmware name> --key <id_rsa private key>
- For more script options:
./scripts/sign_rproc_fw.py sign --help
The signed firmware is available in the ELF file directory with a .sign extension.
4.4. OP-TEE remoteproc TA generation[edit source]
As prerequisite the OP-TEE OS build environment has to be installed.
The OPTEE OS firmware has to be re-generated to include (see here for how to):
- the remote processor Trusted Application (TA),
- the remote processor platform Pseudo Trusted Application (PTA),
- the public key.
4.4.1. Configuration[edit source]
Enable the CFG_RPROC_PTA in core/arch/arm/plat-stm32mp1/conf.mk
CFG_RPROC_PTA ?= y
4.4.2. Build the OP-TEE OS[edit source]
Additional directives to build OP-TEE OS:
- CFG_IN_TREE_EARLY_TAS=remoteproc/80a4c275-0a47-4905-8285-1486a9771a08 : add remoteproc early TA.
- CFG_RPROC_SIGN_KEY= <key file path name>: specify the public key file to authenticate the remote processor firmware. This directive is optional. If not defined the keys/default_rproc.pem default key will be used.
Example of build command for the STM32MP157x-DK2 board:
- Using the default key available in the OP-TEE repository to build the OP-TEE OS (that can be updated): keys/default_rproc.pem
make PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-dk2.dts CFG_SECURE_DT=stm32mp157c-dk2 O=out all comp-cflagscore=--sysroot=$SDKTARGETSYSROOT CFG_IN_TREE_EARLY_TAS=remoteproc/80a4c275-0a47-4905-8285-1486a9771a08
- Or specify the key to use by defining CFG_RPROC_SIGN_KEY:
make PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-dk2.dts CFG_SECURE_DT=stm32mp157c-dk2 O=out all comp-cflagscore=--sysroot=$SDKTARGETSYSROOT CFG_IN_TREE_EARLY_TAS=remoteproc/80a4c275-0a47-4905-8285-1486a9771a08 CFG_RPROC_SIGN_KEY=my_public_key.pem
4.4.3. Update the software on board[edit source]
Refer to update OP-TEE software on board for details.
4.5. Linux Kernel update[edit source]
Activate the remoteproc tee in the kernel configuration using the Linux Menuconfig tool: Menuconfig or how to configure kernel.
Device drivers --->
Remoteproc drivers --->
<*> Support for Remote Processor subsystem
<*> STM32 remoteproc support
<*> Trusted firmware support by a Trusted Application
4.5.1. Device tree configuration[edit source]
To be compliant with the memory mapping and protection the memory regions properties described in the Linux remoteproc framework overview
has to be updated.
Example:
- Declare a memory region for the resource table and remoteproc trace memory region. the Address and size have to be aligned with stm32Cube linker script m_rsc_table definition .
reserved-memory { ... mcu_rsc_table: mcu_rsc_table@10048000 { compatible = "shared-dma-pool"; reg = <0x10048000 0x8000>; no-map; }; };
- In the remoteproc DT node:
- Cleanup retram, mcusram and mcusram2 regions which are now handles by OP-TEE,
- add the mcu_rsc_table region,
- update compatibility field.
/* stm32 M4 remoteproc device */ m4_rproc: m4@0 { ... compatible = "st,stm32mp1-m4_optee"; memory-region = <&vdev0vring0>, <&vdev0vring1>, <&vdev0buffer>, <&mcu_rsc_table>; ... };
4.6. U-Boot update[edit source]
This chapter is relevant only for coprocessor firmware pre-loaded by the bootloaderbr>
To enable the management of a pre-loaded firmware of an authenticated firmware, the compatible field of the remote proc node has to be updated in the device tree configuration. In arch/arm/dts/stm32mp151.dtsi :
m4_rproc: m4@10000000 {
compatible = "st,stm32mp1-m4_optee";
};
5. Authenticated firmware live-cycle[edit source]
The management of the firmware live-cycle does not change from user point of view and is described in the remoteproc article.
The main difference with a non-authenticated firmware is that the remote processor framework delegates the coprocessor management to the OP-TEE. In the same way the load of an authenticated firmware by U-boot is also supported.
5.1. Firmware load and authentication[edit source]
1. The firmware in copied by the Linux Kernel (or U-boot) in a non secure DDR memory.
2. The firmware header is copied in a secure memory to ensure its integrity during the authentication steps.
3. The Header hash is computed.
4. The signature is decrypted using the public key. The result is the hash of the header computed by the signing tools. Both hashes are compared to authenticate the firmware header.
5. At this step the firmware header is valid. The next step can start: load the the firmware in MCU memories:
For each segment to load:
6. The segment is copied in the MCU RAMs which are accessible only by the secure context to ensure their integrity.
7. The hash of the copied segment is computes and compare to the hash stored in the firmware header.
8. At this step all segments have been copied and authenticated, the firmware is loaded.
5.2. Firmware start and stop[edit source]
The firmware is started and stopped by OP-TEE. The reset and RAMs access rights are only accessible by the secure context.
- Before starting the firmware the MCU rams access rights are updated to isolate execution RAMs for the Cortex-M.
- After stopping the firmware the MCU SRAM are cleaned and access rights are updated to Cortex-A secure context.
5.3. RPMsg management[edit source]
As the resource tables and RPMsg vrings and buffers are in a shared memory it is possible to communicate with the authenticated firmware.
5.4. Firmware core dump[edit source]
As the memories are protected it is not possible to dump the firmware on crash.