Boot Software Updates on Zynq UltraScale+ MPSoC
Boot Artifacts
Boot Image
The boot image is generated by the bootgen tool. It contains PEK keys (if secure boot is enabled), Platform Management Unit (PMU) firmware, and Secondary Program Loader (SPL) obtained from a U-Boot build.
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.
U-boot-nodtb.bin
U-boot.dtb
- OP-TEE
- Arm Trusted Firmware (ARMv8)
- FPGA firmware
- Boot script (
bootscr
)
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.
Boot Media
Boot software updates are supported on both MMC and QSPI boot media. However, note that this requires different boot image layouts for each case.
Golden Image Search
The “golden image search” is a mechanism which BootROM uses to search for a valid boot header to load and run a boot image.
For boot header validation, the BootROM looks for the identification string XLNX
(or XNLX
in newer revisions).
When a valid identification string is found in the boot header, the checksum for the boot header is checked.
If the checksum is valid, the rest of the header and boot image—including the SPL—are loaded into the RPU or APU memory for processing.
If an image header is invalid, the BootROM increments the image header address register by 32 KB and tries again.
The offset used by BootROM for loading the boot image can be also be enforced by the user.
This is achieved by writing the boot offset value into the CSU_MULTI_BOOT
register and issuing a system reset (not a POR
).
Based on boot media used, CSU_MULTI_BOOT
may indicate the following:
- In the case of QSP boot, the multi boot value represents a multiple of 32 KB, as the boot image should be placed on 32 KB boundary.
To force BootROM to boot the image from the
0x60000
offset on QSPI,CSU_MULTI_BOOT
needs to be set to0xC
(12
in dec). This is because0x60000 = 0x8000 * 0xC
where0x8000
is the 32KB boundary, and0xC
is the multiboot value. - In the case of MMC boot, BootROM requires boot images to be stored on the first FAT partition, with a specific naming convention.
The filenames of the boot images should contain the multiboot offset, represented by 4 figures, i.e.,
boot0005.bin
. BootROM would boot this file ifCSU_MULTI_BOOT
was set to5
.
The boot image search mechanism is only available for the Quad-SPI, NAND, SD and eMMC boot modes.
MMC Boot Image Layout
All boot images (both boot.bin
and FIT images) are stored in the first FAT partition on MMC with the following naming convention:
boot0001.bin - primary boot image
boot0002.bin - secondary boot image
u-boot0001.itb - primary FIT image
u-boot0002.itb - secondary FIT image
QSPI Boot Image Layout
All boot images (both boot.bin
and FIT images) are written as raw images using these predetermined offsets:
0x0 - primary boot.bin
0x60000 - secondary boot.bin
0x100000 - primary u-boot.itb
0xaa0000 - secondary u-boot.itb
Boot Flow
PMU BootROM
- Reset and initialize CSU, prepare for the configuration stage
- Release the reset of CSU
- Enter a servicing mode
CSU BootROM
- Determine the boot mode
- Activate golden image search mechanism
- Load a boot image
- Perform verification of boot image (in case secure boot is enabled)
- Extract SPL and PMU firmware from boot image
- Load PMU firmware
- Load and jump to SPL
SPL
- Initialize DDR
- Calculate filename (MMC boot)/ QSPI offset for the FIT image
- Load U-Boot FIT-image
- Perform verification
- Extract components
- Load FPGA firmware
- 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
As mentioned under Golden Image Search, the offset used by BootROM for loading the boot image can also be enforced by the user. This implies that multiple boot images can be stored on the media. This gives the possibility to use the A/B approach in Over The Air updates. This is where “A” (primary boot path)represents stable boot image set, and “B” (secondary boot path) is the newly updated, not-validated-yet, images.
Libaktualizr and Aktualizr-lite
- 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. Example of contents: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 should be performed.
U-Boot boot.cmd
Script
- The actual update is done via the U-Boot
boot.cmd
script. boot.cmd
checks if primary path is booted.- If
upgrade\_available
is set, check if 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, then written to the secondary boot path offsets. The multiboot offset value is then set, and a system reset is issued to enforce BootROM to boot secondary boot path. - After reboot, the secondary boot path is executed, the condition verification from step 2 is being checked again. This time it is not true, so the regular boot of Linux is done.
- After Linux is booted, aktualizr-lite confirms a successful update by clearing
upgrade\_available
. At this point, new boot firmware images have been validated and are ready to be flashed to the stable primary path. An additional reboot is needed after this step. - Regular reset
Add a New Board
U-Boot
SPL: FIT Filename Calculation During MMC Boot
U-Boot SPL automatically detects what next image to boot based on the CSU_MULTI_BOOT
register value.
In MMC boot, BootROM expects all boot images to be stored on the first FAT partition.
We need to boot the FIT image which corresponds to the multiboot offset.
Below is an example of how the final filename of the FIT image is calculated on ZynqMP SoCs
(extract from board/xilinx/zynqmp/zynqmp.c
):
int spl_mmc_get_uboot_payload_filename(char *filename, size_t len,
const u32 boot_device)
{
int ret;
u32 multiboot;
if (!filename)
return -1;
multiboot = multi_boot_get();
if (multiboot)
ret = snprintf(filename, len, "u-boot%04d.itb", multiboot);
else
ret = snprintf(filename, len, "u-boot.itb");
if (ret < 0) {
printf("Can't construct SPL payload filename");
return ret;
}
printf("SPL: Booting %s\n", filename);
return 0;
}
SPL: FIT Offset Calculation During QSPI Boot
The offset for the FIT image is calculated from the current value of the CSU_MULTI_BOOT
register.
The multiboot value is multiplied by 0x8000
(32 Kb boundary), and the final value is used as offset of the raw FIT image on QSPI.
Below is an example of how final offset is calculated on ZynqMP SoCs (extract from board/xilinx/zynqmp/zynqmp.c
):
unsigned int spl_spi_get_uboot_offs(struct spi_flash *flash)
{
int ret;
u32 multiboot;
u32 payload_offset = 0;
u32 boot_image_offset = 0x0;
multiboot = multi_boot_get();
boot_image_offset = golden_image_boundary * multiboot;
/*
* Default values are:
* Primary boot.bin offset - 0x0 (multiboot == 0)
* Secondary boot.bin offset - 0x50000 (multiboot == 10,
* as 10 * 32KB == 0x50000)
*/
if (boot_image_offset == CONFIG_SYS_SPI_BOOT_IMAGE_OFFS) {
payload_offset = CONFIG_SYS_SPI_U_BOOT_OFFS;
} else if (boot_image_offset == CONFIG_SYS_SPI_BOOT_IMAGE_OFFS2) {
payload_offset = CONFIG_SYS_SPI_U_BOOT_OFFS2;
} else {
printf("Invalid value of multiboot register, value = %d\n",
multiboot);
hang();
}
printf("SPL: Booting next image from 0x%x SPI offset\n",
payload_offset);
return payload_offset;
}
meta-lmp
The lmp.cfg
File: QSPI boot
To enable support of multiboot, adjust lmp.cfg
for your board.
Add the following config options:
CONFIG_SYS_SPI_BOOT_IMAGE_OFFS=0x0
CONFIG_SYS_SPI_BOOT_IMAGE_OFFS2=0x60000
CONFIG_SYS_SPI_U_BOOT_OFFS=0x100000
CONFIG_SYS_SPI_U_BOOT_OFFS2=0xaa0000
These values correspond to the offsets of primary and secondary boot image sets (boot.bin
and u-boot.itb
).
Pre-Load boot.cmd
With SPL
As boot.cmd
depends on U-Boot commands for booting Linux, it should align with the U-Boot version.
In regular setups without boot firmware update support, boot.cmd
is stored in the first FAT partition in eMMC/SD.
To get boot.cmd
updates together with other boot software images, it should be moved from the FAT partition to the U-Boot FIT image.
To do this, edit lmp-machine-custom.inc
, adding this line for your board:
BOOTSCR_LOAD_ADDR:sota = "0x21000000"
This change will include boot.cmd
into the U-Boot FIT image, alongside the TF-A/OP-TEE/U-Boot proper/U-Boot dtb images.
When SPL parses the U-Boot FIT image (u-boot.itb), it 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 get the boot script from, adjust the CONFIG\_BOOTCOMMAND
param in your U-Boot lmp.cfg
for the board.
CONFIG_BOOTCOMMAND="setenv verify 1; source 0x44800000; reset"
Test Basic API
After applying updates from the previous steps, we should validate that everything is in place. This consists of two steps:
- Check that the multi boot U-Boot command is functional
- Obtain board security state (open/closed states).
To test booting the primary/secondary boot path, use the two U-Boot commands multi\_boot
and reset
.
Example of test:
U-Boot SPL 2022.01+xlnx+g9039256f80 (Jan 24 2022 - 14:57:34 +0000)
...
Chip ID: zu3eg
Multiboot: 0
Trying to boot from SPI
SPL: Booting next image from 0x100000 SPI offset
.....
ZynqMP> multi_boot 0xc && reset
Set multiboot register to: 0xc (dec: 12)
QSPI boot offset to be used after reboot: 0x60000
resetting ...
U-Boot SPL 2022.01+xlnx+g9039256f80 (Jan 24 2022 - 14:57:34 +0000)
....
Multiboot: 12
Trying to boot from SPI
SPL: Booting next image from 0xaa0000 SPI offset
From the output, you can see that after setting the secondary boot (multi_boot 12
or multi_boot 0xc
; both dec and hex values are supported),
and performing a reset, BootROM boots images from secondary boot path (SPL: Booting next image from 0xaa0000 SPI offset
).
To check if the security status of your board is detected correctly, use the is\_boot\_authenticated
command:
ZynqMP> is_boot_authenticated
Board is in open state
boot.cmd
LmP uses a 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 SoC agnostic DEFINEs, common functionality, and a board specific boot.cmd
, which includes common scripts.
Example of board boot.cmd
(./meta-lmp-bsp/recipes-bsp/u-boot/u-boot-ostree-scr-fit/uz/boot.cmd
):
# set default fdt_file
setenv fdt_file system-top.dtb
echo "Using ${fdt_file}"
# Default boot type and device
setenv bootlimit 3
setenv devtype mmc
setenv devnum ${bootseq}
setenv bootpart 1
setenv rootpart 2
setenv loadaddr 0x10000000
setenv fdt_addr 0x40000000
setenv optee_ovl_addr 0x22000000
setenv fdt_file_final ${fdt_file}
setenv fit_addr ${ramdisk_addr_r}
setenv bootloader_image "boot.bin"
setenv bootloader_s_image ${bootloader_image}
setenv bootloader2_image "u-boot.itb"
setenv bootloader2_s_image ${bootloader2_image}
setenv check_board_closed "is_boot_authenticated"
setenv check_secondary_boot "multi_boot"
if test "${modeboot}" = "qspiboot"; then
# Use SD for open boards, and eMMC for closed
run check_board_closed
if test -n "${board_is_closed}"; then
# Use eMMC for further loading/booting Linux FIT image
setenv devnum 0
else
# Use SD for further loading/booting Linux FIT image
setenv devnum 1
fi
setenv qspi_bootloader_offset 0x0
setenv qspi_bootloader_s_offset 0x60000
setenv qspi_bootloader2_offset 0x100000
setenv qspi_bootloader2_s_offset 0xaa0000
setenv setup_update 'sf probe && setenv update_cmd "sf update ${loadaddr}"'
# Boot images (primary and secondary)
setenv bootloader_image_update '${qspi_bootloader_offset}'
setenv bootloader_s_image_update '${qspi_bootloader_s_offset}'
# FIT image (primary and secondary)
setenv bootloader2_image_update '${qspi_bootloader2_offset}'
setenv bootloader2_s_image_update '${qspi_bootloader2_s_offset}'
setenv set_primary_boot "multi_boot 0"
setenv set_secondary_boot "multi_boot 12"
else
setenv setup_update 'setenv update_cmd "mmc dev ${devnum} && fatwrite mmc ${devnum}:${bootpart} ${loadaddr}"'
# Boot images (primary and secondary)
setenv bootloader_image_update 'boot0001.bin'
setenv bootloader_s_image_update 'boot0002.bin'
# FIT image (primary and secondary)
setenv bootloader2_image_update 'u-boot0001.itb'
setenv bootloader2_s_image_update 'u-boot0002.itb'
setenv set_primary_boot "multi_boot 1"
setenv set_secondary_boot "multi_boot 2"
fi
# Writing images
run setup_update
setenv update_primary_image 'echo "${fio_msg} writing ${image_path} ..."; setenv run_update "${update_cmd} ${bootloader_image_update} ${filesize}"; run run_update'
setenv update_secondary_image 'echo "${fio_msg} writing ${image_path} ..."; setenv run_update "${update_cmd} ${bootloader_s_image_update} ${filesize}"; run run_update'
setenv update_primary_image2 'echo "${fio_msg} writing ${image_path} ..."; setenv run_update "${update_cmd} ${bootloader2_image_update} ${filesize}"; run run_update'
setenv update_secondary_image2 'echo "${fio_msg} writing ${image_path} ..."; setenv run_update "${update_cmd} ${bootloader2_s_image_update} ${filesize}"; run run_update'
setenv do_reboot "reset"
@@INCLUDE_COMMON@@
Sysroot and Signed Boot Artifacts
All boot artifacts (boot.bin
and U-Boot FIT) are automatically deployed to sysroot during build time.
However, for closed boards where the initial boot image must be signed in advance by a private key, there is way to add the signed binary instead.
To do that, add lmp-boot-firmware.bbappend
to your meta-subscriber-overrides
layer.
You will add 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 wil be added automatically to ${osroot}/usr/lib/firmware/version.txt
and the U-Boot Device Tree Blob.
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/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
--- 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:uz3eg-iocc-sec = "3"
Note
LMP_BOOT_FIRMWARE_VERSION
is now the preferred way to set boot firmware version.
Defining PV
in lmp-boot-firmware.bbappend
is deprecated and should not be used.
To switch, remove the PV = "<version>"
line from lmp-boot-firmware.bbappend
.
Then define LMP_BOOT_FIRMWARE_VERSION
with the appropriate version value, as shown in the example above.
Deploy Boot Images to QSPI Flash
If QSPI is chosen as the main boot media, you can use the U-Boot shell—loaded via serial console mode or other boot media—for image provisioning with corresponding offsets on QSPI:
ZynqMP> sf probe; setenv loadaddr 0x8000000; mmc dev ${bootseq}; fatload mmc ${bootseq}:1 ${loadaddr} boot0001.bin; sf update ${loadaddr} 0x0 ${filesize}; sf update ${loadaddr} 0x60000 ${filesize}; fatload mmc ${bootseq}:1 ${loadaddr} u-boot0001.itb; sf update ${loadaddr} 0x100000 ${filesize}; sf update ${loadaddr} 0xaa0000 ${filesize};
SF: Detected n25q256ax1 with page size 512 Bytes, erase size 128 KiB, total 64 MiB
switch to partitions #0, OK
mmc1 is current device
280752 bytes read in 37 ms (7.2 MiB/s)
device 0 offset 0x0, size 0x448b0
0 bytes written, 280752 bytes skipped in 0.405s, speed 709851 B/s
device 0 offset 0x60000, size 0x448b0
0 bytes written, 280752 bytes skipped in 0.405s, speed 709851 B/s
7179209 bytes read in 505 ms (13.6 MiB/s)
device 0 offset 0x100000, size 0x6d8bc9
0 bytes written, 7179209 bytes skipped in 7.433s, speed 1025601 B/s
device 0 offset 0xaa0000, size 0x6d8bc9
0 bytes written, 7179209 bytes skipped in 7.433s, speed 1025601 B/s