Skip to content

Commit 0fec91f

Browse files
committed
zynqmp: wolfBoot ZynqMP FSBL with signed FIT Linux boot and EL3 security (eFuse/PUF/AES-CSU)
1 parent 4308b36 commit 0fec91f

28 files changed

Lines changed: 2643 additions & 146 deletions

.github/workflows/test-build-aarch64.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ on:
1313
make-args:
1414
required: false
1515
type: string
16+
psu-init-stub:
17+
# Generate a build-only stub for the board psu_init_gpl.c (ZynqMP FSBL).
18+
required: false
19+
type: boolean
20+
default: false
1621

1722
jobs:
1823

@@ -95,6 +100,29 @@ jobs:
95100
run: |
96101
make -C tools/keytools && make -C tools/bin-assemble
97102
103+
- name: Generate ZynqMP psu_init build-only stub
104+
if: ${{ inputs.psu-init-stub }}
105+
run: |
106+
cat > hal/board/zynqmp/psu_init_gpl.c <<'STUB'
107+
/* CI build-only stub for the board psu_init_gpl.c (XSA-generated, not
108+
* tracked in-tree). Satisfies the hal/zynqmp_psu_shim.c externs so the
109+
* FSBL links; performs NO real PS init -- never run on hardware. */
110+
unsigned long psu_mio_init_data(void) { return 1UL; }
111+
unsigned long psu_peripherals_pre_init_data(void) { return 1UL; }
112+
unsigned long psu_pll_init_data(void) { return 1UL; }
113+
unsigned long psu_clock_init_data(void) { return 1UL; }
114+
unsigned long psu_ddr_init_data(void) { return 1UL; }
115+
unsigned long psu_ddr_phybringup_data(void) { return 1UL; }
116+
unsigned long psu_peripherals_init_data(void) { return 1UL; }
117+
unsigned long psu_resetin_init_data(void) { return 1UL; }
118+
unsigned long psu_serdes_init_data(void) { return 1UL; }
119+
unsigned long psu_resetout_init_data(void) { return 1UL; }
120+
int serdes_fixcal_code(void) { return 0; }
121+
unsigned long psu_peripherals_powerdwn_data(void) { return 1UL; }
122+
unsigned long psu_afi_config(void) { return 1UL; }
123+
unsigned long psu_ddr_qos_init_data(void) { return 1UL; }
124+
STUB
125+
98126
- name: Build wolfboot
99127
run: |
100128
make ${{inputs.make-args}}

.github/workflows/test-configs.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,30 @@ jobs:
750750
arch: aarch64
751751
config-file: ./config/examples/zynqmp_sdcard.config
752752

753+
zynqmp_fsbl_test:
754+
uses: ./.github/workflows/test-build-aarch64.yml
755+
with:
756+
arch: aarch64
757+
config-file: ./config/examples/zynqmp_fsbl.config
758+
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp
759+
psu-init-stub: true
760+
761+
zynqmp_fsbl_sec_test:
762+
uses: ./.github/workflows/test-build-aarch64.yml
763+
with:
764+
arch: aarch64
765+
config-file: ./config/examples/zynqmp_fsbl.config
766+
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp ZYNQMP_SEC=1 ZYNQMP_PUF_SELFTEST=1 ZYNQMP_AES_SELFTEST=1
767+
psu-init-stub: true
768+
769+
zynqmp_fsbl_sd_test:
770+
uses: ./.github/workflows/test-build-aarch64.yml
771+
with:
772+
arch: aarch64
773+
config-file: ./config/examples/zynqmp_fsbl_sd.config
774+
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp DISK_SDCARD=1
775+
psu-init-stub: true
776+
753777
zynq7000_test:
754778
uses: ./.github/workflows/test-build.yml
755779
with:

arch.mk

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,58 @@ ifeq ($(ARCH),AARCH64)
8383
CFLAGS+=-DWOLFBOOT_ZYNQMP_CSU
8484
endif
8585

86+
ifeq ($(ZYNQMP_FSBL),1)
87+
# wolfBoot fully replaces the Xilinx FSBL: the BootROM authenticates and
88+
# loads wolfBoot into OCM at EL3, and wolfBoot runs psu_init() to bring up
89+
# the PLLs/DDR/MIO/clocks before loading the downstream images. wolfBoot
90+
# therefore links and runs entirely from OCM (DDR is not up at entry).
91+
#
92+
# The board-specific psu_init_gpl.c / psu_init_gpl.h (generated from the
93+
# XSA, Xilinx copyright) are supplied at build time from
94+
# ZYNQMP_PSU_INIT_DIR and are NOT part of the wolfBoot tree. The
95+
# hal/zynqmp/ shim headers (xil_io.h, sleep.h) let that unmodified file
96+
# compile. Set EL3_SECURE=1 in the target .config.
97+
ZYNQMP_PSU_INIT_DIR?=hal/board/zynqmp
98+
CFLAGS+=-DWOLFBOOT_ZYNQMP_FSBL
99+
CFLAGS+=-Ihal/zynqmp -I$(ZYNQMP_PSU_INIT_DIR)
100+
LSCRIPT_IN=hal/zynqmp_ocm.ld
101+
OBJS+=hal/zynqmp_psu_shim.o
102+
OBJS+=hal/zynqmp_atf.o
103+
OBJS+=$(ZYNQMP_PSU_INIT_DIR)/psu_init_gpl.o
104+
105+
# Load the PMU configuration object (EEMI permission table) into PMU
106+
# firmware so the APU can control the SoC nodes. Like psu_init_gpl.c the
107+
# pm_cfg_obj.c is design-specific and supplied from ZYNQMP_PSU_INIT_DIR.
108+
ifeq ($(ZYNQMP_PM_CFG),1)
109+
CFLAGS+=-DWOLFBOOT_ZYNQMP_PM_CFG
110+
OBJS+=$(ZYNQMP_PSU_INIT_DIR)/pm_cfg_obj.o
111+
endif
112+
113+
# Run the PS-GTR serdes init (USB3/SATA/PCIe/DP PHY lanes). Required if the
114+
# kernel drives a PS-GTR peripheral (e.g. USB3 dwc3, whose probe hangs on
115+
# an unclocked PHY). Skipped by default since QSPI/SD/RGMII boot needs no
116+
# serdes; the shim runs the full calibrated sequence when enabled.
117+
ifeq ($(ZYNQMP_PSU_INIT_SERDES),1)
118+
CFLAGS+=-DZYNQMP_PSU_INIT_SERDES
119+
endif
120+
121+
# FSBL security features (eFuse read, PUF, AES-CSU). At EL3 these access
122+
# the CSU/eFuse controllers directly (pmu_mmio is direct MMIO in FSBL
123+
# mode). Read-only eFuse dump today; PUF/AES land incrementally.
124+
ifeq ($(ZYNQMP_SEC),1)
125+
CFLAGS+=-DWOLFBOOT_ZYNQMP_FSBL_SEC
126+
endif
127+
# PUF register+regenerate self-test at boot (bring-up only; registers the
128+
# PUF every boot). Opt-in, needs ZYNQMP_SEC=1.
129+
ifeq ($(ZYNQMP_PUF_SELFTEST),1)
130+
CFLAGS+=-DWOLFBOOT_ZYNQMP_PUF_SELFTEST
131+
endif
132+
# AES-CSU known-answer self-test at boot (KUP key; eFuse-safe). Opt-in.
133+
ifeq ($(ZYNQMP_AES_SELFTEST),1)
134+
CFLAGS+=-DWOLFBOOT_ZYNQMP_AES_SELFTEST
135+
endif
136+
endif
137+
86138
endif
87139

88140
ifeq ($(TARGET),versal)

config/examples/zynqmp_fsbl.config

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# wolfBoot configuration for AMD ZynqMP ZCU102 - FSBL REPLACEMENT (QSPI boot)
2+
# Zynq UltraScale+ MPSoC ZU9EG - Quad-core ARM Cortex-A53
3+
#
4+
# wolfBoot replaces the Xilinx First Stage Boot Loader (FSBL). The BootROM
5+
# (optionally with eFuse PPK RSA secure boot) authenticates and loads wolfBoot
6+
# directly into OCM at 0xFFFC0000 and enters it at EL3. wolfBoot then:
7+
# 1. runs the board psu_init() (PLLs, DDR, MIO mux, clocks)
8+
# 2. loads + verifies the downstream images with its OWN signing keys
9+
# 3. (Milestone 1) hands off to real ARM Trusted Firmware (BL31) which keeps
10+
# EL3/PSCI and drops to Linux.
11+
# PMUFW is still loaded by the BootROM via the [pmufw_image] BIF tag.
12+
#
13+
# Boot flow: BootROM -> wolfBoot (OCM/EL3) -> psu_init -> verify+load
14+
# BL31 + kernel + DTB -> BL31 -> Linux
15+
#
16+
# The board-specific psu_init_gpl.c / psu_init_gpl.h (generated from the XSA,
17+
# Xilinx copyright) must be supplied at build time. By default they are read
18+
# from hal/board/zynqmp/ (override with ZYNQMP_PSU_INIT_DIR=...). They are not
19+
# part of the wolfBoot tree. Build with:
20+
# make ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=/path/to/board
21+
# Package with tools/scripts/zcu102/zynqmp_wolfboot_fsbl.bif
22+
23+
ARCH?=AARCH64
24+
TARGET?=zynq
25+
26+
# Enable the FSBL-replacement build: link/run from OCM, run psu_init at boot,
27+
# pull in the board psu_init_gpl.o + the hal/zynqmp shim.
28+
ZYNQMP_FSBL?=1
29+
ZYNQMP_PSU_INIT_DIR?=hal/board/zynqmp
30+
31+
# Load the PMU configuration object (EEMI permission table) into PMU firmware
32+
# so the APU can control SoC nodes; needs hal/board/zynqmp/pm_cfg_obj.c.
33+
ZYNQMP_PM_CFG?=1
34+
35+
# Run the PS-GTR serdes init so USB3/SATA/PCIe/DP PHY lanes are clocked.
36+
# Needed for the kernel's USB (dwc3) probe not to hang. Set 0 to skip (QSPI/SD
37+
# boot does not need serdes).
38+
ZYNQMP_PSU_INIT_SERDES?=1
39+
40+
# FSBL security features (eFuse read/PUF/AES-CSU) via direct EL3 CSU/eFuse
41+
# access. Read-only eFuse dump today. Set 0 to exclude.
42+
ZYNQMP_SEC?=1
43+
44+
# BootROM enters the [bootloader] partition at EL3. Run wolfBoot at EL3.
45+
CFLAGS_EXTRA+=-DEL3_SECURE=1
46+
47+
# DDR-training delay correction. The BootROM leaves the IOU_SCNTRS system
48+
# counter running at the ~1.6GHz pre-divider rate during psu_init (the /15
49+
# TIMESTAMP_REF_CTRL divisor does not re-latch the already-running counter),
50+
# while usleep() computes its tick budget at the nominal 100MHz timestamp
51+
# clock. Without correction every psu_ddr_phybringup settle delay is ~16x too
52+
# short and DDR PHY training comes up marginal. Hardware-measured: a 16x scale
53+
# restores correct training. See hal/zynqmp_psu_shim.c.
54+
CFLAGS_EXTRA+=-DZYNQMP_USLEEP_SCALE=16
55+
56+
WOLFBOOT_VERSION?=0
57+
58+
# RSA 4096-bit with SHA3-384 (downstream image signing)
59+
SIGN?=RSA4096
60+
HASH?=SHA3
61+
IMAGE_HEADER_SIZE?=1024
62+
63+
# Software ARMv8+Crypto assembly for SHA3 (no CSU/PMU SMC available at EL3
64+
# until BL31 is resident).
65+
NO_ARM_ASM?=0
66+
HW_SHA3?=0
67+
68+
DEBUG?=0
69+
DEBUG_SYMBOLS=1
70+
DEBUG_UART=1
71+
# CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1 # per-chunk QSPI/DDR debug spew; enable only for bring-up
72+
73+
VTOR?=1
74+
CORTEX_M0?=0
75+
NO_ASM?=0
76+
ALLOW_DOWNGRADE?=0
77+
NVM_FLASH_WRITEONCE?=0
78+
V?=0
79+
SPMATH?=1
80+
RAM_CODE?=0
81+
DUALBANK_SWAP?=0
82+
PKA?=0
83+
WOLFTPM?=0
84+
85+
# Downstream images are read from QSPI (not XIP; wolfBoot executes from OCM).
86+
EXT_FLASH?=1
87+
SPI_FLASH?=0
88+
NO_XIP=1
89+
USE_GCC=1
90+
ELF?=1
91+
92+
# Native gzip decompression for FIT subimages
93+
GZIP?=1
94+
95+
# Flash Sector Size
96+
WOLFBOOT_SECTOR_SIZE=0x20000
97+
# Application Partition Size
98+
WOLFBOOT_PARTITION_SIZE=0x2A00000
99+
# wolfBoot self-location (OCM). Must match ORIGIN in hal/zynqmp_ocm.ld.
100+
WOLFBOOT_ORIGIN=0xFFFC0000
101+
# Location in QSPI for Primary Boot Partition (OS payload: FIT kernel+DTB)
102+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x800000
103+
# Load Partition to RAM Address (DDR, after psu_init)
104+
WOLFBOOT_LOAD_ADDRESS?=0x10000000
105+
# Location in QSPI for Secondary (update) Partition
106+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x3A00000
107+
# Location to store wolfBoot state
108+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x63E0000
109+
110+
# DTS (Device Tree)
111+
WOLFBOOT_LOAD_DTS_ADDRESS?=0x11800000
112+
WOLFBOOT_DTS_BOOT_ADDRESS?=0x7B0000
113+
WOLFBOOT_DTS_UPDATE_ADDRESS?=0x39B0000
114+
115+
CROSS_COMPILE=aarch64-none-elf-
116+
117+
# Speed up reads from flash by using larger blocks
118+
CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# wolfBoot configuration for AMD ZynqMP ZCU102 - FSBL REPLACEMENT (SD boot)
2+
# Zynq UltraScale+ MPSoC ZU9EG - Quad-core ARM Cortex-A53
3+
#
4+
# Same FSBL-replacement role as config/examples/zynqmp_fsbl.config, but the
5+
# BootROM loads wolfBoot from BOOT.BIN on the SD card FAT partition, and
6+
# wolfBoot reads the downstream images from MBR partitions on the same card.
7+
#
8+
# Boot flow: BootROM -> wolfBoot (OCM/EL3) -> psu_init -> verify+load
9+
# BL31 + kernel + DTB (from SD) -> BL31 -> Linux
10+
#
11+
# Supply the board psu_init_gpl.c/.h at build time (see zynqmp_fsbl.config).
12+
# Build: make ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=/path/to/board
13+
# Package: tools/scripts/zcu102/zynqmp_wolfboot_fsbl.bif
14+
15+
ARCH?=AARCH64
16+
TARGET?=zynq
17+
18+
ZYNQMP_FSBL?=1
19+
ZYNQMP_PSU_INIT_DIR?=hal/board/zynqmp
20+
21+
# BootROM enters the [bootloader] partition at EL3.
22+
CFLAGS_EXTRA+=-DEL3_SECURE=1
23+
24+
WOLFBOOT_VERSION?=0
25+
26+
SIGN?=RSA4096
27+
HASH?=SHA3
28+
IMAGE_HEADER_SIZE?=1024
29+
30+
NO_ARM_ASM?=0
31+
HW_SHA3?=0
32+
33+
DEBUG?=0
34+
DEBUG_SYMBOLS=1
35+
DEBUG_UART=1
36+
CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1
37+
38+
# SD card support - use SDHCI driver (SD1 external slot on ZCU102)
39+
DISK_SDCARD?=1
40+
DISK_EMMC?=0
41+
CFLAGS_EXTRA+=-DSDHCI_FORCE_CARD_DETECT
42+
43+
# No QSPI in the SD configuration
44+
EXT_FLASH?=0
45+
NO_XIP=1
46+
47+
ELF?=1
48+
GZIP?=1
49+
50+
VTOR?=1
51+
CORTEX_M0?=0
52+
NO_ASM?=0
53+
ALLOW_DOWNGRADE?=0
54+
NVM_FLASH_WRITEONCE?=0
55+
V?=0
56+
SPMATH?=1
57+
RAM_CODE?=0
58+
DUALBANK_SWAP?=0
59+
PKA?=0
60+
WOLFTPM?=0
61+
62+
USE_GCC=1
63+
CROSS_COMPILE=aarch64-none-elf-
64+
65+
# MBR partition layout (same as zynqmp_sdcard.config):
66+
# part[0]=boot (FAT32, BOOT.BIN), part[1]=OFP_A, part[2]=OFP_B, part[3]=rootfs
67+
WOLFBOOT_NO_PARTITIONS=1
68+
CFLAGS_EXTRA+=-DBOOT_PART_A=1
69+
CFLAGS_EXTRA+=-DBOOT_PART_B=2
70+
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000
71+
CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\"
72+
73+
# wolfBoot self-location (OCM). Must match ORIGIN in hal/zynqmp_ocm.ld.
74+
WOLFBOOT_ORIGIN=0xFFFC0000
75+
# Bound the RAM load of the (not-yet-authenticated) disk image. Required with
76+
# WOLFBOOT_NO_PARTITIONS=1 (update_disk.c). 64MB comfortably fits the signed
77+
# FIT (kernel+atf+fdt) at WOLFBOOT_LOAD_ADDRESS. NOTE: the SD FSBL boot path is
78+
# not yet HW-validated (QSPI is); this config is build-tested only.
79+
WOLFBOOT_RAMBOOT_MAX_SIZE=0x4000000
80+
# Load Partition to RAM Address (DDR, after psu_init)
81+
WOLFBOOT_LOAD_ADDRESS?=0x10000000
82+
# DTS (Device Tree) load address
83+
WOLFBOOT_LOAD_DTS_ADDRESS?=0x1000
84+
85+
# Required for test-app (even with WOLFBOOT_NO_PARTITIONS=1)
86+
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80200000
87+
WOLFBOOT_PARTITION_SIZE=0x4000000
88+
WOLFBOOT_SECTOR_SIZE=0x1000

0 commit comments

Comments
 (0)