Secure Boot on Zynq UltraScale+ MPSoC

This page covers provisioning a device to enable the bootloader hardware authentication.


Helper scripts are also available in the lmp-tools repository.

Get the PMU Firmware

Get a valid version of the PMU firmware for the hardware, pmu-firmware-MACHINE.bin. This can be found in the deploy folder.

Replace MACHINE with the machine name (e.g. pmu-firmware-uz3eg-iocc-sec.bin).

Build the Bootloader

Build U-boot for the ZynqMP SoC platform with Secondary Program Loader (SPL) support. This will produce a file called u-boot-spl.bin.

Note that this file can also be found in the deploy folder when building LmP for secure targets. This is because with secure targets, the variable SPL_BINARY is set to spl/u-boot-spl.bin. This causes the build to assume that the final boot.bin will be manually generated and signed by the user.

On normal (open) targets, the LmP build process only publishes the final boot.bin binary. Without any signing requirements, the image can be created and published as part of the build process.

Create the Primary and Secondary Keys

Create a set of PEM keys for the hardware to authenticate the bootloader:

$ cat keys.bif
       [ppkfile] <PPK.pem>
       [pskfile] <PSK.pem>
       [spkfile] <SPK.pem>
       [sskfile] <SSK.pem>

$./bootgen -arch zynqmp -image keys.bif -generate_keys pem

Create the Bootable Image

Create the bootable image requesting only authentication, by using the following BIF. In this example, the PMUFW and SPL would be loaded at specific locations. It is worth mentioning that whenever authentication is enabled for the bootloader, the PMUFW will also be signed:

$ cat bootloader.bif
        [pskfile] PSK.pem
        [sskfile] SSK.pem
        [pmufw_image, load=0xffdc0000] pmu.bin
        [bootloader, authentication=rsa, destination_cpu=a53-0, load=0xfffc0000] u-boot-spl.bin

 $ ./bootgen -arch zynqmp -image bootloader.bif -w on -o boot.bin -efuseppkbits fuse-ppk.txt

Besides boot.bin, bootgen will also generate a SHA-384 of the PPK fuse-ppk.txt. This will need to be written to the PPK fuse so that the hardware can authenticate the image with the public primary key.

Check the Bootable Image

Check the integrity of the generated image:

$ ./bootgen -arch zynqmp -image boot.bin -verify

The layout of the bootable image can be read as follows:

$ ./bootgen -arch zynqmp -read boot.bin

Fuse the Primary Public Key SHA-384

Currently, there is no open source solution to read/write to the ZynqMP SoC eFUSEs. An alternative to other GUI based tools from Xilinx is the Xilinx Lightweight Provisioning Tool. It allows requests to be scripted: use this tool to write the content of fuse-ppk.txt to the PPK eFUSE.


The Xilinx LIghtweight Provisiong Tool is only shared on demand from your Xilinx support representative.

For more information on how to program the eFUSEs, please have a look at XAPP1319.

If you want to roll-out your own solution to read or write to the eFUSES, please have a look at the Xilskey service and the relevant documentation. Be aware however that these registers are only accessible from exception level 3 (EL3).

If you want to pass some of those eFUSE values to TF-A or OP-TEE, a solution would be to read them from SPL, and then add them to the secure-chosen node in the device tree. This would then be shared with those executables.

Program the Bootable Image

Unless you are booting from SD or eMMC, you will need to use the JTAG interface for the first write to QSPI. However, JTAG accessibility requires using the Xilinx VIVADO SDK, which is big commitment in terms of storage.

One alternative to a full SDK install is running Vivado in a container from a Linux® machine. During development, we used the following vivado_docker repository.

Sign the FPGA Fitstream

When authentication is enabled in the bootable image, the CSU will also authenticate the FPGA bistream before allowing it to load. Because of this, the bitstream must also be signed before adding it to the FIT image. It can be found inside the target xsa file (e.g. uz3eg_iocc_base.bit):

$ cat fpga.bif
        [auth_params] ppk_select=0; spk_id=0x00000000
        [pskfile] PSK.pem
        [sskfile] SSK.pem
        [destination_device=pl, authentication=rsa] uz3eg_iocc_base.bit

 $ ./bootgen -arch zynqmp -image fpga.bif -w on -o uz3eg_iocc_base.bit.bin

Now extend the bitstream-signed recipe to include your signed bitstream. Select it as the preferred provider for virtual/bitstream, and specify the correct binary and compatible string:

$ cat meta-lmp-bsp/conf/machine/uz3eg-iocc-sec.conf

# Signed FPGA bitstream is needed on secure/closed targets
PREFERRED_PROVIDER_virtual/bitstream = "bitstream-signed"
SPL_FPGA_BINARY = "bitstream-signed.bit.bin"
SPL_FPGA_COMPATIBLE = "u-boot,zynqmp-fpga-ddrauth"

Booting SPL

Applying this patch to U-boot, you should see the following on a successful boot:

U-Boot SPL 2021.07+xlnx+gb9b970209c (Jul 22 2021 - 10:50:54 +0000)
PMUFW:  v1.1
Loading new PMUFW cfg obj (1992 bytes)
Silicon version:        3
EL Level:       EL3
Chip ID:        zu3cg
Multiboot:      0
Secure Boot:    authenticated, not encrypted
Trying to boot from SPI
## Checking hash(es) for config config-1 ... OK
FPGA image loaded from FIT
## Checking hash(es) for Image atf ... sha256+ OK
## Checking hash(es) for Image uboot ... sha256+ OK
## Checking hash(es) for Image ubootfdt ... sha256+ OK
## Checking hash(es) for Image optee ... sha256+ OK

NOTICE:  ATF running on XCZU3CG/silicon v4/RTL5.1 at 0xfffe5000
NOTICE:  BL31: v2.4(release):xlnx_rebase_v2.4_2021.1
NOTICE:  BL31: Built : 15:34:08, Jul  9 2021

I/TC: Non-secure external DT found
I/TC: OP-TEE version: 3.10.0-106-g60c99179 (gcc version 10.2.0 (GCC)) #1 Fri Jul  9 15:34:48 UTC 2021 aarch64
I/TC: Primary CPU initializing
I/TC: Primary CPU switching to normal world boot

U-Boot 2021.07+xlnx+gb9b970209c (Jul 22 2021 - 10:54:24 +0000)


Booting a secure image disables the JTAG interface, even if no JTAG related fuses were written. Use the SPL configuration option CONFIG_SPL_ZYNQMP_RESTORE_JTAG to re-enable it on boot.

Integrating the Signed boot.bin in LmP

Now that you validated the signed boot.bin file, make sure to integrate it as part of the LmP publishing process. This is needed in order to support boot firmware updates:

meta-lmp-bsp/conf/machine/uz3eg-iocc-sec.conf:PREFERRED_PROVIDER_virtual/boot-bin = "lmp-boot-firmware"


$ cat meta-lmp-bsp/recipes-bsp/lmp-boot-firmware/lmp-boot-firmware.bbappend
PROVIDES:uz3eg-iocc-sec = "virtual/boot-bin"
PV:uz3eg-iocc-sec = "1"
SRC_URI:uz3eg-iocc-sec = "file://boot.bin"

With lmp-boot-firmware integration, the signed boot.bin file will be deployed under deploy/lmp-boot-firmware/.

For more information about boot firmware updates on Xilinx-based targets, see Boot Software Updates on Zynq UltraScale+ MPSoC.

Secure Storage (RPMB) using the PUF

The PUF can be used to generate a hardware unique key (HUK) at OP-TEE for secure storage via the eMMC RPMB partition. For PUF to be functional you will need to fuse PPK and RSA_EN (for secure boot), register the PUF and program the Syndrome data (via Red AES key). We recommend using the XLWPT tool (as described at XAPP1319) for registering PUF:

  ___  ___ _ __        _ ____
 /   /\  /| |\ \      / /  _ \
/___/  \/ | | \ \ /\ / /| |_) |
\   \     | |__\ |  | / |  __/
 \   \    \_____\_/\_/  |_|
 /   /     Zynq UltraScale+ MPSoC: ZU3EG
/__ /      Lightweight Provisioning Tool
\   \  /\  XLWP Tool Version: 1.9
 \___\/__\ ::: PUF Menu :::

 1. Register the PUF
 2. Encrypt Red AES Key w/ PUF Key
 3. Display Bootheader Mode PUF Data
 4. Program PUF-related eFUSEs
 5. Read & Display PUF-related eFUSEs

 x. Exit sub-menu

 Please make a selection -> 1 (registering the PUF)

 Please make a selection -> 2 (encrypting red AES key w/ PUF key)

 > Enter the 256-bit Red AES key (64 hex characters):
 Is the key correct?! (y/[n]) -> y

 > Enter the 96-bit AES IV (24 hex characters):
 Is the IV correct?! (y/[n]) -> y

 *** Red AES Key and IV for Black Key Captured OK! ***
 *** Black Key Created OK! ***

 Press any key to continue...

 Please make a selection -> 4 (program PUF-related eFUSEs)

 1. Syndrome, AUX, CHASH & Black Key eFUSEs

 Please make a selection -> 1

 Program Syndrome, AUX, CHASH & Black Key eFUSEs...are you sure?! (y/[n]) -> y

 *** Syndrome, AUX, CHASH & Black Key eFUSEs programmed OK! ***

 Press any key to continue...

 PUF syndrome (helper data) read from eFUSEs:

 PUF AUX value read from eFUSEs   : 0x0062C179
 PUF CHASH value read from eFUSEs : 0x8D22500B

For more information on registering the PUF, and how it is used by OP-TEE for generating a hardware unique key, see XAPP1333 and