Boot Software Updates on iMX¶
Boot artifacts¶
SPL¶
SPL is the first software loader generated by the i.MX U-Boot build. It is signed via the NXP CST tool which fills in various IVT header fields. SPL binaries from CI cannot be added directly to OTA due to the missing signature data which must be added by the subscriber.
In some cases, an SoC may require additional firmware to be loaded (such as DDR firmware for i.MX 8M). This firmware is loaded prior to the load / verification of U-Boot FIT-image.
U-Boot FIT image¶
U-boot FIT-image is a generic name for the signed FIT-image that contains U-Boot proper (u-boot.bin) and a host of other firmware. This file is verified by SPL via a public key stored in SPL’s dtb. This artifact may be signed (on closed boards) as a part of CI and can be included automatically in a boot software OTA package.
- U-boot-nodtb.bin
- U-boot.dtb
- OP-TEE
- Arm Trusted Firmware (ARMv8)
- Possibly other firmwares
If the CI signing key has been rotated since the last OTA, then the SPL.dtb verification data needs to be updated prior to booting the new U-Boot FIT-image.
MMC boot image layout¶
The Secondary Image Table (SIT) is a 20 byte long structure containing 5 32-bit words. These encode the bootloader B-copy area offset (called firstSectorNumber), magic value (called tag) that is always 0x00112233, and three unused words set to 0. Example SIT:
$ hexdump -vC sit-mx8mm.bin
00000000 00 00 00 00
00000004 00 00 00 00
00000008 33 22 11 00 <--- This is the "tag"
0000000c 00 10 00 00 <--- This is the "firstSectorNumber"
00000010 00 00 00 00
Boot Flow¶
SPL¶
- Initialize DDR
- Load U-Boot FIT-image
- Perform verification
- Extract components
- Jump to ATF / OP-TEE
ATF (ARMv8)¶
- Perform memory permission setup
- Drop to EL-2 non-secure
- Jump to OP-TEE
OP-TEE¶
- Perform secure world setup
- Driver init
- Load TAs
- Drop to EL-2 secure world
- Jump to u-boot.bin
U-Boot¶
- Driver init
- Boot script
- Load kernel FIT-image
- Perform verification
- Extract components
- Jump to Linux kernel
Update procedure¶
Primary vs Secondary boot paths¶
There are some i.MX SoC which could be configured to have two copies of the bootloader in SD/eMMC and switch between them. The switch can be triggered either by the BootROM in case the bootloader image is faulty OR can be enforced by the user.
The bootloader A-copy must be placed at a predetermined offset in SD/eMMC. The bootloader B-copy area offset is determined by an offset stored in The Secondary Image Table (SIT). The SIT must be placed at predetermined offset in SD/eMMC.
To enforce BootROM to boot the secondary boot image, PERSIST_SECONDARY_BOOT must be set in SRC_GPR10 register and a warm reset has to be performed. After reboot, BootROM will boot the image using the offset specified in the SIT table. For additional details about the SIT format and SIT offsets, please refer to your the SoC Reference Manual, section Redundant boot support for expansion device.
libaktualizr and aktualizr-lite¶
- aktualizr-lite decides if boot firmware needs to be updated based on the contents of ${ostree_root}/usr/lib/firmware/version.txt, where ostree_root is root of newly deployed ostree sysroot. Example of contents: bootfirmware_version=10
- After parsing bootfirmware_version, it compares the new version number with the existing one, which is obtained via fiovb or ubootenv.
- If bootfirmware_version from version.txt is higher than the existing one, aktualizr-lite sets bootupgrade_available via fiovb or ubootenv.
- Reboot should be performed.
U-Boot boot.cmd script¶
- Actual update is done via U-Boot boot.cmd (boot.scr) script.
- boot.cmd script checks if booting secondary path is booted
- In case upgrade_available is set, check if boot firmware upgrade is needed is by checking the bootupgrade_available flag. If both are true, obtain boot firmware images from the newly deployed ostree sysroot and write them to secondary boot path offsets. After the secondary boot bit is set, warm reset is performed to enforce BootROM to boot secondary boot path.
- After rebooting secondary boot path is executed, perform condition verification from step 2. It should evaluate as false so just regular boot of Linux having taken place.
- After Linux is booted, aktualizr-lite confirms a successful update by clearing the upgrade_available flag. At this point, new boot firmware images are validated and need to be flashed to the stable primary path. Additional reboot is needed after this step.
- Regular POR cold reset is performed.
Add a new board¶
TF-A/OP-TEE¶
TF-A on ARMv8 or OP-TEE on ARMv7, provides PSCI services to the Linux OS and should support the `SYSTEM_RESET2`, which implements a internal warm reset, resetting only the CPU. This is needed for retaining the values of special registers after reboot. This behavior differs from a regular SYSTEM_RESET, which causes POR, removing power for the whole board (resets CPU, DDR and peripherals, on some boards it also resets external PMIC).
U-Boot¶
SPL: FIT Image Offset Calculation¶
U-Boot SPL automatically detects which image to boot next based on the SECONDARY_BOOT value. Every board has the configuration CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR, which contains the offset of the U-Boot FIT image beginning from the boot media sectors (512 bytes each). Below is an example of how the final offset is calculated on iMX SoCs (extract from ./arch/arm/mach-imx/spl.c):
#if defined(CONFIG_SECONDARY_BOOT_RUNTIME_DETECTION) && \
defined(CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR)
unsigned long spl_mmc_get_uboot_raw_sector(struct mmc *mmc,
unsigned long raw_sect)
{
int boot_secondary = boot_mode_getprisec();
unsigned long offset = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR;
if (boot_secondary) {
offset += CONFIG_SECONDARY_BOOT_SECTOR_OFFSET;
printf("SPL: Booting secondary boot path: using 0x%lx offset "
"for next boot image\n", offset);
} else {
printf("SPL: Booting primary boot path: using 0x%lx offset "
"for next boot image\n", offset);
}
return offset;
}
#endif
Fastboot: Support of Secondary Boot Image Offsets¶
The required offsets for the secondary boot images (SPL, U-Boot.itb, and SIT) for iMX6, iMX6ULL, iMX7 and iMX8M SoCs, are defined by the FSL fastboot driver. To change SIT offset used for an SoC, adjust the secondary_image_table_mmc_offset() and bootloader_mmc_offset() functions within the U-Boot fastboot driver source (drivers/fastboot/fb_fsl/fb_fsl_partitions.c).
Secondary Image Table Generation¶
SIT image binary is generated automatically if U-Boot is being built with the correct value of CONFIG_SECONDARY_BOOT_SECTOR_OFFSET config option.
Watchdog¶
As secondary boot path is mainly used for boot firmware update image validation, in exceptional cases it will behave incorrectly, such as the system not responding. To cover such cases, watchdog support has to be enabled in SPL by adding the following config options to lmp.cfg:
CONFIG_IMX_WATCHDOG=y
CONFIG_SPL_HW_WATCHDOG=y
# CONFIG_SPL_WDT is not set
CONFIG_SPL_WATCHDOG_SUPPORT=y
meta-lmp¶
mfgtool scripts¶
To deploy boot images to the destination board, the mfgtools package is used. It uses a special configuration file with uuu extensions, which contains all needed instructions for the deployment of boot images. Default uuu files do not support flashing images for secondary boot path. Doing so requiring the following adjustments: adding SIT image, secondary SPL and U-Boot FIT deployment steps:
+FB: flash bootloader_s ../imx-boot-@@MACHINE@@
+FB: flash bootloader2_s ../u-boot-@@MACHINE@@.itb
+FB: flash sit ../sit-@@MACHINE@@.bin
The final uuu script looks like:
uuu_version 1.2.39
SDP: boot -f imx-boot-mfgtool
SDPS: boot -f imx-boot-mfgtool
SDPV: delay 1000
SDPV: write -f u-boot-mfgtool.itb
SDPV: jump
FB: ucmd setenv fastboot_dev mmc
FB: ucmd setenv mmcdev ${emmc_dev}
FB: ucmd mmc dev ${mmcdev} 1; mmc erase 0 0x2000
FB: flash bootloader ../imx-boot-@@MACHINE@@
FB: flash bootloader2 ../u-boot-@@MACHINE@@.itb
FB: flash bootloader_s ../imx-boot-@@MACHINE@@
FB: flash bootloader2_s ../u-boot-@@MACHINE@@.itb
FB: flash sit ../sit-@@MACHINE@@.bin
FB: ucmd if env exists emmc_ack; then ; else setenv emmc_ack 0; fi;
FB: ucmd mmc partconf ${mmcdev} ${emmc_ack} 1 0
FB: done
lmp.cfg files¶
To enable support for flashing/booting secondary boot images, adjust the default lmp.cfg and the one for mfgtools for the device, enabling support for the secondary boot path. The following config options need to be added to the default lmp.cfg:
CONFIG_SECONDARY_BOOT_RUNTIME_DETECTION=y
CONFIG_SECONDARY_BOOT_SECTOR_OFFSET=0x1000
CONFIG_CMD_SECONDARY_BOOT=y
And to mfgtool lmp.cfg:
CONFIG_FSL_FASTBOOT_BOOTLOADER_SECONDARY=y
CONFIG_SECONDARY_BOOT_SECTOR_OFFSET=0x1000
Pre-load boot.cmd by SPL¶
As boot.cmd script depends on U-Boot cmds for booting Linux, it should be aligned with U-Boot version. By default in regular setups without boot firmware update support boot.cmd is stored in first FAT partition in eMMC/SD. So to get boot.cmd updates together with other boot software images, it should be moved from FAT partition to U-Boot FIT image. To do that edit lmp-machine-custom.inc adding this line for your board (imx8mqevk as an example):
BOOTSCR_LOAD_ADDR_imx8mqevk = "0x44800000"
This change will include Linux boot.cmd into U-Boot FIT image alongside with TF-A/OP-TEE/U-Boot proper/U-Boot dtb images. When SPL parses U-Boot FIT image (u-boot.itb) will pre-load boot.itb (compiled and wrapped boot.cmd) to the address specified in BOOTSCR_LOAD_ADDR variable.
To let U-Boot know where to take boot script from, you should also adjust CONFIG_BOOTCOMMAND param in your U-Boot lmp.cfg of your board.
CONFIG_BOOTCOMMAND="setenv verify 1; source 0x44800000; reset"
Test basic API¶
After applying all updates from previous steps, we should validate that everything is in place. Basically this consists of two basic steps:
- Cold/Warm resets from U-Boot are functional
- Obtain board security state (open/closed states)
So to test cold/warm resets and booting primary/secondary boot path use these two U-Boot commands imx_secondary_boot and reset (for regular reset, usually it’s does POR)/reset -w (warm reset).
Example of test:
U-Boot SPL 2020.04+fio+gee4483499f (Jan 01 1970 - 00:00:00 +0000)
Trying to boot from MMC1
SPL: Booting primary boot path: using 0x300 offset for next boot image
...
Hit any key to stop autoboot: 0
u-boot => imx_secondary_boot 1
u-boot => reset -w
Resetting...
U-Boot SPL 2020.04+fio+gee4483499f (Jan 01 1970 - 00:00:00 +0000)
Trying to boot from MMC1
SPL: Booting secondary boot path: using 0x1300 offset for next boot image
...
Hit any key to stop autoboot: 0
From output you can see that after setting secondary boot and performing warm reset, BootROM boots images from secondary boot path (SPL: Booting secondary boot path: using 0x1300 offset for next boot image).
To check if the security status of your board is detected correctly, use imx_is_closed command:
u-boot=> imx_is_closed
Board is in open state
boot.cmd¶
Currently LmP uses template-based way of generation of final boot.cmd.
It’s constructed from common boot files
(./meta-lmp-base/recipes-bsp/u-boot/u-boot-ostree-scr-fit
),
which contains all SoC agnostic DEFINEs and common functionality, and board
specific boot.cmd, which includes the common scripts.
Example of board boot.cmd
(./meta-lmp-bsp/recipes-bsp/u-boot/u-boot-ostree-scr-fit/imx8mm-lpddr4-evk/boot.cmd
):
echo "Using freescale_${fdt_file}"
# Default boot type and device
setenv bootlimit 3
setenv devtype mmc
setenv devnum 2
setenv bootpart 1
setenv rootpart 2
# Boot image files
setenv fdt_file_final freescale_${fdt_file}
setenv fit_addr ${initrd_addr}
# Boot firmware updates
# Offsets are in blocks (512KB each)
setenv bootloader 0x42
setenv bootloader2 0x300
setenv bootloader_s 0x1042
setenv bootloader2_s 0x1300
setenv bootloader_image "imx-boot"
setenv bootloader_s_image ${bootloader_image}
setenv bootloader2_image "u-boot.itb"
setenv bootloader2_s_image ${bootloader2_image}
setenv uboot_hwpart 1
@@INCLUDE_COMMON_IMX@@
@@INCLUDE_COMMON@@
From the list above you can find that the only needed variables that should be defined are: boot/root partition indexes, mmc device index and fdt_file. For boot firmware updates functionality also bootloader image offsets and names should be provided.
sysroot and signed boot artifacts¶
All boot artifacts (SPL/imx-boot and U-Boot FIT) are automatically deployed to sysroot during build time, however on closed boards, where initial boot image has to be signed in advance by a subscriber private key, there is way to add signed binary instead of automatic inclusion of unsigned boot artifacts.
To do that, just add lmp-boot-firmware.bbappend
to your meta-subscriber-overrides
layer, adding the path to the signed binary and the signed binary itself.
Then define boot firmware version number by setting LMP_BOOT_FIRMWARE_VERSION
global variable in your lmp-factory-custom.inc
. Boot firmware version
information will be automatically added to ${osroot}/usr/lib/firmware/version.txt
file and U-Boot Device Tree Blob.
Note
The signed binary is called SPL
for i.MX 6/7 and imx-boot
for i.MX 8* platforms. No need to append .signed
to the binary name.
Example:
diff --git a/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware.bbappend b/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware.bbappend
new file mode 100644
index 0000000..6c11380
--- /dev/null
+++ b/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware.bbappend
@@ -0,0 +1,7 @@
+FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
+
+SRC_URI = " \
+ file://SPL \
+"
diff --git a/conf/machine/include/lmp-factory-custom.inc b/conf/machine/include/lmp-factory-custom.inc
index 0fe26b8..2a9815d 100644
--- a/conf/machine/include/lmp-factory-custom.inc
+++ b/conf/machine/include/lmp-factory-custom.inc
@@ -22,4 +22,4 @@ UEFI_SIGN_KEYDIR = "${TOPDIR}/conf/factory-keys/uefi"
# TF-A Trusted Boot
TF_A_SIGN_KEY_PATH = "${TOPDIR}/conf/factory-keys/tf-a/privkey_ec_prime256v1.pem"
+LMP_BOOT_FIRMWARE_VERSION:imx8mm-lpddr4-evk = "3"
diff --git a/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware/SPL b/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware/SPL
new file mode 100644
index 0000000..50f5013
Binary files /dev/null and b/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware/SPL differ
Note
As LMP_BOOT_FIRMWARE_VERSION
is now a preferable way to set boot firmware version, defining PV
in lmp-boot-firmware.bbappend
is deprecated and should not be used. To switch to a new approach just remove PV = "<version>"
line from
lmp-boot-firmware.bbappend
and define LMP_BOOT_FIRMWARE_VERSION
with appropriate version value as shown above in the example.