Boot Software Updates on STM32MP1
Boot Artifacts
STM32 TF-A BL2 Image
The TF-A BL2 image is the first boot image loaded by the application processor’s trusted ROM. It is in charge of loading the next-stage images (secure and non-secure) and preliminary initialization of required peripherals:
- SoC clocks, DRAM
- Security IPs: memory firewalls, crypto engines
- Storage (MMC, QSPI etc).
The image is prepended with a STM32 header.
It can also be signed using the STM32 Signing tool.
TF-A FIP Image
The Trusted Firmware-A Firmware Image Package (TF-A FIP) is the signed FIP-image containing U-Boot proper (u-boot.bin
) and other firmware.
This file is verified by TF-A BL2 using RoT HASH (of the public key) in the BL2 device tree blob and public key.
These are provided by the FIP image.
This artifact may be signed—on closed boards—as part of CI, and can be included automatically in a boot software OTA package.
List of images inside a FIP Image container:
U-boot-nodtb.bin
(U-Boot proper binary)U-boot.dtb
(U-Boot device tree)- OP-TEE runtime (OP-TEE core, which provides PSCI and secure services)
If the CI signing key has been rotated since the last OTA, update the BL2 verification data prior to trying to boot the new FIP-image.
MMC Boot Image Layout
The location of the boot image depends on what boot media is being used.
For eMMC, the initial boot images are flashed to a 0x0
offset of the boot hw partitions.
When using a SD card as boot media, boot images of different offsets are used within the same HW partition. For additional details see Flash Layout File (eMMC Boot) and WKS Layout (SD Boot).
Update Procedure
Primary vs Secondary Boot Paths
The STM32MP1 SoC supports booting from two copies of First Stage Boot Loaders:
- First Stage Boot Loader Copy 1 (FSBL1)
- First Stage Boot Loader Copy 2 (FSBL2)
Unfortunately, the SoC does not provide any mechanisms for a user to control in runtime what FSBL copies to boot. Only the SoC ROM can make this decision based on image authentication/checksum results. As such, the setup for FSBL1 is treated as the primary boot path, with FSBL2 as a recovery path (secondary boot path) in case the authentication/loading of FSBL1 fails.
Libaktualizr and Aktualizr-lite
Libaktualizr and aktualizr-lite make decisions during the boot phase. Those decisions are listed below, ordered as they appear in the source code:
- Aktualizr-lite makes the decision if boot firmware needs to be updated based on the contents of
${ostree_root}/usr/lib/firmware/version.txt
. Here,ostree_root
is root of the newly deployed ostree sysroot. For example,bootfirmware_version=10
. - After parsing
bootfirmware_version
, it compares the version number with the existing one, obtained viafiovb
orubootenv
. - If
bootfirmware_version
fromversion.txt
is higher than the existing one, Aktualizr-lite setsbootupgrade_available
viafiovb
orubootenv
. - Reboot is to be performed.
The U-Boot boot.cmd
Script
The following steps represent the boot path from U-Boot’s perspective.
- The update is done via the U-Boot
boot.cmd
(boot.scr
) script. boot.cmd
checks if the primary path was booted.- If
upgrade_available
is set, check if a boot firmware upgrade is needed by checking thebootupgrade_available
flag. If both are true, boot firmware images are obtained from the newly deployed ostree sysroot, and written to the primary boot path offsets. Afterwards,bootupgrade_primary_updated
is set, and a regular reset signal is issued. - After the reboot, the SoC ROM tries to boot newly updated images from the primary boot path (FSBL1). If image verification fails, it will automatically fall back to FSBL2. When FSBL2 is booted, a rollback procedure will be issued, and the previous version of FSBL1 will be restored. If FSBL1 does boot, but bootcount hits bootlimit (meaning the boot procedure has not finished successfully), the rollback procedure will be also issued.
- After Linux is booted, Aktualizr-lite confirms a successful update by clearing the
upgrade_available
flag. At this point, new boot firmware images are already validated. An additional reboot is needed after this step. - After rebooting, U-Boot checks if
bootupgrade_primary_updated
is set and ifupgrade_available
is cleared. This means that Aktualizr-lite has confirmed a successful boot, and U-Boot clearsbootupgrade_primary_updated
. Otherwisebootcount
is incremented.
Adding a New Board
meta-lmp
Flash Layout File (eMMC Boot)
To deploy boot images to the destination board, STM32CubeProg
is used.
This tool uses a special configuration file (with a tsv
extension) that contains the eMMC flash layout.
For additional details, refer to the official
documentation.
As STM32CubeProg
does not support defining offsets inside boot0/boot1 hw partitions, TF-A BL2 and FIP are both combined into one image in advance.
This is done with the correct padding between them, so that they are flashed to the correct offsets during STM32CubeProg
execution:
$ cp tf-a-stm32mp157c-ev1-emmc.stm32 combo-emmc-tfa-fip-stm32mp157c-ev1.bin
$ dd if=fip-stm32mp157c-ev1-optee.bin combo-emmc-tfa-fip-stm32mp157c-ev1.bin bs=1024 seek=256 conv=notrunc
Note
Concatenation is done automatically via the flashlayouts-stm32mp1.bb
recipe.
Example TSV file:
#Opt Id Name Type IP Offset Binary
- 0x01 fsbl-boot Binary none 0x0 tf-a-stm32mp157c-ev1-usb.stm32
- 0x03 fip-boot Binary none 0x0 fip-stm32mp157c-ev1-optee.bin
PD 0x04 fsbl1 Binary mmc1 boot1 combo-emmc-tfa-fip-stm32mp157c-ev1.bin
PD 0x05 fsbl2 Binary mmc1 boot2 combo-emmc-tfa-fip-stm32mp157c-ev1.bin
PED 0x06 u-boot-env Binary mmc1 0x00080000 none
P 0x10 rootfs System mmc1 0x00100000 lmp-base-console-image-stm32mp15-eval.ext4
WKS Layout (SD Boot)
In a SD setup, two sets of FSBL images are stored, each with different offsets:
- FSBL1 17KB
- FSBL2 256KB
To support both images, the WKS file should be adjusted so that both copies are placed at the correct offsets:
part fsbl1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl1 --sourceparams="file=tf-a-stm32mp157c-dk2-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K --align 17
part fsbl2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fsbl2 --sourceparams="file=tf-a-stm32mp157c-dk2-sdcard.stm32" --ondisk mmcblk --part-type 0x8301 --fixed-size 256K
part fip1 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-a --sourceparams="file=fip-stm32mp157c-dk2-optee.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 4096K
part fip2 --source rawcopy --fstype=ext4 --fsoptions "noauto" --part-name=fip-b --sourceparams="file=fip-stm32mp157c-dk2-optee.bin" --ondisk mmcblk --part-type 0x8301 --fixed-size 4096K
part u-boot-env --source empty --part-name=uboot-env --ondisk mmcblk --part-type 0x8301 --fixed-size 16K --align 8192
part / --source otaimage --ondisk mmcblk --fstype=ext4 --align 4096
bootloader --ptable gpt
Testing FSBL Set and Auth Status
After applying the updates from previous steps, we should validate that everything is in place. This consists of two steps:
- FSBL1 vs FSBL2 detection (primary or secondary path)
- Obtain board security state (results of FSBL authentication)
To test FSBP copy detection, check boot\_part
using U-Boot shell:
STM32MP> print boot_part
boot_part=1
To check if the security status of your board is shown correctly, check boot\_auth
:
STM32MP> print boot_auth
boot_auth=0
Possible values are:
- 0 — No authentication done
- 1 — Authentication done and failed
- 2 — Authentication done and success
boot.cmd
LmP uses template-based generation for the final boot.cmd
.
It is constructed from common boot files (./meta-lmp-base/recipes-bsp/u-boot/u-boot-ostree-scr-fit
).
These contain all SoC agnostic “DEFINEs”, common functionality, and board-specific boot.cmd
, including common scripts.
Example of boot.cmd
(./meta-lmp-bsp/recipes-bsp/u-boot/u-boot-ostree-scr-fit/stm32mp15-eval/boot.cmd
):
setenv fdtfile stm32mp157c-ev1-scmi.dtb
echo "Using ${fdtfile}"
# Default boot type and device
setenv bootlimit 3
setenv devtype ${boot_device}
setenv devnum ${boot_instance}
setenv rootpart 2
setenv fit_addr 0xc4400000
setenv fdt_file_final ${fdtfile}
setenv fdt_addr 0xc4000000
setenv optee_ovl_addr 0xc4300000
setenv loadaddr 0xc4400000
setenv do_reboot "reset"
setenv check_board_closed 'if test "${boot_auth}" = "2"; then setenv board_is_closed 1; else setenv board_is_closed; fi;'
setenv check_secondary_boot 'if test "${boot_part}" = "2"; then setenv fiovb.is_secondary_boot 1; else setenv fiovb.is_secondary_boot 0; fi;'
# All values are provided in blocks (512 bytes each)
setenv bootloader 0x0
setenv bootloader2 0x200
setenv bootloader_size 0x1000
setenv bootloader_s ${bootloader}
setenv bootloader2_s ${bootloader2}
setenv bootloader_image "tf-a-stm32mp157c-ev1-emmc.stm32"
setenv bootloader_s_image ${bootloader_image}
setenv bootloader2_image "fip-stm32mp157c-ev1-optee.bin"
setenv bootloader2_s_image ${bootloader2_image}
setenv update_image_boot0 '\
echo "${fio_msg} writing ${image_path} ..."; \
run set_blkcnt && \
mmc dev ${devnum} && \
mmc partconf ${devnum} 1 1 1 && \
mmc write ${loadaddr} ${start_blk} ${blkcnt} && \
mmc partconf ${devnum} 1 1 0 \
'
setenv backup_primary_image '\
echo "${fio_msg} backing up primary boot image set ..."; \
mmc dev ${devnum} && \
mmc partconf ${devnum} 1 1 1 && \
mmc read ${loadaddr} ${bootloader} ${bootloader_size} && \
mmc partconf ${devnum} 1 1 0 && \
mmc dev ${devnum} && \
mmc partconf ${devnum} 1 1 2 && \
mmc write ${loadaddr} ${bootloader} ${bootloader_size} && \
mmc partconf ${devnum} 1 1 0 \
'
setenv restore_primary_image '\
echo "${fio_msg} restore primary boot image set ..." ; \
mmc dev ${devnum} && \
mmc partconf ${devnum} 1 1 2 && \
mmc read ${loadaddr} ${bootloader} ${bootloader_size} && \
mmc partconf ${devnum} 1 1 0 && \
mmc dev ${devnum} && \
mmc partconf ${devnum} 1 1 1 && \
mmc write ${loadaddr} ${bootloader} ${bootloader_size} && \
mmc partconf ${devnum} 1 1 0 \
'
setenv update_primary_image1 'setenv image_path "${ostree_root}/usr/lib/firmware/${bootloader_s_image}"; setenv start_blk "${bootloader_s}"; run load_image; run update_image_boot0'
setenv update_primary_image2 'setenv image_path "${ostree_root}/usr/lib/firmware/${bootloader2_s_image}"; setenv start_blk "${bootloader2_s}"; run load_image; run update_image_boot0'
setenv update_primary_image 'run update_primary_image1 && run update_primary_image2'
@@INCLUDE_COMMON_ALTERNATIVE@@
Sysroot and Signed Boot Artifacts
Boot artifacts (TF-A BL2 and FIP) are automatically deployed to sysroot during build time. On closed boards however, where the initial boot image has to be signed in advance by a private key, there is a way to add a signed binary instead.
Add lmp-boot-firmware.bbappend
to your meta-subscriber-overrides
layer.
Add both the path and the signed binary itself.
Then define the boot firmware version number by setting the LMP_BOOT_FIRMWARE_VERSION
global variable in lmp-factory-custom.inc
.
Boot firmware version information will be automatically added to both ${osroot}/usr/lib/firmware/version.txt
and the U-Boot Device Tree Blob.
Versioning convention is up to you. The only requirement is that the version string be unique; there should not be duplicates.
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://tf-a-stm32mp157c-ev1-mmc.stm32 \
+"
diff --git a/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware/tf-a-stm32mp157c-ev1-mmc.stm32 b/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware/tf-a-stm32mp157c-ev1-mmc.stm32
new file mode 100644
index 0000000..50f5013
Binary files /dev/null and b/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware/tf-a-stm32mp157c-ev1-mmc.stm32 differ
--- 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:stm32mp15-eval = "3"
Note
LMP_BOOT_FIRMWARE_VERSION
is now the preferred way to set the boot firmware version.
Defining PV
in lmp-boot-firmware.bbappend
is deprecated and should not be used.
To switch, first remove PV = "<version>"
from lmp-boot-firmware.bbappend
.
Then define LMP_BOOT_FIRMWARE_VERSION
with the appropriate version value, as in the above example.
See also