Secure Boot iMX8M
iMX8M-series devices
Overview
The i.MX8QM application processor provides the Secure Boot capability with the High Assurance Boot (HAB). The ROM is responsible for loading the initial program image from the boot medium. HAB enables the ROM to authenticate the program image by using digital signatures component on the on-chip ROM. HAB provides a mechanism to establish a root of trust for the remaining software components and establishes a secure state on the i.MX IC’s secure state machine inhardware.
HAB authentication is based on public key cryptography using the RSA algorithm in which image data is signed offline using a series of private keys. The resulting signed image data is then verified on the i.MX processor using the corresponding public keys. HAB can leverage CAAM module for hw crypto acceleration for SHA-256 operations.
The public keys are included in the CSF binary and the SRK Hash is programmed in the SoC fuses for establishing the root of trust.
Additional Details about HAB architecture can be found in the application note AN4581 and in the introduction_habv4.txt, hab-for-dummies documents.
Code signing tool (CST)
NXP provides the reference Code Signing Tool (CST) for key generation and code signing for use with the HAB library. The CST can be downloaded by following this link
Understanding the i.MX8M and i.MX8MM flash.bin image layout
Due to the new the architecture, multiple firmwares and softwares are required to boot i.MX8M and i.MX8MM devices. In order to store all the images in a single binary the FIT (Flattened Image Tree) image structure is used.
The final image is generated by the imx-mkimage project
, the tool combines all
the input images in a FIT structure, generating a flash.bin
image with an
appropriate IVT set.
For a secure boot process users should ensure all images included in flash.bin
file are covered by a digital signature.
The diagram below illustrate a signed flash.bin image layout:
+-----------------------------+
| |
| *Signed HDMI/DP FW |
| |
+-----------------------------+
| Padding |
------- +-----------------------------+ --------
^ | IVT - SPL | ^
Signed | +-----------------------------+ |
Data | | u-boot-spl.bin | |
| | + | | SPL
v | DDR FW | | Image
------- +-----------------------------+ |
| CSF - SPL + DDR FW | v
+-----------------------------+ --------
| Padding |
------- +-----------------------------+ --------
Signed ^ | FDT - FIT | ^
Data | +-----------------------------+ |
v | IVT - FIT | |
------- +-----------------------------+ |
| CSF - FIT | |
------- +-----------------------------+ |
^ | u-boot-nodtb.bin | | FIT
| | + | | Image
| | u-boot.bin | |
Signed | +-----------------------------+ |
Data | | OP-TEE (Optional) | |
| +-----------------------------+ |
| | bl31.bin (ATF) | |
| +-----------------------------+ |
v | u-boot.dtb | v
------- +-----------------------------+ --------
- Only supported on i.MX8M series
The boot flow on i.MX8M and i.MX8MM devices are slightly different when compared with i.MX6 and i.MX7 series, the diagram below illustrate the boot sequence overview:
i.MX8M and i.MX8MM devices boot flow:
Secure World Non-Secure World
|
|
+------------+ +------------+ |
| SPL | | i.MX 8M/MM | |
| + | ---> | ROM | |
| DDR FW | | + HAB | |
+------------+ +------------+ |
| |
v |
+------------+ |
| *Signed | |
| HDMI/DP FW | |
+------------+ |
| |
v |
+------------+ +------------+ |
| FIT Image: | | SPL | |
| ATF + TEE | ---> | + | |
| + U-Boot | | DDR FW | | +-----------+
+------------+ +------------+ | | Linux |
| | +-----------+
v | ^
+------------+ | | +-------+
| ARM | | +-----------+ | Linux |
| Trusted | ----+---> | U-Boot | <--- | + |
| Firmware | | +-----------+ | DTB |
+------------+ | +-------+
| |
v |
+----------+ |
| **OP-TEE | |
+----------+ |
- Only supported on i.MX8M series ** Optional
On i.MX8M devices the HDMI firmware or DisplayPort firmware are the first image to boot on the device. These firmwares are signed and distributed by NXP, and are always authenticated regardless of security configuration. In case not required by the application the HDMI or DisplayPort controllers can be disabled by eFuses and the firmwares are not required anymore.
The next images are not signed by NXP and users should follow the signing procedure as described in this document.
The Second Program Loader (SPL) and DDR firmware are loaded and authenticated by the ROM code, these images are executed in the internal RAM and responsible for initializing essential features such as DDR, UART, PMIC and clock enablement.
Once the DDR is available, the SPL code loads all the images included in the FIT structure to their specific execution addresses, the HAB APIs are called to extend the root of trust, authenticating the U-Boot, ARM trusted firmware (ATF) and OP-TEE (in case if included).
The root of trust can be extended again at U-Boot level to authenticate Kernel and M4 images.
Enabling the secure boot support in U-Boot
The first step is to generate an U-Boot image supporting the HAB features, similar to i.MX6 and i.MX7 series the U-Boot provides extra functions for HAB, such as the HAB status logs retrievement through the hab_status command and support to extend the root of trust.
The support is enabled by adding the CONFIG_SECURE_BOOT to the build configuration:
-
Defconfig:
CONFIG_SECURE_BOOT=y
-
Kconfig:
ARM architecture -{'>'} Support i.MX HAB features
Preparing the fit image
The imx-mkimage
project is used to combines all the images in a single
flash.bin binary, the following files are required:
- U-Boot:
u-boot.bin
u-boot-nodtb.bin
u-boot-spl.bin
U-Boot DTB file (e.g. fsl-imx8mq-evk.dtb)
- ATF image:
bl31.bin
- DDR firmware:
lpddr4_pmu_train_1d_dmem.bin
lpddr4_pmu_train_1d_imem.bin
lpddr4_pmu_train_2d_dmem.bin
lpddr4_pmu_train_2d_imem.bin
- HDMI firmware (Only in i.MX8M):
signed_hdmi_imx8m.bin
- DisplayPort firmware (Only in i.MX8M):
signed_dp_imx8m.bin
- OP-TEE (Optional):
tee.bin
The procedure to build ATF and download the firmwares are out of the scope of this document, please refer to the Linux BSP Release Notes.
Copy all files to iMX8M directory and run the following command according to the target device, on this example we are building a HDMI target and also including the OP-TEE binary:
- Assembly flash.bin binary:
$ make SOC=<SoC Name> flash_hdmi_spl_uboot
The mkimage log can be used to calculate the authenticate image command parameters and CSF offsets:
imx-mkimage
build log:
Loader IMAGE:
header_image_off 0x1a000
dcd_off 0x0
image_off 0x1a040
csf_off 0x44600
spl hab block: 0x7e0fd0 0x1a000 0x2e600
Second Loader IMAGE:
sld_header_off 0x57c00
sld_csf_off 0x58c20
sld hab block: 0x401fcdc0 0x57c00 0x1020
Additional HAB information is provided by running the following command:
- Printing HAB FIT information:
$ make SOC=<SoC Name> print_fit_hab
TEE_LOAD_ADDR=0xfe000000 ATF_LOAD_ADDR=0x00910000 ./print_fit_hab.sh \
0x60000 fsl-imx8mq-evk.dtb
0x40200000 0x5AC00 0x9AAC8
0x910000 0xF56C8 0x9139
0xFE000000 0xFE804 0x4D268
0x4029AAC8 0x14BA6C 0x6DCF
Creating the CSF description file
The CSF contains all the commands that the ROM executes during the secure boot. These commands instruct the HAB code on which memory areas of the image to authenticate, which keys to install, use and etc.
CSF examples are available under doc/imx/habv4/csf_examples/ directory in U-Boot sources.
As explained in sections above the SPL is first authenticated by the ROM code and the root of trust is extended to the FIT image, hence two CSF files are necessary to completely sign an flash.bin image.
The build log provided by imx-mkimage can be used to define the "Authenticate Data" parameter in CSF.
- SPL "Authenticate Data" addresses in flash.bin build log:
spl hab block: 0x7e0fd0 0x1a000 0x2e600
- "Authenticate Data" command in csf_spl.txt file:
Blocks = 0x7e0fd0 0x1a000 0x2e600 "flash.bin"
- FIT image "Authenticate Data" addresses in flash.bin build log:
sld hab block: 0x401fcdc0 0x57c00 0x1020
- FIT image "Authenticate Data" addresses in print_fit_hab build log:
0x40200000 0x5AC00 0x9AAC8
0x910000 0xF56C8 0x9139
0xFE000000 0xFE804 0x4D268
0x4029AAC8 0x14BA6C 0x6DCF
- "Authenticate Data" command in csf_fit.txt file:
Blocks = 0x401fcdc0 0x057c00 0x01020 "flash.bin", \
0x40200000 0x05AC00 0x9AAC8 "flash.bin", \
0x00910000 0x0F56C8 0x09139 "flash.bin", \
0xFE000000 0x0FE804 0x4D268 "flash.bin", \
0x4029AAC8 0x14BA6C 0x06DCF "flash.bin"
Avoiding Kernel crash in closed devices
For devices prior to HAB v4.4.0, the HAB code locks the Job Ring and DECO master ID registers in closed configuration. In case the user specific application requires any changes in CAAM MID registers it's necessary to add the "Unlock CAAM MID" command in CSF file.
The current NXP BSP implementation expects the CAAM registers to be unlocked when configuring CAAM to operate in non-secure TrustZone world.
The Unlock command is already included by default in the signed HDMI and DisplayPort firmwares, on i.MX8MM devices or in case the HDMI or DisplayPort controllers are disabled, users must ensure this command is included in SPL CSF.
- Add Unlock MID command in csf_spl.txt:
[Unlock]
Engine = CAAM
Features = MID
Signing the flash.bin binary
The CST tool is used for singing the flash.bin image and generating the CSF binary. Users should input the CSF description file created in the step above and receive a CSF binary, which contains the CSF commands, SRK table, signatures and certificates.
- Create SPL CSF binary file:
$ ./cst -i csf_spl.txt -o csf_spl.bin
- Create FIT CSF binary file:
$ ./cst -i csf_fit.txt -o csf_fit.bin
Assembling the CSF in flash.bin binary
The CSF binaries generated in the step above have to be inserted into the
flash.bin
image.
The CSF offsets can be obtained from the flash.bin build log:
- SPL CSF offset:
csf_off 0x44600
- FIT CSF offset:
sld_csf_off 0x58c20
The signed flash.bin image can be then assembled:
- Create a flash.bin copy:
$ cp flash.bin signed_flash.bin
- Insert csf_spl.bin in signed_flash.bin at 0x44600 offset:
$ dd if=csf_spl.bin of=signed_flash.bin seek=$((0x44600)) bs=1 conv=notrunc
- Insert csf_fit.bin in signed_flash.bin at 0x58c20 offset:
$ dd if=csf_fit.bin of=signed_flash.bin seek=$((0x58c20)) bs=1 conv=notrunc
- Flash signed flash.bin image:
$ sudo dd if=signed_flash.bin of=/dev/sd<x> bs=1K seek=33 && sync
Programming SRK Hash
As explained in AN4581 and in introduction_habv4.txt document the SRK Hash
fuse values are generated by the srktool and should be programmed in the
SoC SRK_HASH[255:0]
fuses.
Be careful when programming these values, as this data is the basis for the root of trust. An error in SRK Hash results in a part that does not boot.
The U-Boot fuse tool can be used for programming eFuses on i.MX SoCs.
- Dump SRK Hash fuses values in host machine:
$ hexdump -e '/4 "0x"' -e '/4 "%X""\n"' SRK_1_2_3_4_fuse.bin
0x20593752
0x6ACE6962
0x26E0D06C
0xFC600661
0x1240E88F
0x1209F144
0x831C8117
0x1190FD4D
- Program SRK_HASH[255:0] fuses on i.MX8MQ and i.MX8MM devices:
=> fuse prog 6 0 0x20593752
=> fuse prog 6 1 0x6ACE6962
=> fuse prog 6 2 0x26E0D06C
=> fuse prog 6 3 0xFC600661
=> fuse prog 7 0 0x1240E88F
=> fuse prog 7 1 0x1209F144
=> fuse prog 7 2 0x831C8117
=> fuse prog 7 3 0x1190FD4D
Verifying HAB events
The next step is to verify that the signatures included in flash.bin image is successfully processed without errors. HAB generates events when processing the commands if it encounters issues.
The hab_status U-Boot command call the hab_report_event()
and hab_status()
HAB API functions to verify the processor security configuration and status.
This command displays any events that were generated during the process.
Prior to closing the device users should ensure no HAB events were found, as the example below:
- Verify HAB events:
=> hab_status
Secure boot disabled
HAB Configuration: 0xf0, HAB State: 0x66
Closing the device
After the device successfully boots a signed image without generating any HAB
events, it is safe to close the device. This is the last step in the HAB
process, and is achieved by programming the SEC_CONFIG[1]
fuse bit.
Once the fuse is programmed, the chip does not load an image that has not been signed using the correct PKI tree.
- Program SEC_CONFIG[1] fuse on i.MX8MQ and i.MX8MM devices:
=> fuse prog 1 3 0x2000000
Completely secure the device
Additional fuses can be programmed for completely secure the device, more details about these fuses and their possible impact can be found at AN4581.
- Program SRK_LOCK:
=> fuse prog 0 0 0x200
- Program DIR_BT_DIS:
=> fuse prog 1 3 0x8000000
- Program SJC_DISABLE:
=> fuse prog 1 3 0x200000
- JTAG_SMODE:
=> fuse prog 1 3 0xC00000
Authenticating additional boot images
The High Assurance Boot (HAB) code located in the on-chip ROM provides an Application Programming Interface (API) making it possible to call back into the HAB code for authenticating additional boot images.
The U-Boot is running in non-secure TrustZone world and to make use of this
feature it's necessary to use a SIP call to the ATF, this is already
implemented in hab.c
code and it's transparent to the user.
The process of signing an additional image is similar as in i.MX6 and i.MX7 series devices, the steps below are using the Linux Kernel image as example.
The diagram below illustrate the Image layout:
------- +-----------------------------+ <-- *load_address
^ | |
| | |
| | |
| | |
| | Image |
Signed | | |
Data | | |
| | |
| +-----------------------------+
| | Padding to Image size |
| | in header |
| +-----------------------------+ <-- *ivt
v | Image Vector Table |
------- +-----------------------------+ <-- *csf
| |
| Command Sequence File (CSF) |
| |
+-----------------------------+
| Padding (optional) |
+-----------------------------+
Padding the image
The Image must be padded to the size specified in the Image header, this can be achieved by using the od command.
- Read Image size:
$ od -x -j 0x10 -N 0x4 --endian=little Image
0000020 5000 0145
0000024
The tool objcopy can be used for padding the image.
- Pad the Image:
$ objcopy -I binary -O binary --pad-to 0x1455000 --gap-fill=0x00 Image Image_pad.bin
Generating Image Vector Table
The HAB code requires an Image Vector Table (IVT) for determining the image length and the CSF location. Since Image does not include an IVT this has to be manually created and appended to the end of the padded Image, the script genIVT.pl in script_examples directory can be used as reference.
- Generate IVT:
$ genIVT.pl
Note: The load Address may change depending on the device.
- Append the ivt.bin at the end of the padded Image:
$ cat Image_pad.bin ivt.bin > Image_pad_ivt.bin
Signing the image
A CSF file has to be created to sign the image. HAB does not allow to change the SRK once the first image is authenticated, so the same SRK key used in the initial image must be used when extending the root of trust.
CSF examples are available in ../csf_examples/additional_images/ directory.
- Create CSF binary file:
$ ./cst --i csf_additional_images.txt --o csf_Image.bin
- Attach the CSF binary to the end of the image:
$ cat Image_pad_ivt.bin csf_Image.bin > Image_signed.bin
Verifying HAB events
The U-Boot includes the hab_auth_img command which can be used for authenticating and troubleshooting the signed image, the Image must be loaded at the load address specified in the IVT.
- Authenticate additional image:
=> hab_auth_img <Load Address> <Image Size> <IVT Offset>
If no HAB events were found the Image is successfully signed.