Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/test-build-aarch64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ on:
make-args:
required: false
type: string
psu-init-stub:
# Generate a build-only stub for the board psu_init_gpl.c (ZynqMP FSBL).
required: false
type: boolean
default: false

jobs:

Expand Down Expand Up @@ -95,6 +100,29 @@ jobs:
run: |
make -C tools/keytools && make -C tools/bin-assemble

- name: Generate ZynqMP psu_init build-only stub
if: ${{ inputs.psu-init-stub }}
run: |
cat > hal/board/zynqmp/psu_init_gpl.c <<'STUB'
/* CI build-only stub for the board psu_init_gpl.c (XSA-generated, not
* tracked in-tree). Satisfies the hal/zynqmp_psu_shim.c externs so the
* FSBL links; performs NO real PS init -- never run on hardware. */
unsigned long psu_mio_init_data(void) { return 1UL; }
unsigned long psu_peripherals_pre_init_data(void) { return 1UL; }
unsigned long psu_pll_init_data(void) { return 1UL; }
unsigned long psu_clock_init_data(void) { return 1UL; }
unsigned long psu_ddr_init_data(void) { return 1UL; }
unsigned long psu_ddr_phybringup_data(void) { return 1UL; }
unsigned long psu_peripherals_init_data(void) { return 1UL; }
unsigned long psu_resetin_init_data(void) { return 1UL; }
unsigned long psu_serdes_init_data(void) { return 1UL; }
unsigned long psu_resetout_init_data(void) { return 1UL; }
int serdes_fixcal_code(void) { return 0; }
unsigned long psu_peripherals_powerdwn_data(void) { return 1UL; }
unsigned long psu_afi_config(void) { return 1UL; }
unsigned long psu_ddr_qos_init_data(void) { return 1UL; }
STUB

- name: Build wolfboot
run: |
make ${{inputs.make-args}}
24 changes: 24 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,30 @@ jobs:
arch: aarch64
config-file: ./config/examples/zynqmp_sdcard.config

zynqmp_fsbl_test:
uses: ./.github/workflows/test-build-aarch64.yml
with:
arch: aarch64
config-file: ./config/examples/zynqmp_fsbl.config
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp
psu-init-stub: true

zynqmp_fsbl_sec_test:
uses: ./.github/workflows/test-build-aarch64.yml
with:
arch: aarch64
config-file: ./config/examples/zynqmp_fsbl.config
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp ZYNQMP_SEC=1 ZYNQMP_PUF_SELFTEST=1 ZYNQMP_AES_SELFTEST=1
psu-init-stub: true

zynqmp_fsbl_sd_test:
uses: ./.github/workflows/test-build-aarch64.yml
with:
arch: aarch64
config-file: ./config/examples/zynqmp_fsbl_sd.config
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp DISK_SDCARD=1
psu-init-stub: true

zynq7000_test:
uses: ./.github/workflows/test-build.yml
with:
Expand Down
52 changes: 52 additions & 0 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,58 @@ ifeq ($(ARCH),AARCH64)
CFLAGS+=-DWOLFBOOT_ZYNQMP_CSU
endif

ifeq ($(ZYNQMP_FSBL),1)
# wolfBoot fully replaces the Xilinx FSBL: the BootROM authenticates and
# loads wolfBoot into OCM at EL3, and wolfBoot runs psu_init() to bring up
# the PLLs/DDR/MIO/clocks before loading the downstream images. wolfBoot
# therefore links and runs entirely from OCM (DDR is not up at entry).
#
# The board-specific psu_init_gpl.c / psu_init_gpl.h (generated from the
# XSA, Xilinx copyright) are supplied at build time from
# ZYNQMP_PSU_INIT_DIR and are NOT part of the wolfBoot tree. The
# hal/zynqmp/ shim headers (xil_io.h, sleep.h) let that unmodified file
# compile. Set EL3_SECURE=1 in the target .config.
ZYNQMP_PSU_INIT_DIR?=hal/board/zynqmp
CFLAGS+=-DWOLFBOOT_ZYNQMP_FSBL
CFLAGS+=-Ihal/zynqmp -I$(ZYNQMP_PSU_INIT_DIR)
LSCRIPT_IN=hal/zynqmp_ocm.ld
OBJS+=hal/zynqmp_psu_shim.o
OBJS+=hal/zynqmp_atf.o
OBJS+=$(ZYNQMP_PSU_INIT_DIR)/psu_init_gpl.o

# Load the PMU configuration object (EEMI permission table) into PMU
# firmware so the APU can control the SoC nodes. Like psu_init_gpl.c the
# pm_cfg_obj.c is design-specific and supplied from ZYNQMP_PSU_INIT_DIR.
ifeq ($(ZYNQMP_PM_CFG),1)
CFLAGS+=-DWOLFBOOT_ZYNQMP_PM_CFG
OBJS+=$(ZYNQMP_PSU_INIT_DIR)/pm_cfg_obj.o
endif

# Run the PS-GTR serdes init (USB3/SATA/PCIe/DP PHY lanes). Required if the
# kernel drives a PS-GTR peripheral (e.g. USB3 dwc3, whose probe hangs on
# an unclocked PHY). Skipped by default since QSPI/SD/RGMII boot needs no
# serdes; the shim runs the full calibrated sequence when enabled.
ifeq ($(ZYNQMP_PSU_INIT_SERDES),1)
CFLAGS+=-DZYNQMP_PSU_INIT_SERDES
endif

# FSBL security features (eFuse read, PUF, AES-CSU). At EL3 these access
# the CSU/eFuse controllers directly (pmu_mmio is direct MMIO in FSBL
# mode). Read-only eFuse dump today; PUF/AES land incrementally.
ifeq ($(ZYNQMP_SEC),1)
CFLAGS+=-DWOLFBOOT_ZYNQMP_FSBL_SEC
endif
# PUF register+regenerate self-test at boot (bring-up only; registers the
# PUF every boot). Opt-in, needs ZYNQMP_SEC=1.
ifeq ($(ZYNQMP_PUF_SELFTEST),1)
CFLAGS+=-DWOLFBOOT_ZYNQMP_PUF_SELFTEST
endif
# AES-CSU known-answer self-test at boot (KUP key; eFuse-safe). Opt-in.
ifeq ($(ZYNQMP_AES_SELFTEST),1)
CFLAGS+=-DWOLFBOOT_ZYNQMP_AES_SELFTEST
endif
endif

endif

ifeq ($(TARGET),versal)
Expand Down
118 changes: 118 additions & 0 deletions config/examples/zynqmp_fsbl.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# wolfBoot configuration for AMD ZynqMP ZCU102 - FSBL REPLACEMENT (QSPI boot)
# Zynq UltraScale+ MPSoC ZU9EG - Quad-core ARM Cortex-A53
#
# wolfBoot replaces the Xilinx First Stage Boot Loader (FSBL). The BootROM
# (optionally with eFuse PPK RSA secure boot) authenticates and loads wolfBoot
# directly into OCM at 0xFFFC0000 and enters it at EL3. wolfBoot then:
# 1. runs the board psu_init() (PLLs, DDR, MIO mux, clocks)
# 2. loads + verifies the downstream images with its OWN signing keys
# 3. (Milestone 1) hands off to real ARM Trusted Firmware (BL31) which keeps
# EL3/PSCI and drops to Linux.
# PMUFW is still loaded by the BootROM via the [pmufw_image] BIF tag.
#
# Boot flow: BootROM -> wolfBoot (OCM/EL3) -> psu_init -> verify+load
# BL31 + kernel + DTB -> BL31 -> Linux
#
# The board-specific psu_init_gpl.c / psu_init_gpl.h (generated from the XSA,
# Xilinx copyright) must be supplied at build time. By default they are read
# from hal/board/zynqmp/ (override with ZYNQMP_PSU_INIT_DIR=...). They are not
# part of the wolfBoot tree. Build with:
# make ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=/path/to/board
# Package with tools/scripts/zcu102/zynqmp_wolfboot_fsbl.bif

ARCH?=AARCH64
TARGET?=zynq

# Enable the FSBL-replacement build: link/run from OCM, run psu_init at boot,
# pull in the board psu_init_gpl.o + the hal/zynqmp shim.
ZYNQMP_FSBL?=1
ZYNQMP_PSU_INIT_DIR?=hal/board/zynqmp

# Load the PMU configuration object (EEMI permission table) into PMU firmware
# so the APU can control SoC nodes; needs hal/board/zynqmp/pm_cfg_obj.c.
ZYNQMP_PM_CFG?=1

# Run the PS-GTR serdes init so USB3/SATA/PCIe/DP PHY lanes are clocked.
# Needed for the kernel's USB (dwc3) probe not to hang. Set 0 to skip (QSPI/SD
# boot does not need serdes).
ZYNQMP_PSU_INIT_SERDES?=1

# FSBL security features (eFuse read/PUF/AES-CSU) via direct EL3 CSU/eFuse
# access. Read-only eFuse dump today. Set 0 to exclude.
ZYNQMP_SEC?=1

# BootROM enters the [bootloader] partition at EL3. Run wolfBoot at EL3.
CFLAGS_EXTRA+=-DEL3_SECURE=1

# DDR-training delay correction. The BootROM leaves the IOU_SCNTRS system
# counter running at the ~1.6GHz pre-divider rate during psu_init (the /15
# TIMESTAMP_REF_CTRL divisor does not re-latch the already-running counter),
# while usleep() computes its tick budget at the nominal 100MHz timestamp
# clock. Without correction every psu_ddr_phybringup settle delay is ~16x too
# short and DDR PHY training comes up marginal. Hardware-measured: a 16x scale
# restores correct training. See hal/zynqmp_psu_shim.c.
CFLAGS_EXTRA+=-DZYNQMP_USLEEP_SCALE=16

WOLFBOOT_VERSION?=0

# RSA 4096-bit with SHA3-384 (downstream image signing)
SIGN?=RSA4096
HASH?=SHA3
IMAGE_HEADER_SIZE?=1024

# Software ARMv8+Crypto assembly for SHA3 (no CSU/PMU SMC available at EL3
# until BL31 is resident).
NO_ARM_ASM?=0
HW_SHA3?=0

DEBUG?=0
DEBUG_SYMBOLS=1
DEBUG_UART=1
# CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1 # per-chunk QSPI/DDR debug spew; enable only for bring-up

VTOR?=1
CORTEX_M0?=0
NO_ASM?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
V?=0
SPMATH?=1
RAM_CODE?=0
DUALBANK_SWAP?=0
PKA?=0
WOLFTPM?=0

# Downstream images are read from QSPI (not XIP; wolfBoot executes from OCM).
EXT_FLASH?=1
SPI_FLASH?=0
NO_XIP=1
USE_GCC=1
ELF?=1

# Native gzip decompression for FIT subimages
GZIP?=1

# Flash Sector Size
WOLFBOOT_SECTOR_SIZE=0x20000
# Application Partition Size
WOLFBOOT_PARTITION_SIZE=0x2A00000
# wolfBoot self-location (OCM). Must match ORIGIN in hal/zynqmp_ocm.ld.
WOLFBOOT_ORIGIN=0xFFFC0000
# Location in QSPI for Primary Boot Partition (OS payload: FIT kernel+DTB)
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x800000
# Load Partition to RAM Address (DDR, after psu_init)
WOLFBOOT_LOAD_ADDRESS?=0x10000000
# Location in QSPI for Secondary (update) Partition
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x3A00000
# Location to store wolfBoot state
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x63E0000

# DTS (Device Tree)
WOLFBOOT_LOAD_DTS_ADDRESS?=0x11800000
WOLFBOOT_DTS_BOOT_ADDRESS?=0x7B0000
WOLFBOOT_DTS_UPDATE_ADDRESS?=0x39B0000

CROSS_COMPILE=aarch64-none-elf-

# Speed up reads from flash by using larger blocks
CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096
88 changes: 88 additions & 0 deletions config/examples/zynqmp_fsbl_sd.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# wolfBoot configuration for AMD ZynqMP ZCU102 - FSBL REPLACEMENT (SD boot)
# Zynq UltraScale+ MPSoC ZU9EG - Quad-core ARM Cortex-A53
#
# Same FSBL-replacement role as config/examples/zynqmp_fsbl.config, but the
# BootROM loads wolfBoot from BOOT.BIN on the SD card FAT partition, and
# wolfBoot reads the downstream images from MBR partitions on the same card.
#
# Boot flow: BootROM -> wolfBoot (OCM/EL3) -> psu_init -> verify+load
# BL31 + kernel + DTB (from SD) -> BL31 -> Linux
#
# Supply the board psu_init_gpl.c/.h at build time (see zynqmp_fsbl.config).
# Build: make ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=/path/to/board
# Package: tools/scripts/zcu102/zynqmp_wolfboot_fsbl.bif

ARCH?=AARCH64
TARGET?=zynq

ZYNQMP_FSBL?=1
ZYNQMP_PSU_INIT_DIR?=hal/board/zynqmp

# BootROM enters the [bootloader] partition at EL3.
CFLAGS_EXTRA+=-DEL3_SECURE=1

WOLFBOOT_VERSION?=0

SIGN?=RSA4096
HASH?=SHA3
IMAGE_HEADER_SIZE?=1024

NO_ARM_ASM?=0
HW_SHA3?=0

DEBUG?=0
DEBUG_SYMBOLS=1
DEBUG_UART=1
CFLAGS_EXTRA+=-DDEBUG_ZYNQ=1

# SD card support - use SDHCI driver (SD1 external slot on ZCU102)
DISK_SDCARD?=1
DISK_EMMC?=0
CFLAGS_EXTRA+=-DSDHCI_FORCE_CARD_DETECT

# No QSPI in the SD configuration
EXT_FLASH?=0
NO_XIP=1

ELF?=1
GZIP?=1

VTOR?=1
CORTEX_M0?=0
NO_ASM?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
V?=0
SPMATH?=1
RAM_CODE?=0
DUALBANK_SWAP?=0
PKA?=0
WOLFTPM?=0

USE_GCC=1
CROSS_COMPILE=aarch64-none-elf-

# MBR partition layout (same as zynqmp_sdcard.config):
# part[0]=boot (FAT32, BOOT.BIN), part[1]=OFP_A, part[2]=OFP_B, part[3]=rootfs
WOLFBOOT_NO_PARTITIONS=1
CFLAGS_EXTRA+=-DBOOT_PART_A=1
CFLAGS_EXTRA+=-DBOOT_PART_B=2
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000
CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\"

# wolfBoot self-location (OCM). Must match ORIGIN in hal/zynqmp_ocm.ld.
WOLFBOOT_ORIGIN=0xFFFC0000
# Bound the RAM load of the (not-yet-authenticated) disk image. Required with
# WOLFBOOT_NO_PARTITIONS=1 (update_disk.c). 64MB comfortably fits the signed
# FIT (kernel+atf+fdt) at WOLFBOOT_LOAD_ADDRESS. NOTE: the SD FSBL boot path is
# not yet HW-validated (QSPI is); this config is build-tested only.
WOLFBOOT_RAMBOOT_MAX_SIZE=0x4000000
# Load Partition to RAM Address (DDR, after psu_init)
WOLFBOOT_LOAD_ADDRESS?=0x10000000
# DTS (Device Tree) load address
WOLFBOOT_LOAD_DTS_ADDRESS?=0x1000

# Required for test-app (even with WOLFBOOT_NO_PARTITIONS=1)
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80200000
WOLFBOOT_PARTITION_SIZE=0x4000000
WOLFBOOT_SECTOR_SIZE=0x1000
52 changes: 52 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -3709,6 +3709,58 @@ This target supports **two boot paths**:
- **QSPI boot** (primary, production-style): `config/examples/zynqmp.config`
- **SD card boot** (MBR, A/B images): `config/examples/zynqmp_sdcard.config`

In both of the above, wolfBoot is loaded *by* the Xilinx FSBL and runs at EL2. wolfBoot can also replace the FSBL entirely -- see the next section.

### wolfBoot as a full FSBL replacement (`ZYNQMP_FSBL=1`)

Instead of being loaded by the Xilinx FSBL, wolfBoot can *be* the FSBL. The BootROM authenticates and loads wolfBoot directly (as the boot-image `[bootloader]` partition) into the 256 KB OCM at `0xFFFC0000` and enters it at EL3. wolfBoot then runs the board `psu_init()` (PLLs, DDR controller + PHY training, MIO mux, clocks) and loads + verifies the downstream images with its own keys:

```
BootROM -> wolfBoot (OCM, EL3) -> psu_init -> verify+load BL31 + kernel + DTB -> BL31 (EL3) -> Linux
```

PMUFW is still loaded by the BootROM via the `[pmufw_image]` BIF tag. Milestone 1 keeps real ARM Trusted Firmware (BL31) as the resident EL3 monitor.

**Board PS init (psu_init) drop-in.** DDR controller init and PHY training are board/XSA specific and are generated by the Xilinx tools; they carry the Xilinx copyright and are not part of the wolfBoot tree. Copy your generated `psu_init_gpl.c` and `psu_init_gpl.h` into `hal/board/zynqmp/` (gitignored) or point `ZYNQMP_PSU_INIT_DIR` at them. For the ZCU102 these come from PetaLinux/Vitis (`project-spec/hw-description/psu_init_gpl.*`). See `hal/board/zynqmp/README.txt`. wolfBoot-owned shims at `hal/zynqmp/` (`xil_io.h`, `sleep.h`) let the unmodified file compile.

**Build.** Link/run from OCM and run psu_init at boot:

```sh
make ZYNQMP_FSBL=1 # QSPI: config/examples/zynqmp_fsbl.config
make ZYNQMP_FSBL=1 \
ZYNQMP_PSU_INIT_DIR=/path/to/hw-description # if not in hal/board/zynqmp/
```

The SD variant is `config/examples/zynqmp_fsbl_sd.config`. wolfBoot is ~197 KB of the 256 KB OCM (single-stage; wolfBoot's own code does not need DDR).

**Package the boot image.** wolfBoot is the bootloader; there is no `zynqmp_fsbl.elf`:

```sh
cp wolfboot.elf pmufw.elf tools/scripts/zcu102/ # pmufw.elf from PetaLinux/Vitis
cd tools/scripts/zcu102
bootgen -arch zynqmp -image zynqmp_wolfboot_fsbl.bif -w -o BOOT.BIN
```

Use `zynqmp_wolfboot_fsbl_auth.bif` for the Xilinx hardware root of trust (RSA authentication via the eFuse PPK; see comments in that file).

**Flash QSPI** (over JTAG, any boot mode; `program_flash` is under the Vitis `bin/`):

```sh
program_flash -f BOOT.BIN -fsbl zynqmp_fsbl.elf \
-flash_type qspi-x8-dual_parallel -flash_density 1024 \
-verify -url tcp:127.0.0.1:3121
```

`zynqmp_fsbl.elf` here is the stock Xilinx FSBL used only as the `program_flash` flash-writer bootstrap (it is not flashed). Then set SW6 to QSPI32 and power-cycle.

**SD card.** Write `BOOT.BIN` to the FAT (boot) partition of the SD card, set SW6 to SD, power-cycle.

**Boot mode switches (SW6).** See the SD-card SW6 table below (JTAG `0000`, QSPI32 `0010`, SD1 `1110`).

**Downstream images (direct-kernel BL33).** The boot image wolfBoot loads is a wolfBoot-signed FIT containing an `atf` (BL31) sub-image plus the kernel and DTB. Two requirements: build BL31 with its link base in DDR (the default OCM `0xFFFE0000` overlaps wolfBoot), and, because BL33 is the kernel directly (no U-Boot), apply `tools/scripts/zcu102/tf-a-zynqmp-wolfboot-dtb.patch` so BL31 forwards the DTB (which wolfBoot publishes in `PMU_GLOBAL.GLOBAL_GEN_STORAGE5`) into the kernel's `x0`.

**Validation status (ZCU102).** The FSBL path is hardware-validated: wolfBoot runs at `Current EL: 3`, cold-boot `psu_init` brings up DDR (read/write tested), clocks, MIO, UART, and QSPI (flash ID read), from both a JTAG load and a real BootROM QSPI cold boot. The full downstream chain to Linux (FIT + DDR-linked BL31 + the TF-A patch) is in progress.

### Prerequisites

1. **Xilinx Vitis 2024.1 or newer**
Expand Down
Loading
Loading