mirror of https://github.com/wolfSSL/wolfBoot.git
zynqmp: wolfBoot ZynqMP FSBL with signed FIT Linux boot and EL3 security (eFuse/PUF/AES-CSU)
parent
235bd820e6
commit
1ceaef3eee
|
|
@ -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:
|
||||
|
||||
|
|
@ -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}}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
52
arch.mk
52
arch.mk
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -3710,6 +3710,70 @@ 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`.
|
||||
|
||||
**Building BL31 (ARM Trusted Firmware).** BL31 is upstream Xilinx Arm Trusted Firmware - clone `https://github.com/Xilinx/arm-trusted-firmware.git` (validated on branch `xlnx_rebase_v2.12`); it is not vendored in the wolfBoot tree. Apply the in-tree DTB-forwarding patch and build for ZynqMP with the reset base relocated into DDR (so it does not overlap wolfBoot in OCM), for example:
|
||||
|
||||
```sh
|
||||
git clone -b xlnx_rebase_v2.12 https://github.com/Xilinx/arm-trusted-firmware.git
|
||||
cd arm-trusted-firmware
|
||||
git apply /path/to/wolfboot/tools/scripts/zcu102/tf-a-zynqmp-wolfboot-dtb.patch
|
||||
make CROSS_COMPILE=aarch64-none-elf- PLAT=zynqmp RESET_TO_BL31=1 \
|
||||
PRELOADED_BL33_BASE=0x0 ZYNQMP_ATF_MEM_BASE=0x8000000 bl31
|
||||
```
|
||||
|
||||
`ZYNQMP_ATF_MEM_BASE` sets the DDR link base; use the value your FIT `atf` sub-image loads to (must not overlap the OCM range wolfBoot runs in). The resulting `bl31.elf` is what you place in the signed FIT's `atf` sub-image. The `tf-a-zynqmp-wolfboot-dtb.patch` is the only wolfBoot-specific change; everything else is stock upstream TF-A.
|
||||
|
||||
**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 DTB patch) is hardware-validated end-to-end: wolfBoot verifies and loads the signed FIT, hands off to BL31, and boots the kernel all the way to a PetaLinux login prompt on the ZCU102.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Xilinx Vitis 2024.1 or newer**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# The board PS init is generated from your XSA by the Xilinx tools and carries
|
||||
# the Xilinx copyright; it is board/design specific and is NOT tracked in the
|
||||
# wolfBoot tree. Drop your generated files here (see README.txt).
|
||||
psu_init_gpl.c
|
||||
psu_init_gpl.h
|
||||
psu_init.c
|
||||
psu_init.h
|
||||
*.o
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
wolfBoot ZynqMP FSBL replacement - board PS init drop-in
|
||||
========================================================
|
||||
|
||||
When wolfBoot replaces the Xilinx FSBL (ZYNQMP_FSBL=1), it must run the
|
||||
board-specific PS initialization (PLLs, DDR controller + PHY training, MIO pin
|
||||
mux, clocks) before any DDR/UART/QSPI/SD access. That init is generated by the
|
||||
Xilinx tools from your hardware design (XSA) and is specific to your board and
|
||||
DDR configuration. It carries the Xilinx copyright and is therefore NOT part of
|
||||
the wolfBoot source tree.
|
||||
|
||||
What to drop here
|
||||
-----------------
|
||||
Copy your generated files into this directory:
|
||||
|
||||
hal/board/zynqmp/psu_init_gpl.c
|
||||
hal/board/zynqmp/psu_init_gpl.h
|
||||
|
||||
For the ZCU102 reference board these are produced by PetaLinux/Vitis, e.g.:
|
||||
|
||||
<petalinux-project>/project-spec/hw-description/psu_init_gpl.c
|
||||
<petalinux-project>/project-spec/hw-description/psu_init_gpl.h
|
||||
|
||||
or from the FSBL app sources:
|
||||
|
||||
.../embeddedsw/lib/sw_apps/zynqmp_fsbl/misc/zcu102/psu_init_gpl.c
|
||||
|
||||
Use the GPL variant (psu_init_gpl.*). Do not edit it.
|
||||
|
||||
How it builds
|
||||
-------------
|
||||
The unmodified Xilinx file includes <xil_io.h> and <sleep.h>. wolfBoot supplies
|
||||
minimal, wolfSSL-owned shims for those at hal/zynqmp/ (added to the include
|
||||
path automatically). The Xilinx file defines the top-level psu_init(), which
|
||||
hal/zynq.c calls at the start of hal_init().
|
||||
|
||||
Build (default location is this directory):
|
||||
|
||||
make ZYNQMP_FSBL=1
|
||||
|
||||
Or point at the files elsewhere:
|
||||
|
||||
make ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=/path/to/hw-description
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/* ZynqMP PMU firmware configuration object -- design-specific data.
|
||||
*
|
||||
* EEMI permission table the PMU firmware needs to grant the APU access to SoC
|
||||
* nodes; loaded via PM_SET_CONFIGURATION -- see zynqmp_pm_set_configuration()
|
||||
* in hal/zynq.c. Board-specific numeric data (xczu9eg / ZCU102), like
|
||||
* psu_init_gpl.c, supplied from ZYNQMP_PSU_INIT_DIR. Regenerate by extracting
|
||||
* the .sys_cfg_data section (symbol XPm_ConfigObject) from the design elf:
|
||||
* aarch64-none-elf-objcopy -O binary --only-section=.sys_cfg_data
|
||||
* zynqmp_fsbl.elf pm_cfg_obj.bin
|
||||
* then emit the words below (little-endian u32).
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
const uint32_t pm_cfg_obj[] = {
|
||||
0x00000002, 0x00000008, 0x00000001, 0x00000101, 0x00000003, 0x00000001,
|
||||
0x00000001, 0xFFFFFFFF, 0x00000300, 0x00000300, 0x00000007, 0x00000100,
|
||||
0xFFFFFFFF, 0x00000201, 0x00000201, 0x00000008, 0x00000200, 0xFFFFFFFF,
|
||||
0x00000101, 0x00000101, 0x00000102, 0x00000028, 0x0000000B, 0x00000001,
|
||||
0x00000301, 0x0000000C, 0x00000001, 0x00000301, 0x0000000D, 0x00000001,
|
||||
0x00000301, 0x0000000E, 0x00000001, 0x00000301, 0x0000000F, 0x00000001,
|
||||
0x00000101, 0x00000010, 0x00000001, 0x00000101, 0x00000011, 0x00000001,
|
||||
0x00000201, 0x00000012, 0x00000001, 0x00000201, 0x00000013, 0x00000001,
|
||||
0x00000301, 0x00000014, 0x00000001, 0x00000301, 0x00000015, 0x00000001,
|
||||
0x00000301, 0x00000016, 0x00000001, 0x00000301, 0x00000018, 0x00000001,
|
||||
0x00000301, 0x00000019, 0x00000001, 0x00000301, 0x0000001A, 0x00000001,
|
||||
0x00000301, 0x0000001B, 0x00000001, 0x00000301, 0x0000001C, 0x00000001,
|
||||
0x00000301, 0x00000020, 0x00000001, 0x00000301, 0x00000021, 0x00000001,
|
||||
0x00000301, 0x00000022, 0x00000001, 0x00000301, 0x00000023, 0x00000001,
|
||||
0x00000301, 0x00000024, 0x00000001, 0x00000301, 0x00000025, 0x00000001,
|
||||
0x00000301, 0x00000026, 0x00000001, 0x00000301, 0x00000028, 0x00000001,
|
||||
0x00000301, 0x00000029, 0x00000001, 0x00000301, 0x0000002A, 0x00000001,
|
||||
0x00000301, 0x0000002B, 0x00000001, 0x00000301, 0x0000002D, 0x00000001,
|
||||
0x00000301, 0x0000002E, 0x00000001, 0x00000301, 0x00000030, 0x00000001,
|
||||
0x00000301, 0x00000031, 0x00000001, 0x00000301, 0x00000037, 0x00000001,
|
||||
0x00000301, 0x00000038, 0x00000000, 0x00000001, 0x00000039, 0x00000000,
|
||||
0x00000100, 0x00000040, 0x00000000, 0x00000200, 0x0000003A, 0x00000001,
|
||||
0x00000301, 0x0000003B, 0x00000001, 0x00000301, 0x0000003D, 0x00000001,
|
||||
0x00000301, 0x00000045, 0x00000001, 0x00000301, 0x00000103, 0x00000003,
|
||||
0x00000001, 0x0000000C, 0x00000037, 0x00000002, 0x00000003, 0x00000003,
|
||||
0x00000013, 0x00000002, 0x00000003, 0x00000003, 0x0000000B, 0x00000002,
|
||||
0x00000003, 0x00000003, 0x0000000C, 0x00000002, 0x00000003, 0x00000003,
|
||||
0x0000000D, 0x00000002, 0x00000003, 0x00000003, 0x0000000E, 0x00000002,
|
||||
0x00000003, 0x00000003, 0x00000025, 0x00000002, 0x00000003, 0x00000003,
|
||||
0x00000026, 0x00000002, 0x00000003, 0x00000003, 0x00000028, 0x00000002,
|
||||
0x00000003, 0x00000003, 0x0000002D, 0x00000002, 0x00000003, 0x00000003,
|
||||
0x00000045, 0x00000002, 0x00000003, 0x00000003, 0x00000038, 0x00000002,
|
||||
0x00000003, 0x00000003, 0x00000100, 0x00000003, 0x0000000F, 0x00000002,
|
||||
0x00000003, 0x00000003, 0x00000010, 0x00000002, 0x00000003, 0x00000003,
|
||||
0x00000039, 0x00000002, 0x00000003, 0x00000003, 0x00000200, 0x00000003,
|
||||
0x00000011, 0x00000002, 0x00000003, 0x00000003, 0x00000012, 0x00000002,
|
||||
0x00000003, 0x00000003, 0x00000040, 0x00000002, 0x00000003, 0x00000003,
|
||||
0x00000104, 0x00000004, 0x00000001, 0x00000300, 0x00000006, 0x00000301,
|
||||
0x0000000A, 0x00000300, 0x00000009, 0x00000301, 0x00000105, 0x00000078,
|
||||
0x000003E8, 0x00000301, 0x000003E9, 0x00000301, 0x000003EA, 0x00000301,
|
||||
0x000003EB, 0x00000301, 0x000003EC, 0x00000301, 0x000003ED, 0x00000301,
|
||||
0x000003EE, 0x00000301, 0x000003EF, 0x00000301, 0x000003F0, 0x00000301,
|
||||
0x000003F1, 0x00000301, 0x000003F2, 0x00000301, 0x000003F3, 0x00000301,
|
||||
0x000003F4, 0x00000301, 0x000003F5, 0x00000301, 0x000003F6, 0x00000301,
|
||||
0x000003F7, 0x00000301, 0x000003F8, 0x00000301, 0x000003F9, 0x00000301,
|
||||
0x000003FA, 0x00000301, 0x000003FB, 0x00000301, 0x000003FC, 0x00000301,
|
||||
0x000003FD, 0x00000301, 0x000003FE, 0x00000301, 0x000003FF, 0x00000301,
|
||||
0x00000400, 0x00000301, 0x00000401, 0x00000301, 0x00000402, 0x00000301,
|
||||
0x00000403, 0x00000301, 0x00000404, 0x00000301, 0x00000405, 0x00000301,
|
||||
0x00000406, 0x00000301, 0x00000407, 0x00000301, 0x00000408, 0x00000301,
|
||||
0x00000409, 0x00000301, 0x0000040A, 0x00000301, 0x0000040B, 0x00000301,
|
||||
0x0000040C, 0x00000301, 0x0000040D, 0x00000301, 0x0000040E, 0x00000301,
|
||||
0x0000040F, 0x00000301, 0x00000410, 0x00000301, 0x00000411, 0x00000301,
|
||||
0x00000412, 0x00000301, 0x00000413, 0x00000301, 0x00000414, 0x00000301,
|
||||
0x00000415, 0x00000301, 0x00000416, 0x00000301, 0x00000417, 0x00000301,
|
||||
0x00000418, 0x00000301, 0x00000419, 0x00000301, 0x0000041A, 0x00000301,
|
||||
0x0000041B, 0x00000301, 0x0000041C, 0x00000301, 0x0000041D, 0x00000301,
|
||||
0x0000041E, 0x00000301, 0x0000041F, 0x00000301, 0x00000420, 0x00000301,
|
||||
0x00000421, 0x00000301, 0x00000422, 0x00000301, 0x00000423, 0x00000301,
|
||||
0x00000424, 0x00000301, 0x00000425, 0x00000301, 0x00000426, 0x00000301,
|
||||
0x00000427, 0x00000301, 0x00000428, 0x00000301, 0x00000429, 0x00000301,
|
||||
0x0000042A, 0x00000301, 0x0000042B, 0x00000301, 0x0000042C, 0x00000000,
|
||||
0x0000042D, 0x00000301, 0x0000042E, 0x00000301, 0x0000042F, 0x00000100,
|
||||
0x00000430, 0x00000301, 0x00000431, 0x00000301, 0x00000432, 0x00000301,
|
||||
0x00000433, 0x00000301, 0x00000434, 0x00000301, 0x00000435, 0x00000301,
|
||||
0x00000436, 0x00000301, 0x00000437, 0x00000301, 0x00000438, 0x00000301,
|
||||
0x00000439, 0x00000301, 0x0000043A, 0x00000301, 0x0000043B, 0x00000301,
|
||||
0x0000043C, 0x00000301, 0x0000043D, 0x00000301, 0x0000043E, 0x00000301,
|
||||
0x0000043F, 0x00000301, 0x00000440, 0x00000301, 0x00000441, 0x00000301,
|
||||
0x00000442, 0x00000301, 0x00000443, 0x00000301, 0x00000444, 0x00000301,
|
||||
0x00000445, 0x00000301, 0x00000446, 0x00000301, 0x00000447, 0x00000301,
|
||||
0x00000448, 0x00000301, 0x00000449, 0x00000301, 0x0000044A, 0x00000301,
|
||||
0x0000044B, 0x00000301, 0x0000044C, 0x00000301, 0x0000044D, 0x00000301,
|
||||
0x0000044E, 0x00000301, 0x0000044F, 0x00000301, 0x00000450, 0x00000301,
|
||||
0x00000451, 0x00000301, 0x00000452, 0x00000301, 0x00000453, 0x00000301,
|
||||
0x00000454, 0x00000301, 0x00000455, 0x00000301, 0x00000456, 0x00000301,
|
||||
0x00000457, 0x00000301, 0x00000458, 0x00000301, 0x00000459, 0x00000301,
|
||||
0x0000045A, 0x00000301, 0x0000045B, 0x00000301, 0x0000045C, 0x00000301,
|
||||
0x0000045D, 0x00000301, 0x0000045E, 0x00000301, 0x0000045F, 0x00000301,
|
||||
0x00000107, 0x00000000, 0x00000000, 0x00000106, 0x00000301, 0x00000108,
|
||||
0x00003C04,
|
||||
};
|
||||
const uint32_t pm_cfg_obj_words =
|
||||
(uint32_t)(sizeof(pm_cfg_obj) / sizeof(pm_cfg_obj[0]));
|
||||
797
hal/zynq.c
797
hal/zynq.c
File diff suppressed because it is too large
Load Diff
|
|
@ -204,6 +204,14 @@
|
|||
|
||||
#define GQSPI_FIFO_WORD_SZ 4
|
||||
#define QQSPI_DMA_ALIGN 64 /* L1 cache size */
|
||||
/* Maximum bytes per GQSPI DMA transfer. The controller does not reliably
|
||||
* complete a single very large (>=128KB) DMA read into DDR on ZynqMP -- the
|
||||
* large chunk lands as zeros -- so cap each transfer and let the flash stream
|
||||
* continuously across multiple RX gen-FIFO entries. 32KB is well within the
|
||||
* working range and keeps the one-time boot read fast. */
|
||||
#ifndef GQSPI_DMA_MAX_CHUNK
|
||||
#define GQSPI_DMA_MAX_CHUNK 0x8000U
|
||||
#endif
|
||||
#ifndef GQSPI_DMA_TMPSZ
|
||||
/* Use larger of WOLFBOOT_SHA_BLOCK_SIZE or IMAGE_HEADER_SIZE */
|
||||
#if defined(WOLFBOOT_SHA_BLOCK_SIZE) && \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
/* sleep.h
|
||||
*
|
||||
* Copyright (C) 2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* Minimal Xilinx-compatible <sleep.h> shim for the board-supplied
|
||||
* psu_init_gpl.c. Implemented in hal/zynqmp_psu_shim.c using the ARMv8
|
||||
* generic timer (CNTPCT/CNTFRQ), matching how the Xilinx FSBL provides
|
||||
* usleep()/sleep() during early init. */
|
||||
|
||||
#ifndef WOLFBOOT_ZYNQMP_SLEEP_H
|
||||
#define WOLFBOOT_ZYNQMP_SLEEP_H
|
||||
|
||||
void usleep(unsigned long useconds);
|
||||
unsigned int sleep(unsigned int seconds);
|
||||
|
||||
#endif /* WOLFBOOT_ZYNQMP_SLEEP_H */
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/* xil_io.h
|
||||
*
|
||||
* Copyright (C) 2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* Minimal Xilinx-compatible MMIO/types shim.
|
||||
*
|
||||
* This provides just enough of the Xilinx BSP <xil_io.h> surface for an
|
||||
* unmodified, board-generated psu_init_gpl.c to compile and link into
|
||||
* wolfBoot when it runs as the ZynqMP FSBL replacement (WOLFBOOT_ZYNQMP_FSBL).
|
||||
* The board's psu_init_gpl.c / psu_init_gpl.h are supplied at build time and
|
||||
* are NOT part of the wolfBoot tree (they are board/XSA specific and carry the
|
||||
* Xilinx copyright). See docs and config/examples/zynqmp_fsbl.config.
|
||||
*/
|
||||
|
||||
#ifndef WOLFBOOT_ZYNQMP_XIL_IO_H
|
||||
#define WOLFBOOT_ZYNQMP_XIL_IO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Xilinx fixed-width type aliases used by psu_init_gpl.c */
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
typedef uintptr_t UINTPTR;
|
||||
typedef intptr_t INTPTR;
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
static inline u32 Xil_In32(UINTPTR addr)
|
||||
{
|
||||
return *(volatile u32*)addr;
|
||||
}
|
||||
|
||||
static inline void Xil_Out32(UINTPTR addr, u32 value)
|
||||
{
|
||||
*(volatile u32*)addr = value;
|
||||
}
|
||||
|
||||
static inline u16 Xil_In16(UINTPTR addr)
|
||||
{
|
||||
return *(volatile u16*)addr;
|
||||
}
|
||||
|
||||
static inline void Xil_Out16(UINTPTR addr, u16 value)
|
||||
{
|
||||
*(volatile u16*)addr = value;
|
||||
}
|
||||
|
||||
static inline u8 Xil_In8(UINTPTR addr)
|
||||
{
|
||||
return *(volatile u8*)addr;
|
||||
}
|
||||
|
||||
/* Xilinx BSP logging used by a few psu_init error paths (e.g. SERDES cal
|
||||
* timeout). psu_init runs before uart_init(), so this is a no-op; see
|
||||
* hal/zynqmp_psu_shim.c. */
|
||||
int xil_printf(const char* ctrl1, ...);
|
||||
|
||||
static inline void Xil_Out8(UINTPTR addr, u8 value)
|
||||
{
|
||||
*(volatile u8*)addr = value;
|
||||
}
|
||||
|
||||
#endif /* WOLFBOOT_ZYNQMP_XIL_IO_H */
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/* zynqmp_atf.c
|
||||
*
|
||||
* Copyright (C) 2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* ARM Trusted Firmware (BL31) handoff for wolfBoot as the ZynqMP FSBL.
|
||||
*
|
||||
* Matches the Xilinx FSBL handoff format (embeddedsw zynqmp_fsbl): a block of
|
||||
* ASCII magic "XLNX", an entry count, and up to 8 entries {u64 entry_point,
|
||||
* u64 flags}. Its address is published in PMU_GLOBAL.GEN_STORAGE6, which stock
|
||||
* BL31 reads during early setup. wolfBoot writes a single entry for the BL33
|
||||
* image at its requested EL; BL31 owns starting BL33. */
|
||||
|
||||
#include "zynqmp_atf.h"
|
||||
|
||||
/* PMU_GLOBAL base 0xFFD80000. GEN_STORAGE6 (0x48) carries the handoff-block
|
||||
* address (read by stock BL31); GEN_STORAGE5 (0x44) carries the BL33 device
|
||||
* tree address (consumed by a small TF-A patch). Both are device memory. */
|
||||
#define PMU_GLOBAL_GLOB_GEN_STORAGE5 0xFFD80044UL
|
||||
#define PMU_GLOBAL_GLOB_GEN_STORAGE6 0xFFD80048UL
|
||||
|
||||
/* PartitionFlags: bit0 exec-AA32, bit1 big-endian, bit2 secure (all 0 for
|
||||
* non-secure AArch64 LE), bits[4:3] target EL, bits[6:5] A53 core (0). */
|
||||
#define ZYNQMP_ATF_FLAG_EL_SHIFT 3U
|
||||
#define ZYNQMP_ATF_FLAG_EL_MASK (3U << ZYNQMP_ATF_FLAG_EL_SHIFT)
|
||||
|
||||
#define ZYNQMP_ATF_MAX_ENTRIES 8
|
||||
|
||||
struct zynqmp_atf_entry {
|
||||
uint64_t entry_point;
|
||||
uint64_t flags;
|
||||
};
|
||||
|
||||
struct zynqmp_atf_handoff_params {
|
||||
char magic[4]; /* "XLNX" */
|
||||
uint32_t num_entries;
|
||||
struct zynqmp_atf_entry entry[ZYNQMP_ATF_MAX_ENTRIES];
|
||||
};
|
||||
|
||||
/* Lives in OCM (wolfBoot's .bss). BL31 is loaded to DDR, so this block is
|
||||
* untouched until BL31 reads it. */
|
||||
static struct zynqmp_atf_handoff_params atf_handoff;
|
||||
|
||||
/* Generic AArch64 EL3 exit (cache/MMU teardown + branch), defined in
|
||||
* src/boot_aarch64_start.S (built under WOLFBOOT_ZYNQMP_FSBL). */
|
||||
extern void el3_jump_to_entry(uintptr_t entry);
|
||||
extern void flush_dcache_range(uintptr_t start, uintptr_t end);
|
||||
|
||||
void zynqmp_atf_handoff(uintptr_t bl31_entry, uintptr_t bl33_entry,
|
||||
uintptr_t dts_addr, uint32_t bl33_el)
|
||||
{
|
||||
volatile uint32_t* storage5 =
|
||||
(volatile uint32_t*)PMU_GLOBAL_GLOB_GEN_STORAGE5;
|
||||
volatile uint32_t* storage6 =
|
||||
(volatile uint32_t*)PMU_GLOBAL_GLOB_GEN_STORAGE6;
|
||||
uint64_t flags;
|
||||
|
||||
/* Non-secure, AArch64, little-endian, A53-0, at the requested EL. */
|
||||
flags = ((uint64_t)(bl33_el << ZYNQMP_ATF_FLAG_EL_SHIFT))
|
||||
& ZYNQMP_ATF_FLAG_EL_MASK;
|
||||
|
||||
atf_handoff.magic[0] = 'X';
|
||||
atf_handoff.magic[1] = 'L';
|
||||
atf_handoff.magic[2] = 'N';
|
||||
atf_handoff.magic[3] = 'X';
|
||||
atf_handoff.num_entries = 1;
|
||||
atf_handoff.entry[0].entry_point = (uint64_t)bl33_entry;
|
||||
atf_handoff.entry[0].flags = flags;
|
||||
|
||||
/* Publish the parameter-block address where BL31 reads it, and the BL33
|
||||
* device tree address for the TF-A patch that forwards it to x0. */
|
||||
*storage6 = (uint32_t)(uintptr_t)&atf_handoff;
|
||||
*storage5 = (uint32_t)dts_addr;
|
||||
|
||||
/* Clean the parameter block to the PoC so BL31, entered with the MMU and
|
||||
* caches off, observes it. */
|
||||
flush_dcache_range((uintptr_t)&atf_handoff,
|
||||
(uintptr_t)&atf_handoff + sizeof(atf_handoff));
|
||||
|
||||
/* Tear down EL3 MMU/caches and branch to BL31. Does not return. */
|
||||
el3_jump_to_entry(bl31_entry);
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/* zynqmp_atf.h
|
||||
*
|
||||
* Copyright (C) 2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* ARM Trusted Firmware (BL31) handoff for wolfBoot running as the ZynqMP FSBL
|
||||
* replacement (Milestone 1). wolfBoot loads + verifies BL31 and the next
|
||||
* normal-world image (BL33: U-Boot or the Linux kernel) with its own keys,
|
||||
* then hands off to BL31 at EL3. BL31 stays resident as the EL3 monitor and
|
||||
* drops BL33 to the requested lower exception level. */
|
||||
|
||||
#ifndef ZYNQMP_ATF_H
|
||||
#define ZYNQMP_ATF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Target exception level for the BL33 (normal-world) image. */
|
||||
#define ZYNQMP_ATF_EL1 1U
|
||||
#define ZYNQMP_ATF_EL2 2U
|
||||
|
||||
/* Build the BL31 handoff parameters for the given normal-world (BL33) entry
|
||||
* point and exception level, publish the parameter-block address to the
|
||||
* PMU_GLOBAL scratch register that BL31 reads, and hand off to BL31 at EL3.
|
||||
* Does not return.
|
||||
*
|
||||
* bl31_entry: EL3 entry point of the loaded BL31 image (from its ELF entry).
|
||||
* bl33_entry: entry point of the next normal-world image BL31 will start.
|
||||
* dts_addr: device tree address for BL33. The standard ATF handoff block has
|
||||
* no argument fields and stock ZynqMP TF-A enters BL33 with x0=0,
|
||||
* so for a direct (no U-Boot) Linux BL33 wolfBoot also publishes
|
||||
* dts_addr in PMU_GLOBAL.GLOBAL_GEN_STORAGE5. A small TF-A patch
|
||||
* must read that register into the BL33 entrypoint x0; pass 0 when
|
||||
* BL33 finds its own DTB (e.g. U-Boot). See docs.
|
||||
* bl33_el: ZYNQMP_ATF_EL1 or ZYNQMP_ATF_EL2. */
|
||||
void zynqmp_atf_handoff(uintptr_t bl31_entry, uintptr_t bl33_entry,
|
||||
uintptr_t dts_addr, uint32_t bl33_el);
|
||||
|
||||
#endif /* ZYNQMP_ATF_H */
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/dts-v1/;
|
||||
|
||||
/*
|
||||
* Xilinx ZynqMP ZCU102 - wolfBoot-as-FSBL -> Linux boot FIT.
|
||||
*
|
||||
* wolfBoot (running as the FSBL replacement at EL3 in OCM) loads this FIT
|
||||
* from QSPI, verifies the outer wolfBoot RSA4096/SHA3 signature, then:
|
||||
* - gunzips the kernel to 0x00200000 (BL33 entry),
|
||||
* - loads BL31 to 0x70000000 (found by the literal node name "atf"),
|
||||
* - relocates the DTB to WOLFBOOT_LOAD_DTS_ADDRESS (0x11800000),
|
||||
* - publishes DTB addr in PMU_GLOBAL_GEN_STORAGE5 and the XLNX handoff
|
||||
* block addr in GEN_STORAGE6, and branches to BL31 at EL3.
|
||||
* BL31 (xlnx_rebase_v2.12 + tf-a-zynqmp-wolfboot-dtb.patch) hands the kernel
|
||||
* its DTB in x0 and enters BL33 at EL2.
|
||||
*
|
||||
* The incbin filenames are relative: copy the three artifacts into the
|
||||
* directory you run mkimage from (or next to this .its). Sources:
|
||||
* Image.gz, system.dtb - from the PetaLinux images/linux tree
|
||||
* bl31-ddr-0x70000000.bin - BL31 (TF-A) built per docs/Targets.md at link
|
||||
* base 0x70000000; it must equal the "atf" load/entry below.
|
||||
*
|
||||
* Build: mkimage -f zynqmp_fsbl.its fitImage
|
||||
* Sign: sign --rsa4096 --sha3 fitImage <key>.der 1
|
||||
*/
|
||||
|
||||
/ {
|
||||
description = "Xilinx ZynqMP ZCU102 - wolfBoot Linux (FSBL)";
|
||||
#address-cells = <1>;
|
||||
|
||||
images {
|
||||
kernel-1 {
|
||||
description = "Linux Kernel (gzip)";
|
||||
data = /incbin/("Image.gz");
|
||||
type = "kernel";
|
||||
arch = "arm64";
|
||||
os = "linux";
|
||||
compression = "gzip";
|
||||
load = <0x00200000>;
|
||||
entry = <0x00200000>;
|
||||
hash-1 {
|
||||
algo = "sha256";
|
||||
};
|
||||
};
|
||||
atf {
|
||||
description = "ARM Trusted Firmware BL31 (DDR-linked)";
|
||||
data = /incbin/("bl31-ddr-0x70000000.bin");
|
||||
type = "firmware";
|
||||
arch = "arm64";
|
||||
os = "arm-trusted-firmware";
|
||||
compression = "none";
|
||||
load = <0x70000000>;
|
||||
entry = <0x70000000>;
|
||||
hash-1 {
|
||||
algo = "sha256";
|
||||
};
|
||||
};
|
||||
fdt-1 {
|
||||
description = "Flattened Device Tree blob";
|
||||
data = /incbin/("system.dtb");
|
||||
type = "flat_dt";
|
||||
arch = "arm64";
|
||||
compression = "none";
|
||||
hash-1 {
|
||||
algo = "sha256";
|
||||
};
|
||||
};
|
||||
};
|
||||
configurations {
|
||||
default = "conf-1";
|
||||
conf-1 {
|
||||
description = "Linux kernel + FDT (BL31 loaded by literal name atf)";
|
||||
kernel = "kernel-1";
|
||||
fdt = "fdt-1";
|
||||
hash-1 {
|
||||
algo = "sha256";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
/* Linker Script for Zynq UltraScale+ (ZynqMP) - wolfBoot as FSBL replacement.
|
||||
*
|
||||
* The BootROM loads wolfBoot into the 256KB OCM at 0xFFFC0000 and enters at
|
||||
* EL3; wolfBoot runs psu_init() to bring up DDR, so it must link/run entirely
|
||||
* from OCM (DDR is unavailable at entry). Downstream image staging/verify
|
||||
* buffers live in DDR (WOLFBOOT_LOAD_ADDRESS) once psu_init completes. Raise
|
||||
* _HEAP_SIZE only if a config needs it (SPMATH keeps heap use small).
|
||||
*/
|
||||
|
||||
/* EL3 stack 64KB for RSA 4096-bit verify; heap kept small to fit OCM. */
|
||||
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x10000;
|
||||
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x8000;
|
||||
|
||||
_EL0_STACK_SIZE = DEFINED(_EL0_STACK_SIZE) ? _EL0_STACK_SIZE : 1024;
|
||||
_EL1_STACK_SIZE = DEFINED(_EL1_STACK_SIZE) ? _EL1_STACK_SIZE : 2048;
|
||||
_EL2_STACK_SIZE = DEFINED(_EL2_STACK_SIZE) ? _EL2_STACK_SIZE : 1024;
|
||||
|
||||
/* Define Memories in the system */
|
||||
MEMORY
|
||||
{
|
||||
/* wolfBoot links/runs from OCM (256KB) as the FSBL. */
|
||||
psu_ocm_ram_0_MEM_0 : ORIGIN = 0xFFFC0000, LENGTH = 0x40000
|
||||
psu_ddr_0_MEM_0 : ORIGIN = 0x0, LENGTH = 0x80000000
|
||||
psu_ddr_1_MEM_0 : ORIGIN = 0x800000000, LENGTH = 0x80000000
|
||||
psu_qspi_linear_0_MEM_0 : ORIGIN = 0xC0000000, LENGTH = 0x20000000
|
||||
}
|
||||
|
||||
/* Specify the default entry point to the program */
|
||||
ENTRY(_vector_table)
|
||||
|
||||
/* Define the sections, and where they are mapped in memory */
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
PROVIDE (_DDR_ADDRESS = 0x80001000);
|
||||
PROVIDE (_OCRAM_ADDRESS = ORIGIN(psu_ocm_ram_0_MEM_0));
|
||||
PROVIDE (_MEMORY_SIZE = LENGTH(psu_ocm_ram_0_MEM_0));
|
||||
|
||||
.text : {
|
||||
KEEP (*(.vectors))
|
||||
*(.boot)
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
*(.plt)
|
||||
*(.gnu_warning)
|
||||
*(.gcc_except_table)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.ARM.extab)
|
||||
*(.gnu.linkonce.armextab.*)
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.init (ALIGN(64)) : {
|
||||
KEEP (*(.init))
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.fini (ALIGN(64)) : {
|
||||
KEEP (*(.fini))
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.interp : {
|
||||
KEEP (*(.interp))
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.note-ABI-tag : {
|
||||
KEEP (*(.note-ABI-tag))
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(64);
|
||||
__rodata_start = .;
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
__rodata_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.rodata1 : {
|
||||
. = ALIGN(64);
|
||||
__rodata1_start = .;
|
||||
*(.rodata1)
|
||||
*(.rodata1.*)
|
||||
__rodata1_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.sdata2 : {
|
||||
. = ALIGN(64);
|
||||
__sdata2_start = .;
|
||||
*(.sdata2)
|
||||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s2.*)
|
||||
__sdata2_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.sbss2 : {
|
||||
. = ALIGN(64);
|
||||
__sbss2_start = .;
|
||||
*(.sbss2)
|
||||
*(.sbss2.*)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
__sbss2_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.data : {
|
||||
. = ALIGN(64);
|
||||
__data_start = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.jcr)
|
||||
*(.got)
|
||||
*(.got.plt)
|
||||
__data_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.data1 : {
|
||||
. = ALIGN(64);
|
||||
__data1_start = .;
|
||||
*(.data1)
|
||||
*(.data1.*)
|
||||
__data1_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.got : {
|
||||
*(.got)
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.got1 : {
|
||||
*(.got1)
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.got2 : {
|
||||
*(.got2)
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.note.gnu.build-id : {
|
||||
KEEP (*(.note.gnu.build-id))
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.ctors : {
|
||||
. = ALIGN(64);
|
||||
__CTOR_LIST__ = .;
|
||||
___CTORS_LIST___ = .;
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
__CTOR_END__ = .;
|
||||
___CTORS_END___ = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.dtors : {
|
||||
. = ALIGN(64);
|
||||
__DTOR_LIST__ = .;
|
||||
___DTORS_LIST___ = .;
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
__DTOR_END__ = .;
|
||||
___DTORS_END___ = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.fixup : {
|
||||
__fixup_start = .;
|
||||
*(.fixup)
|
||||
__fixup_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.eh_frame : {
|
||||
*(.eh_frame)
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.eh_framehdr : {
|
||||
__eh_framehdr_start = .;
|
||||
*(.eh_framehdr)
|
||||
__eh_framehdr_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.gcc_except_table : {
|
||||
*(.gcc_except_table)
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.mmu_tbl0 (ALIGN(4096)) : {
|
||||
__mmu_tbl0_start = .;
|
||||
*(.mmu_tbl0)
|
||||
__mmu_tbl0_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.mmu_tbl1 (ALIGN(4096)) : {
|
||||
__mmu_tbl1_start = .;
|
||||
*(.mmu_tbl1)
|
||||
__mmu_tbl1_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.mmu_tbl2 (ALIGN(4096)) : {
|
||||
__mmu_tbl2_start = .;
|
||||
*(.mmu_tbl2)
|
||||
__mmu_tbl2_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.ARM.exidx : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
*(.gnu.linkonce.armexidx.*.*)
|
||||
__exidx_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.preinit_array : {
|
||||
. = ALIGN(64);
|
||||
__preinit_array_start = .;
|
||||
KEEP (*(SORT(.preinit_array.*)))
|
||||
KEEP (*(.preinit_array))
|
||||
__preinit_array_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.init_array : {
|
||||
. = ALIGN(64);
|
||||
__init_array_start = .;
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
__init_array_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.fini_array : {
|
||||
. = ALIGN(64);
|
||||
__fini_array_start = .;
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array))
|
||||
__fini_array_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.ARM.attributes : {
|
||||
__ARM.attributes_start = .;
|
||||
*(.ARM.attributes)
|
||||
__ARM.attributes_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.sdata : {
|
||||
. = ALIGN(64);
|
||||
__sdata_start = .;
|
||||
*(.sdata)
|
||||
*(.sdata.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
__sdata_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.tdata : {
|
||||
. = ALIGN(64);
|
||||
__tdata_start = .;
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
*(.gnu.linkonce.td.*)
|
||||
__tdata_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.tbss : {
|
||||
. = ALIGN(64);
|
||||
__tbss_start = .;
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
*(.gnu.linkonce.tb.*)
|
||||
__tbss_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
|
||||
.sbss (NOLOAD) : {
|
||||
. = ALIGN(64);
|
||||
__sbss_start = .;
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
. = ALIGN(64);
|
||||
__sbss_end = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(64);
|
||||
__bss_start__ = .;
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(64);
|
||||
__bss_end__ = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
|
||||
|
||||
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
|
||||
|
||||
/* Generate Stack and Heap definitions */
|
||||
|
||||
.heap (NOLOAD) : {
|
||||
. = ALIGN(64);
|
||||
_heap = .;
|
||||
HeapBase = .;
|
||||
_heap_start = .;
|
||||
. += _HEAP_SIZE;
|
||||
_heap_end = .;
|
||||
HeapLimit = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(64);
|
||||
_el3_stack_end = .;
|
||||
. += _STACK_SIZE;
|
||||
__el3_stack = .;
|
||||
_el2_stack_end = .;
|
||||
. += _EL2_STACK_SIZE;
|
||||
. = ALIGN(64);
|
||||
__el2_stack = .;
|
||||
_el1_stack_end = .;
|
||||
. += _EL1_STACK_SIZE;
|
||||
. = ALIGN(64);
|
||||
__el1_stack = .;
|
||||
_el0_stack_end = .;
|
||||
. += _EL0_STACK_SIZE;
|
||||
. = ALIGN(64);
|
||||
__el0_stack = .;
|
||||
} > psu_ocm_ram_0_MEM_0
|
||||
|
||||
PROVIDE(_stack_base = .);
|
||||
|
||||
_end = .;
|
||||
}
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
/* zynqmp_psu_shim.c
|
||||
*
|
||||
* Copyright (C) 2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* Delay primitives backing the <sleep.h> shim used by the board-supplied
|
||||
* psu_init_gpl.c when wolfBoot runs as the ZynqMP FSBL replacement.
|
||||
*
|
||||
* These use the ARMv8 generic timer (CNTPCT_EL0 / CNTFRQ_EL0). The EL3 startup
|
||||
* (boot_aarch64_start.S) programs CNTFRQ_EL0 and the system counter runs from
|
||||
* the PS reference clock out of reset, so these are usable from the very first
|
||||
* psu_init() stage, before the PLLs/clocks have been reprogrammed. */
|
||||
|
||||
#include "sleep.h"
|
||||
#include "xil_io.h"
|
||||
|
||||
/* Xilinx BSP logging shim. The board psu_init_gpl.c calls xil_printf() on a few
|
||||
* error paths; psu_init runs before the UART is initialized, so this is a
|
||||
* no-op. Route to wolfBoot_printf() once the UART is up if you need the
|
||||
* diagnostics. */
|
||||
int xil_printf(const char* ctrl1, ...)
|
||||
{
|
||||
(void)ctrl1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ZynqMP IOU system timestamp counter (drives CNTPCT_EL0) and its fixed
|
||||
* timestamp clock frequency (XPAR_CPU_CORTEXA53_0_TIMESTAMP_CLK_FREQ = 100MHz,
|
||||
* set by psu_clock_init_data's TIMESTAMP_REF_CTRL). */
|
||||
#define IOU_SCNTRS_CNT_CONTROL 0xFF260000UL
|
||||
#define IOU_SCNTRS_FREQ_REG 0xFF260020UL
|
||||
#define IOU_SCNTRS_EN 0x00000001UL
|
||||
#ifndef ZYNQMP_TIMESTAMP_HZ
|
||||
#define ZYNQMP_TIMESTAMP_HZ 100000000UL
|
||||
#endif
|
||||
|
||||
/* Equivalent of the Xilinx BSP XTime_StartTimer(): enable the IOU_SCNTRS
|
||||
* system counter (so CNTPCT_EL0 increments) if it is not already enabled. The
|
||||
* BootROM/early psu_init leaves it DISABLED -- the generated psu_init only
|
||||
* enables it in psu_peripherals_init_data, which runs AFTER DDR training --
|
||||
* yet psu_ddr_phybringup_data needs working usleep delays. The Xilinx BSP
|
||||
* usleep calls this on every delay for exactly this reason. */
|
||||
static void zynqmp_start_timer(void)
|
||||
{
|
||||
if ((Xil_In32(IOU_SCNTRS_CNT_CONTROL) & IOU_SCNTRS_EN) != IOU_SCNTRS_EN) {
|
||||
Xil_Out32(IOU_SCNTRS_FREQ_REG, ZYNQMP_TIMESTAMP_HZ);
|
||||
Xil_Out32(IOU_SCNTRS_CNT_CONTROL, IOU_SCNTRS_EN);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long psu_timer_count(void)
|
||||
{
|
||||
unsigned long cntpct;
|
||||
asm volatile("mrs %0, cntpct_el0" : "=r"(cntpct));
|
||||
return cntpct;
|
||||
}
|
||||
|
||||
void usleep(unsigned long useconds)
|
||||
{
|
||||
unsigned long start;
|
||||
unsigned long ticks;
|
||||
unsigned long freq = ZYNQMP_TIMESTAMP_HZ;
|
||||
|
||||
#ifdef WOLFBOOT_ZYNQMP_FSBL
|
||||
/* Match the Xilinx BSP: ensure the system counter is running before use. */
|
||||
zynqmp_start_timer();
|
||||
#endif
|
||||
#ifdef ZYNQMP_USLEEP_SCALE
|
||||
useconds *= (unsigned long)(ZYNQMP_USLEEP_SCALE);
|
||||
#endif
|
||||
start = psu_timer_count();
|
||||
/* ticks = useconds * freq / 1e6, split to avoid 64-bit overflow for the
|
||||
* small delays psu_init uses. */
|
||||
ticks = (useconds / 1000000UL) * freq
|
||||
+ ((useconds % 1000000UL) * freq) / 1000000UL;
|
||||
|
||||
while ((psu_timer_count() - start) < ticks) {
|
||||
/* busy-wait */
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int sleep(unsigned int seconds)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < seconds; i++) {
|
||||
usleep(1000000UL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef WOLFBOOT_ZYNQMP_FSBL
|
||||
|
||||
/* Board psu_init sub-stages (non-static in the XSA-generated psu_init_gpl.c).
|
||||
* Declared here so we do not pull in the huge board psu_init_gpl.h. */
|
||||
extern unsigned long psu_mio_init_data(void);
|
||||
extern unsigned long psu_peripherals_pre_init_data(void);
|
||||
extern unsigned long psu_pll_init_data(void);
|
||||
extern unsigned long psu_clock_init_data(void);
|
||||
extern unsigned long psu_ddr_init_data(void);
|
||||
extern unsigned long psu_ddr_phybringup_data(void);
|
||||
extern unsigned long psu_peripherals_init_data(void);
|
||||
extern unsigned long psu_resetin_init_data(void);
|
||||
extern unsigned long psu_serdes_init_data(void);
|
||||
extern unsigned long psu_resetout_init_data(void);
|
||||
/* serdes signal-integrity calibration. serdes_fixcal_code() is non-static in
|
||||
* the board file; serdes_enb_coarse_saturation() is static (inlined below). */
|
||||
extern int serdes_fixcal_code(void);
|
||||
extern unsigned long psu_peripherals_powerdwn_data(void);
|
||||
extern unsigned long psu_afi_config(void);
|
||||
extern unsigned long psu_ddr_qos_init_data(void);
|
||||
|
||||
/* Diagnostic capture of the early (BootROM) timer/clock state, printed by
|
||||
* hal_init() once the UART is up. Only CRL_APB registers are readable this
|
||||
* early (the IOU_SCNTRS block is not yet clocked). */
|
||||
unsigned long zynqmp_dbg_cntfrq_boot = 0; /* CNTFRQ_EL0 left by the BootROM */
|
||||
unsigned long zynqmp_dbg_cntfrq_used = 0; /* value usleep() ends up using */
|
||||
unsigned int zynqmp_dbg_ts_ctrl = 0; /* CRL TIMESTAMP_REF_CTRL 0xFF5E0128 */
|
||||
unsigned int zynqmp_dbg_iopll_ctrl = 0; /* CRL IOPLL_CTRL 0xFF5E0020 */
|
||||
|
||||
static void zynqmp_fix_timer_freq(void)
|
||||
{
|
||||
unsigned long f = 0;
|
||||
|
||||
/* Capture the BootROM timer/clock state BEFORE psu_init reprograms it.
|
||||
* Only CRL_APB registers (always clocked) are safe to read this early;
|
||||
* the IOU_SCNTRS block (0xFF260000) is NOT accessible until psu_init
|
||||
* clocks it and reading it here bus-stalls the core. */
|
||||
__asm__ volatile("mrs %0, cntfrq_el0" : "=r"(f));
|
||||
zynqmp_dbg_cntfrq_boot = f;
|
||||
zynqmp_dbg_ts_ctrl = Xil_In32(0xFF5E0128UL);
|
||||
zynqmp_dbg_iopll_ctrl = Xil_In32(0xFF5E0020UL);
|
||||
|
||||
/* The IOU_SCNTRS system counter runs at the fixed 100MHz timestamp clock
|
||||
* (set by psu_clock_init_data) once enabled. usleep() enables it and uses
|
||||
* 100MHz directly; set CNTFRQ_EL0 to match so hal_get_timer_us()/
|
||||
* hal_timer_ms() are also correct. */
|
||||
f = ZYNQMP_TIMESTAMP_HZ;
|
||||
__asm__ volatile("msr cntfrq_el0, %0\n\t isb" : : "r"(f));
|
||||
zynqmp_dbg_cntfrq_used = f;
|
||||
}
|
||||
|
||||
/* Replacement for the board file's psu_init(): mirrors its call sequence (see
|
||||
* psu_init_gpl.c) but calls zynqmp_fix_timer_freq() right after clock init so
|
||||
* DDR training settle delays are accurate. The static init_serdes()/
|
||||
* init_peripheral() are inlined via their non-static sub-stages. Returns 0 on
|
||||
* success.
|
||||
*
|
||||
* PSU_STAGE(n) writes a progress marker to PMU_GLOBAL GEN_STORAGE0
|
||||
* (0xFFD80030) before each sub-stage; after a cold-boot hang, read it over
|
||||
* JTAG to see the last sub-stage that STARTED. */
|
||||
#define ZYNQMP_PSU_STAGE_REG 0xFFD80030UL
|
||||
#define PSU_STAGE(n) Xil_Out32(ZYNQMP_PSU_STAGE_REG, (u32)(n))
|
||||
|
||||
int zynqmp_psu_init(void)
|
||||
{
|
||||
int status = 1;
|
||||
u32 smmu;
|
||||
|
||||
PSU_STAGE(0xA000);
|
||||
/* Correct CNTFRQ_EL0 up front so EVERY psu_init stage (PLL/clock/DDR) gets
|
||||
* accurate usleep settle delays. The BootROM leaves the system counter at
|
||||
* the ~1.5 GHz pre-divider rate for the whole psu_init sequence (the /15
|
||||
* divisor never re-latches the running counter), so this single correction
|
||||
* holds across all stages. */
|
||||
zynqmp_fix_timer_freq();
|
||||
|
||||
PSU_STAGE(0xA001); status &= psu_mio_init_data();
|
||||
PSU_STAGE(0xA002); status &= psu_peripherals_pre_init_data();
|
||||
PSU_STAGE(0xA003); status &= psu_pll_init_data();
|
||||
PSU_STAGE(0xA004); status &= psu_clock_init_data();
|
||||
|
||||
PSU_STAGE(0xA005); status &= psu_ddr_init_data();
|
||||
PSU_STAGE(0xA006); status &= psu_ddr_phybringup_data();
|
||||
PSU_STAGE(0xA007);
|
||||
|
||||
PSU_STAGE(0xA008); status &= psu_peripherals_init_data();
|
||||
|
||||
/* init_serdes() equivalent: PS-GTR (USB/SATA/PCIe/DP) lane bringup. Not
|
||||
* needed to boot QSPI/SD -> DDR -> Linux (Linux re-inits the PS-GTR), so
|
||||
* skipped by default; define ZYNQMP_PSU_INIT_SERDES to run it -- required
|
||||
* if the kernel drives a PS-GTR peripheral (e.g. USB3 dwc3, whose probe
|
||||
* else hangs on an unclocked PHY). Replicates the board init_serdes() order:
|
||||
* the calibration helpers must run first or the SERDES PLLs never lock.
|
||||
* serdes_fixcal_code() is non-static (called); serdes_enb_coarse_saturation()
|
||||
* is static so its four writes are inlined; serdes_illcalib() runs inside
|
||||
* psu_serdes_init_data(). */
|
||||
#ifdef ZYNQMP_PSU_INIT_SERDES
|
||||
PSU_STAGE(0xA009);
|
||||
status &= psu_resetin_init_data();
|
||||
PSU_STAGE(0xB000);
|
||||
status &= serdes_fixcal_code();
|
||||
/* serdes_enb_coarse_saturation(): enable PLL coarse-code saturation logic */
|
||||
Xil_Out32(0xFD402094UL, 0x00000010UL);
|
||||
Xil_Out32(0xFD406094UL, 0x00000010UL);
|
||||
Xil_Out32(0xFD40A094UL, 0x00000010UL);
|
||||
Xil_Out32(0xFD40E094UL, 0x00000010UL);
|
||||
PSU_STAGE(0xB001); status &= psu_serdes_init_data();
|
||||
PSU_STAGE(0xB002); status &= psu_resetout_init_data();
|
||||
PSU_STAGE(0xB003);
|
||||
#endif
|
||||
|
||||
/* init_peripheral(): SMMU interrupt enable (read-modify-write set of
|
||||
* bits 0x8000001F, matching the board PSU_Mask_Write). This is NOT part of
|
||||
* init_serdes(), so it runs even when the PS-GTR serdes block is skipped. */
|
||||
smmu = Xil_In32(0xFD5F0018UL);
|
||||
smmu &= ~0x8000001FUL;
|
||||
smmu |= 0x8000001FUL;
|
||||
Xil_Out32(0xFD5F0018UL, smmu);
|
||||
PSU_STAGE(0xB004);
|
||||
|
||||
PSU_STAGE(0xA00A);
|
||||
status &= psu_peripherals_powerdwn_data();
|
||||
status &= psu_afi_config();
|
||||
psu_ddr_qos_init_data();
|
||||
|
||||
PSU_STAGE(0xA0DD); /* psu_init fully complete */
|
||||
if (status == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* WOLFBOOT_ZYNQMP_FSBL */
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/* wolfboot_smc.h
|
||||
*
|
||||
* Copyright (C) 2025 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA
|
||||
*/
|
||||
|
||||
/* SMC ABI between the wolfBoot EL3 secure monitor (server) and the normal
|
||||
* world (client). This is the shared contract used by both sides:
|
||||
*
|
||||
* - The crypto / key / secure-storage surface is carried by the wolfHSM
|
||||
* client/server protocol over a shared-memory transport (wh_transport_mem).
|
||||
* SMC is used only as a doorbell: the client writes a request into the
|
||||
* shared buffer, issues WOLFBOOT_SMC_FID_HSM_DOORBELL, and the EL3 server
|
||||
* processes exactly one wolfHSM request and writes the response back.
|
||||
* - A few small fixed-ABI calls (firmware update trigger/status, version)
|
||||
* do not need the wolfHSM channel.
|
||||
*
|
||||
* Function IDs use the SMCCC Fast Call, SMC64, OEM range (0xC3000000+) to
|
||||
* avoid the Xilinx SiP range (0xC2000000, used by PM_SIP_SVC in hal/zynq.c).
|
||||
*/
|
||||
|
||||
#ifndef WOLFBOOT_SMC_H
|
||||
#define WOLFBOOT_SMC_H
|
||||
|
||||
/* SMCCC Fast Call / SMC64 / OEM service range base. */
|
||||
#define WOLFBOOT_SMC_FID_BASE 0xC3000000U
|
||||
|
||||
/* Doorbell: a wolfHSM request is ready in the shared buffer. The server runs
|
||||
* one wh_Server_HandleRequestMessage() pass. Returns WOLFBOOT_SMC_OK on a
|
||||
* processed request, WOLFBOOT_SMC_NOT_READY if none was pending. */
|
||||
#define WOLFBOOT_SMC_FID_HSM_DOORBELL (WOLFBOOT_SMC_FID_BASE + 0x00U)
|
||||
|
||||
/* Firmware update / boot control (fixed ABI, no wolfHSM channel). */
|
||||
#define WOLFBOOT_SMC_FID_FW_UPDATE (WOLFBOOT_SMC_FID_BASE + 0x01U)
|
||||
#define WOLFBOOT_SMC_FID_FW_STATUS (WOLFBOOT_SMC_FID_BASE + 0x02U)
|
||||
#define WOLFBOOT_SMC_FID_VERSION (WOLFBOOT_SMC_FID_BASE + 0x03U)
|
||||
|
||||
/* SMC return codes (in x0). Aligned with SMCCC conventions: 0 success,
|
||||
* negative for errors. */
|
||||
#define WOLFBOOT_SMC_OK (0)
|
||||
#define WOLFBOOT_SMC_NOT_READY (-1)
|
||||
#define WOLFBOOT_SMC_NOT_SUPPORTED (-2)
|
||||
#define WOLFBOOT_SMC_ERROR (-3)
|
||||
|
||||
/* Shared-memory region for the wolfHSM transport (wh_transport_mem). Carved
|
||||
* out of DDR and mapped on both sides; must be reserved from the OS via the
|
||||
* device tree (/reserved-memory) so Linux does not use it. The region holds
|
||||
* the request buffer followed by the response buffer.
|
||||
*
|
||||
* Override at build time with -DWOLFBOOT_HSM_SHM_BASE / _SIZE to match the
|
||||
* DTB reservation and the wolfHSM whTransportMemConfig on both ends. */
|
||||
#ifndef WOLFBOOT_HSM_SHM_BASE
|
||||
#define WOLFBOOT_HSM_SHM_BASE 0x7F000000UL
|
||||
#endif
|
||||
#ifndef WOLFBOOT_HSM_SHM_SIZE
|
||||
#define WOLFBOOT_HSM_SHM_SIZE 0x00010000UL /* 64 KB total */
|
||||
#endif
|
||||
|
||||
/* Split the region into request and response halves for wh_transport_mem. */
|
||||
#define WOLFBOOT_HSM_SHM_REQ_BASE (WOLFBOOT_HSM_SHM_BASE)
|
||||
#define WOLFBOOT_HSM_SHM_REQ_SIZE (WOLFBOOT_HSM_SHM_SIZE / 2U)
|
||||
#define WOLFBOOT_HSM_SHM_RESP_BASE (WOLFBOOT_HSM_SHM_BASE + \
|
||||
WOLFBOOT_HSM_SHM_REQ_SIZE)
|
||||
#define WOLFBOOT_HSM_SHM_RESP_SIZE (WOLFBOOT_HSM_SHM_SIZE / 2U)
|
||||
|
||||
#endif /* WOLFBOOT_SMC_H */
|
||||
|
|
@ -242,7 +242,37 @@ void RAMFUNCTION arch_reboot(void)
|
|||
* ============================================================================
|
||||
*/
|
||||
|
||||
#if defined(DEBUG_HARDFAULT) && defined(DEBUG_UART) && defined(EL2_HYPERVISOR)
|
||||
#if defined(EL3_SECURE) && EL3_SECURE == 1 && defined(DEBUG_UART)
|
||||
|
||||
/* EL3 exception reporting. Without this, a data abort / SError taken at EL3
|
||||
* lands in the silent wfi stub below and looks like a hang. Print the syndrome
|
||||
* so the fault class (ESR_EL3.EC) and faulting address (FAR_EL3) are visible.
|
||||
* Gated on DEBUG_UART so release EL3 targets keep the minimal silent stub. */
|
||||
static void print_exception_info_el3(const char *type)
|
||||
{
|
||||
unsigned long esr = 0, elr = 0, far = 0;
|
||||
__asm__ volatile("mrs %0, ESR_EL3" : "=r"(esr));
|
||||
__asm__ volatile("mrs %0, ELR_EL3" : "=r"(elr));
|
||||
__asm__ volatile("mrs %0, FAR_EL3" : "=r"(far));
|
||||
wolfBoot_printf("\n*** %s EXCEPTION (EL3) ***\n", type);
|
||||
wolfBoot_printf("ESR_EL3: 0x%08x%08x\n",
|
||||
(uint32_t)(esr >> 32), (uint32_t)esr);
|
||||
wolfBoot_printf("ELR_EL3: 0x%08x%08x\n",
|
||||
(uint32_t)(elr >> 32), (uint32_t)elr);
|
||||
wolfBoot_printf("FAR_EL3: 0x%08x%08x\n",
|
||||
(uint32_t)(far >> 32), (uint32_t)far);
|
||||
}
|
||||
|
||||
void SynchronousInterrupt(void)
|
||||
{ print_exception_info_el3("SYNCHRONOUS"); while (1) { __asm__ volatile("wfi"); } }
|
||||
void IRQInterrupt(void)
|
||||
{ print_exception_info_el3("IRQ"); while (1) { __asm__ volatile("wfi"); } }
|
||||
void FIQInterrupt(void)
|
||||
{ print_exception_info_el3("FIQ"); while (1) { __asm__ volatile("wfi"); } }
|
||||
void SErrorInterrupt(void)
|
||||
{ print_exception_info_el3("SERROR"); while (1) { __asm__ volatile("wfi"); } }
|
||||
|
||||
#elif defined(DEBUG_HARDFAULT) && defined(DEBUG_UART) && defined(EL2_HYPERVISOR)
|
||||
|
||||
#define READ_SYSREG(_out, _reg) __asm__ volatile("mrs %0, " #_reg : "=r"(_out))
|
||||
|
||||
|
|
|
|||
|
|
@ -213,9 +213,15 @@ InitEL3:
|
|||
#endif
|
||||
msr S3_1_C15_C2_0, x0 /* CPUACTLR_EL1 */
|
||||
|
||||
/* Program the counter frequency */
|
||||
/* Program the counter frequency.
|
||||
* For the FSBL-replacement build, do NOT overwrite CNTFRQ_EL0: the BootROM
|
||||
* sets it to the actual system-counter rate, which our usleep() needs for
|
||||
* accurate psu_init DDR-training delays. Hardcoding 100MHz here is wrong
|
||||
* when the counter runs at the BootROM (undivided) rate. */
|
||||
#ifndef WOLFBOOT_ZYNQMP_FSBL
|
||||
ldr x0,=counterfreq
|
||||
msr CNTFRQ_EL0, x0
|
||||
#endif
|
||||
|
||||
/* Enable hardware coherency between cores */
|
||||
mrs x0, S3_1_c15_c2_1 /* Read EL1 CPU Extended Control Register */
|
||||
|
|
@ -557,7 +563,13 @@ invalidatecaches_end:
|
|||
*/
|
||||
|
||||
.set reserved, 0x0 /* Fault */
|
||||
#if defined(EL1_NONSECURE) && EL1_NONSECURE == 1
|
||||
#if defined(ZYNQMP_DDR_NC) && ZYNQMP_DDR_NC == 1
|
||||
.set Memory, 0x401 | (0 << 8) | (0x0) /* DDR as Normal NON-cacheable (MAIR idx0=0x44) - diag */
|
||||
#elif defined(ZYNQMP_DDR_DEVICE) && ZYNQMP_DDR_DEVICE == 1
|
||||
.set Memory, 0x409 | (1 << 53) | (1 << 54) /* DDR as Device-nGnRnE (no speculation) - diag */
|
||||
#elif defined(ZYNQMP_DDR_NONSHARED) && ZYNQMP_DDR_NONSHARED == 1
|
||||
.set Memory, 0x405 | (0 << 8) | (0x0) /* normal writeback write allocate NON-shareable read write */
|
||||
#elif defined(EL1_NONSECURE) && EL1_NONSECURE == 1
|
||||
.set Memory, 0x405 | (2 << 8) | (0x0) /* normal writeback write allocate outer shared read write */
|
||||
#else
|
||||
.set Memory, 0x405 | (3 << 8) | (0x0) /* normal writeback write allocate inner shared read write */
|
||||
|
|
@ -1212,6 +1224,74 @@ flush_dcache_range:
|
|||
ret
|
||||
|
||||
|
||||
/*
|
||||
* void zynqmp_dcache_disable(void)
|
||||
*
|
||||
* Clean+invalidate the entire data cache to PoC by set/way, then disable the
|
||||
* D-cache (SCTLR_EL3.C=0) and drop the core out of the coherency domain
|
||||
* (CPUECTLR_EL1.SMPEN=0). Used to TEST whether the QSPI body-load failure is
|
||||
* purely a coherency artifact: with no cache lines to allocate into and the
|
||||
* core out of the snoop domain, the coherent QSPIDMA writes must land in DDR.
|
||||
* Clobbers x0-x11. Modeled on the ARMv8 set/way routine used elsewhere here.
|
||||
*/
|
||||
.global zynqmp_dcache_disable
|
||||
zynqmp_dcache_disable:
|
||||
mrs x0, clidr_el1
|
||||
and x3, x0, #0x07000000
|
||||
lsr x3, x3, #23 /* x3 = LoC * 2 */
|
||||
cbz x3, .Lzdd_done
|
||||
mov x10, #0
|
||||
.Lzdd_level:
|
||||
add x2, x10, x10, lsr #1
|
||||
lsr x1, x0, x2
|
||||
and x1, x1, #7
|
||||
cmp x1, #2
|
||||
b.lt .Lzdd_skip
|
||||
msr csselr_el1, x10
|
||||
isb
|
||||
mrs x1, ccsidr_el1
|
||||
and x2, x1, #7
|
||||
add x2, x2, #4 /* x2 = log2(line len) */
|
||||
mov x4, #0x3ff
|
||||
and x4, x4, x1, lsr #3 /* x4 = max way */
|
||||
clz w5, w4
|
||||
mov x7, #0x7fff
|
||||
and x7, x7, x1, lsr #13 /* x7 = max set */
|
||||
.Lzdd_set:
|
||||
mov x9, x4
|
||||
.Lzdd_way:
|
||||
lsl x6, x9, x5
|
||||
orr x11, x10, x6
|
||||
lsl x6, x7, x2
|
||||
orr x11, x11, x6
|
||||
dc cisw, x11
|
||||
subs x9, x9, #1
|
||||
b.ge .Lzdd_way
|
||||
subs x7, x7, #1
|
||||
b.ge .Lzdd_set
|
||||
.Lzdd_skip:
|
||||
add x10, x10, #2
|
||||
cmp x3, x10
|
||||
b.gt .Lzdd_level
|
||||
.Lzdd_done:
|
||||
mov x10, #0
|
||||
msr csselr_el1, x10
|
||||
dsb sy
|
||||
isb
|
||||
/* disable D-cache (SCTLR_EL3.C, bit 2) */
|
||||
mrs x1, sctlr_el3
|
||||
bic x1, x1, #(1 << 2)
|
||||
msr sctlr_el3, x1
|
||||
isb
|
||||
/* leave the SMP/coherency domain (CPUECTLR_EL1.SMPEN, bit 6) */
|
||||
mrs x1, S3_1_C15_C2_1
|
||||
bic x1, x1, #(1 << 6)
|
||||
msr S3_1_C15_C2_1, x1
|
||||
dsb sy
|
||||
isb
|
||||
ret
|
||||
|
||||
|
||||
/* Initialize GIC 400 (GICv2) */
|
||||
.global gicv2_init_secure
|
||||
gicv2_init_secure:
|
||||
|
|
@ -1448,4 +1528,90 @@ el2_flush_and_disable_mmu:
|
|||
ret
|
||||
#endif /* EL2_HYPERVISOR */
|
||||
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL)
|
||||
/*
|
||||
* void el3_jump_to_entry(uintptr_t entry)
|
||||
*
|
||||
* Generic AArch64 EL3 exit primitive: cleans & invalidates the entire D-cache
|
||||
* to the PoC, invalidates the I-cache, disables the EL3 MMU + I/D-caches, then
|
||||
* branches to the given entry point at EL3 with the MMU off. Not ZynqMP-
|
||||
* specific -- reusable by any AArch64 EL3-FSBL target that hands off to a
|
||||
* next-stage image entered at EL3 (here BL31, mirroring the stock ZynqMP FSBL's
|
||||
* XFsbl_Exit()). Any handoff parameters are the caller's responsibility (the
|
||||
* ZynqMP caller publishes them via PMU_GLOBAL scratch in hal/zynqmp_atf.c
|
||||
* beforehand), so no GP-register arguments are used here.
|
||||
*
|
||||
* x0: entry. The set/way loop below clobbers x0-x11, so the entry point is
|
||||
* preserved in x19 (callee-saved, untouched by the loop). Does not return.
|
||||
*/
|
||||
.global el3_jump_to_entry
|
||||
el3_jump_to_entry:
|
||||
mov x19, x0 /* preserve entry point across the loop */
|
||||
|
||||
/* ---- 1. Clean & invalidate entire data cache to PoC by set/way ---- */
|
||||
mrs x0, clidr_el1
|
||||
and x3, x0, #0x07000000 /* x3 = LoC (level of coherency) */
|
||||
lsr x3, x3, #23 /* x3 = LoC * 2 */
|
||||
cbz x3, .Latf_dcache_done
|
||||
mov x10, #0 /* x10 = current cache level << 1 */
|
||||
|
||||
.Latf_dcache_level_loop:
|
||||
add x2, x10, x10, lsr #1 /* x2 = level * 3 */
|
||||
lsr x1, x0, x2 /* x1 = ctype field for this level */
|
||||
and x1, x1, #7
|
||||
cmp x1, #2
|
||||
b.lt .Latf_dcache_skip_level /* No data cache at this level */
|
||||
msr csselr_el1, x10 /* Select cache level (instruction = 0) */
|
||||
isb
|
||||
mrs x1, ccsidr_el1
|
||||
and x2, x1, #7 /* x2 = log2(line length) - 4 */
|
||||
add x2, x2, #4 /* x2 = log2(line length) */
|
||||
mov x4, #0x3ff
|
||||
and x4, x4, x1, lsr #3 /* x4 = max way number */
|
||||
clz w5, w4 /* x5 = bit position of way size */
|
||||
mov x7, #0x7fff
|
||||
and x7, x7, x1, lsr #13 /* x7 = max set number */
|
||||
|
||||
.Latf_dcache_set_loop:
|
||||
mov x9, x4 /* x9 = current way */
|
||||
.Latf_dcache_way_loop:
|
||||
lsl x6, x9, x5
|
||||
orr x11, x10, x6 /* level | way */
|
||||
lsl x6, x7, x2
|
||||
orr x11, x11, x6 /* level | way | set */
|
||||
dc cisw, x11 /* clean & invalidate by set/way */
|
||||
subs x9, x9, #1
|
||||
b.ge .Latf_dcache_way_loop
|
||||
subs x7, x7, #1
|
||||
b.ge .Latf_dcache_set_loop
|
||||
|
||||
.Latf_dcache_skip_level:
|
||||
add x10, x10, #2
|
||||
cmp x3, x10
|
||||
b.gt .Latf_dcache_level_loop
|
||||
|
||||
.Latf_dcache_done:
|
||||
mov x10, #0
|
||||
msr csselr_el1, x10
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
/* ---- 2. Invalidate entire I-cache to PoU ---- */
|
||||
ic iallu
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
/* ---- 3. Disable MMU + I-cache + D-cache at EL3 ---- */
|
||||
mrs x0, SCTLR_EL3
|
||||
bic x0, x0, #(1 << 0) /* M */
|
||||
bic x0, x0, #(1 << 2) /* C */
|
||||
bic x0, x0, #(1 << 12) /* I */
|
||||
msr SCTLR_EL3, x0
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
/* ---- 4. Branch to BL31 (does not return) ---- */
|
||||
br x19
|
||||
#endif /* WOLFBOOT_ZYNQMP_FSBL */
|
||||
|
||||
.end
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@
|
|||
#ifdef WOLFBOOT_ELF
|
||||
#include "elf.h"
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
#include "../hal/zynqmp_atf.h"
|
||||
#endif
|
||||
|
||||
/* Disk encryption support for AES-256, AES-128, or ChaCha20 */
|
||||
#if defined(ENCRYPT_WITH_AES256) || defined(ENCRYPT_WITH_AES128) || \
|
||||
|
|
@ -282,6 +285,10 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
#ifdef WOLFBOOT_FDT
|
||||
uint32_t dts_size = 0;
|
||||
#endif
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
/* BL31 (ARM-TF) entry, set when the boot FIT carries an "atf" sub-image. */
|
||||
uintptr_t bl31_entry = 0;
|
||||
#endif
|
||||
char part_name[4] = {'P', ':', 'X', '\0'};
|
||||
BENCHMARK_DECLARE();
|
||||
|
|
@ -566,6 +573,9 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
void* fit = (void*)load_address;
|
||||
const char *kernel = NULL, *flat_dt = NULL, *ramdisk = NULL;
|
||||
const char *fpga = NULL;
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
void *atf_load;
|
||||
#endif
|
||||
|
||||
wolfBoot_printf("Flattened uImage Tree: Version %d, Size %d\n",
|
||||
fdt_version(fit), fdt_totalsize(fit));
|
||||
|
|
@ -600,6 +610,14 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
}
|
||||
load_address = new_load;
|
||||
}
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
/* Load BL31 (ARM-TF) from the FIT "atf" sub-image, if present. */
|
||||
atf_load = fit_load_image(fit, "atf", NULL);
|
||||
if (atf_load != NULL) {
|
||||
bl31_entry = (uintptr_t)atf_load;
|
||||
wolfBoot_printf("FIT: BL31 (atf) loaded at %p\r\n", atf_load);
|
||||
}
|
||||
#endif
|
||||
if (flat_dt != NULL) {
|
||||
uint8_t *dts_ptr = fit_load_image(fit, flat_dt, (int*)&dts_size);
|
||||
if (dts_ptr != NULL && wolfBoot_get_dts_size(dts_ptr) >= 0) {
|
||||
|
|
@ -657,6 +675,14 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
#ifdef DISK_ENCRYPT
|
||||
disk_decrypted_header_clear(dec_hdr);
|
||||
disk_crypto_clear();
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
if (bl31_entry != 0) {
|
||||
wolfBoot_printf("Handing off to BL31 at %p (kernel %p)\r\n",
|
||||
(void*)bl31_entry, (void*)load_address);
|
||||
zynqmp_atf_handoff(bl31_entry, (uintptr_t)load_address,
|
||||
(uintptr_t)dts_addr, ZYNQMP_ATF_EL2);
|
||||
}
|
||||
#endif
|
||||
do_boot((uint32_t*)load_address
|
||||
#ifdef MMU
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@
|
|||
#ifdef WOLFBOOT_ELF
|
||||
#include "elf.h"
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
#include "../hal/zynqmp_atf.h"
|
||||
#endif
|
||||
|
||||
extern void hal_flash_dualbank_swap(void);
|
||||
extern int wolfBoot_get_dts_size(void *dts_addr);
|
||||
|
|
@ -52,6 +55,20 @@ extern uint32_t dts_load_addr;
|
|||
extern uint8_t _end[]; /* linker symbol: end of wolfBoot BSS */
|
||||
#endif
|
||||
|
||||
/* Return non-zero if the RAM load region [img_lo, img_hi) overlaps wolfBoot's
|
||||
* own region [wb_lo, wb_hi). wb_lo == 0 means the origin is unknown, so guard
|
||||
* only that the image loads above wolfBoot's end (wb_hi). Pure arithmetic,
|
||||
* exposed at file scope for unit testing (tools/unit-tests/unit-update-ram.c). */
|
||||
static inline int ramboot_region_overlap(uintptr_t img_lo, uintptr_t img_hi,
|
||||
uintptr_t wb_lo, uintptr_t wb_hi)
|
||||
{
|
||||
if (img_hi < img_lo)
|
||||
return 1; /* header+size wrapped the end address: reject conservatively */
|
||||
if (wb_lo == 0)
|
||||
return (img_lo < wb_hi);
|
||||
return (img_lo < wb_hi && img_hi > wb_lo);
|
||||
}
|
||||
|
||||
#if ((defined(EXT_FLASH) && defined(NO_XIP)) || \
|
||||
(defined(EXT_ENCRYPTED) && defined(MMU))) && \
|
||||
!defined(WOLFBOOT_NO_RAMBOOT)
|
||||
|
|
@ -114,12 +131,28 @@ int wolfBoot_ramboot(struct wolfBoot_image *img, uint8_t *src, uint8_t *dst)
|
|||
#endif
|
||||
|
||||
#if defined(__WOLFBOOT) && defined(WOLFBOOT_LOAD_ADDRESS)
|
||||
/* Runtime overlap check: ensure image destination does not overwrite
|
||||
* wolfBoot's own code/data/bss in RAM. */
|
||||
if ((uintptr_t)dst < (uintptr_t)_end) {
|
||||
wolfBoot_printf("Error: image dest %p overlaps wolfBoot end %p\n",
|
||||
dst, _end);
|
||||
return -1;
|
||||
/* Overlap check: the image destination must not overwrite wolfBoot's own
|
||||
* code/data/bss (ends at _end). The image occupies [dst, dst+header+size]. */
|
||||
{
|
||||
uintptr_t wb_hi = (uintptr_t)_end;
|
||||
uintptr_t img_lo = (uintptr_t)dst;
|
||||
uintptr_t img_hi = img_lo + (uintptr_t)IMAGE_HEADER_SIZE +
|
||||
(uintptr_t)img_size;
|
||||
#if defined(WOLFBOOT_ORIGIN)
|
||||
/* wolfBoot spans [WOLFBOOT_ORIGIN, _end]; range-intersect so it holds
|
||||
* whether wolfBoot is below or above the image -- e.g. ZynqMP FSBL runs
|
||||
* from high OCM while the image loads to low DDR, where the plain
|
||||
* "dst < _end" test gave a false positive. */
|
||||
uintptr_t wb_lo = (uintptr_t)(WOLFBOOT_ORIGIN);
|
||||
#else
|
||||
/* Without WOLFBOOT_ORIGIN, wb_lo=0 keeps the original low-addr guard. */
|
||||
uintptr_t wb_lo = 0;
|
||||
#endif
|
||||
if (ramboot_region_overlap(img_lo, img_hi, wb_lo, wb_hi)) {
|
||||
wolfBoot_printf("Error: image %p-%p overlaps wolfBoot %p-%p\n",
|
||||
(void*)img_lo, (void*)img_hi, (void*)wb_lo, (void*)wb_hi);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -250,6 +283,12 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
uint8_t *dts_addr = NULL;
|
||||
uint32_t dts_size = 0;
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
/* When wolfBoot is the FSBL, the boot FIT carries an "atf" (BL31)
|
||||
* sub-image. If present, hand off to BL31 instead of jumping to the
|
||||
* kernel directly. */
|
||||
uintptr_t bl31_entry = 0;
|
||||
#endif
|
||||
#if !defined(ALLOW_DOWNGRADE) && defined(WOLFBOOT_FIXED_PARTITIONS)
|
||||
uint32_t boot_v = wolfBoot_current_firmware_version();
|
||||
uint32_t update_v = wolfBoot_update_firmware_version();
|
||||
|
|
@ -503,6 +542,9 @@ backup_on_failure:
|
|||
void* fit = (void*)load_address;
|
||||
const char *kernel = NULL, *flat_dt = NULL, *ramdisk = NULL;
|
||||
const char *fpga = NULL;
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
void *atf_load;
|
||||
#endif
|
||||
|
||||
wolfBoot_printf("Flattened uImage Tree: Version %d, Size %d\n",
|
||||
fdt_version(fit), fdt_totalsize(fit));
|
||||
|
|
@ -528,6 +570,16 @@ backup_on_failure:
|
|||
}
|
||||
load_address = new_load;
|
||||
}
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
/* Load BL31 (ARM Trusted Firmware) to its DDR exec address. Its entry
|
||||
* point is the FIT `load`/`entry` address returned here. Optional: if
|
||||
* absent, fall through to the normal direct boot. */
|
||||
atf_load = fit_load_image(fit, "atf", NULL);
|
||||
if (atf_load != NULL) {
|
||||
bl31_entry = (uintptr_t)atf_load;
|
||||
wolfBoot_printf("FIT: BL31 (atf) loaded at %p\n", atf_load);
|
||||
}
|
||||
#endif
|
||||
if (flat_dt != NULL) {
|
||||
uint8_t *dts_ptr = fit_load_image(fit, flat_dt, (int*)&dts_size);
|
||||
if (dts_ptr != NULL && wolfBoot_get_dts_size(dts_ptr) >= 0) {
|
||||
|
|
@ -605,6 +657,17 @@ backup_on_failure:
|
|||
#ifndef WOLFBOOT_SKIP_BOOT_VERIFY
|
||||
PART_SANITY_CHECK(&os_image);
|
||||
#endif
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
if (bl31_entry != 0) {
|
||||
/* Hand off to BL31 (resident EL3 monitor). BL31 starts the kernel
|
||||
* (BL33) at EL2; the DTB is forwarded via PMU_GLOBAL scratch (see
|
||||
* hal/zynqmp_atf.c). Does not return. */
|
||||
wolfBoot_printf("Handing off to BL31 at %p (kernel %p)\n",
|
||||
(void*)bl31_entry, (void*)load_address);
|
||||
zynqmp_atf_handoff(bl31_entry, (uintptr_t)load_address,
|
||||
(uintptr_t)dts_addr, ZYNQMP_ATF_EL2);
|
||||
}
|
||||
#endif
|
||||
#ifdef MMU
|
||||
do_boot((uint32_t*)load_address,
|
||||
(uint32_t*)dts_addr);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
plat: xilinx: zynqmp: forward DTB to a direct-kernel BL33
|
||||
|
||||
When wolfBoot replaces the Xilinx FSBL and boots a Linux kernel directly as
|
||||
BL33 (no U-Boot), BL31 must hand the kernel its device tree in x0 (the arm64
|
||||
boot protocol requires the DTB pointer in x0). Stock ZynqMP TF-A leaves the
|
||||
BL33 entry arguments zeroed, which is fine for a U-Boot BL33 but not for a
|
||||
direct kernel.
|
||||
|
||||
wolfBoot publishes the DTB address in PMU_GLOBAL.GLOBAL_GEN_STORAGE5 (it already
|
||||
uses GLOBAL_GEN_STORAGE6 for the standard ATF handoff-parameter block). This
|
||||
patch reads GEN_STORAGE5 and, when non-zero, places it in BL33's arg0. When the
|
||||
register is zero (JTAG/default path, or a U-Boot BL33), behaviour is unchanged.
|
||||
|
||||
Apply against the Xilinx ARM Trusted Firmware tree:
|
||||
cd arm-trusted-firmware
|
||||
git apply /path/to/tf-a-zynqmp-wolfboot-dtb.patch
|
||||
make PLAT=zynqmp RESET_TO_BL31=1 bl31
|
||||
|
||||
Build BL31 with its link base in DDR (ZYNQMP_ATF_MEM_BASE in DDR), not the
|
||||
default OCM 0xFFFE0000, because wolfBoot occupies OCM as the FSBL.
|
||||
|
||||
diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
|
||||
index 65616e599..b9a4284e3 100644
|
||||
--- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
|
||||
+++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c
|
||||
@@ -143,6 +143,21 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1,
|
||||
panic();
|
||||
}
|
||||
}
|
||||
+
|
||||
+ /*
|
||||
+ * wolfBoot (as the FSBL) publishes the BL33 device tree address in
|
||||
+ * PMU_GLOBAL_GEN_STORAGE5. The arm64 boot protocol requires the DTB
|
||||
+ * pointer in x0, but stock TF-A leaves BL33's args zeroed (fine for a
|
||||
+ * U-Boot BL33, not for a direct kernel BL33). Forward it when set.
|
||||
+ */
|
||||
+ {
|
||||
+ uint64_t dtb_addr = (uint64_t)mmio_read_32(PMU_GLOBAL_GEN_STORAGE5);
|
||||
+
|
||||
+ if (dtb_addr != 0U) {
|
||||
+ bl33_image_ep_info.args.arg0 = dtb_addr;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (bl32_image_ep_info.pc != 0U) {
|
||||
NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc);
|
||||
}
|
||||
diff --git a/plat/xilinx/zynqmp/include/zynqmp_def.h b/plat/xilinx/zynqmp/include/zynqmp_def.h
|
||||
index cd3bbbc64..19156a5fb 100644
|
||||
--- a/plat/xilinx/zynqmp/include/zynqmp_def.h
|
||||
+++ b/plat/xilinx/zynqmp/include/zynqmp_def.h
|
||||
@@ -102,6 +102,7 @@
|
||||
#define PMU_GLOBAL_BASE U(0xFFD80000)
|
||||
#define PMU_GLOBAL_CNTRL (PMU_GLOBAL_BASE + 0)
|
||||
#define PMU_GLOBAL_GEN_STORAGE6 (PMU_GLOBAL_BASE + U(0x48))
|
||||
+#define PMU_GLOBAL_GEN_STORAGE5 (PMU_GLOBAL_BASE + U(0x44))
|
||||
#define PMU_GLOBAL_REQ_PWRUP_STATUS (PMU_GLOBAL_BASE + U(0x110))
|
||||
#define PMU_GLOBAL_REQ_PWRUP_EN (PMU_GLOBAL_BASE + U(0x118))
|
||||
#define PMU_GLOBAL_REQ_PWRUP_DIS (PMU_GLOBAL_BASE + U(0x11c))
|
||||
|
|
@ -127,13 +127,20 @@ if &programnow
|
|||
FLASHFILE.ReProgram off
|
||||
)
|
||||
|
||||
; Flash signed application image at partition offset
|
||||
DIALOG.YESNO "Flash signed application image now?"
|
||||
; Flash signed FIT image at the wolfBoot boot partition offset.
|
||||
; Offset must match WOLFBOOT_PARTITION_BOOT_ADDRESS in the active .config
|
||||
; (config/examples/zynqmp_fsbl.config = 0x800000). This is the combined
|
||||
; dual-parallel QSPI linear offset, same address space as BOOT.BIN@0x0.
|
||||
; The signed FIT is produced by:
|
||||
; mkimage -f hal/zynqmp_fsbl.its fitImage
|
||||
; ./tools/keytools/sign --rsa4096 --sha3 fitImage <key>.der 1
|
||||
; and must sit in the TRACE32 working directory (alongside BOOT.BIN).
|
||||
DIALOG.YESNO "Flash signed FIT image (fitImage_v1_signed.bin) now?"
|
||||
ENTRY &programnow
|
||||
if &programnow
|
||||
(
|
||||
FLASHFILE.ReProgram ALL
|
||||
FLASHFILE.Load "test-app/image_v1_signed.bin" 0x7000000
|
||||
FLASHFILE.Load "fitImage_v1_signed.bin" 0x800000
|
||||
FLASHFILE.ReProgram off
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
// Boot BIF for wolfBoot as the ZynqMP FSBL REPLACEMENT (ZCU102).
|
||||
//
|
||||
// wolfBoot is the [bootloader] partition: the BootROM loads it into OCM and
|
||||
// enters it at EL3. There is NO zynqmp_fsbl.elf. PMUFW is still loaded by the
|
||||
// BootROM via [pmufw_image]. wolfBoot itself runs psu_init() and then loads +
|
||||
// verifies the downstream images (BL31, kernel, DTB) from its own QSPI/SD
|
||||
// partitions using wolfBoot signing keys, so they are NOT listed here.
|
||||
//
|
||||
// The same image works for QSPI and SD boot; the boot device is selected by
|
||||
// the ZCU102 boot-mode pins (SW6). See README for deployment.
|
||||
//
|
||||
// Usage:
|
||||
// bootgen -arch zynqmp -image tools/scripts/zcu102/zynqmp_wolfboot_fsbl.bif \
|
||||
// -w -o BOOT.BIN
|
||||
//
|
||||
// Required files (copy next to this BIF, or use full paths):
|
||||
// wolfboot.elf - wolfBoot, linked at OCM 0xFFFC0000 (built ZYNQMP_FSBL=1)
|
||||
// pmufw.elf - Platform Management Unit firmware (from PetaLinux/Vitis)
|
||||
the_ROM_image:
|
||||
{
|
||||
[bootloader, destination_cpu=a53-0] wolfboot.elf
|
||||
[pmufw_image] pmufw.elf
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// Boot BIF for wolfBoot as the ZynqMP FSBL REPLACEMENT (ZCU102) with Xilinx
|
||||
// hardware root-of-trust (RSA authentication via eFuse PPK).
|
||||
//
|
||||
// Same layout as zynqmp_wolfboot_fsbl.bif but the BootROM authenticates
|
||||
// wolfBoot (and PMUFW) with RSA-4096/SHA3-384 before running them. This is the
|
||||
// hardware root of trust: the BootROM verifies wolfBoot against the eFuse PPK
|
||||
// hash; wolfBoot then verifies the downstream images with its OWN keys.
|
||||
//
|
||||
// Bring-up vs production:
|
||||
// - [fsbl_config] bh_auth_enable below makes the BootROM trust the PPK in the
|
||||
// boot header WITHOUT checking it against the eFuse PPK hash. Use this for
|
||||
// development only. Remove it for production and program the eFuse PPK0
|
||||
// hash + blow RSA_EN so the BootROM enforces the check.
|
||||
// - Generate the PPK eFuse hash: bootgen -arch zynqmp -efuseppkbits ...
|
||||
// - Program eFuses with the xilskey programming example.
|
||||
//
|
||||
// Usage:
|
||||
// bootgen -arch zynqmp \
|
||||
// -image tools/scripts/zcu102/zynqmp_wolfboot_fsbl_auth.bif \
|
||||
// -w -o BOOT.BIN
|
||||
//
|
||||
// Required files:
|
||||
// wolfboot.elf - wolfBoot linked at OCM 0xFFFC0000 (built ZYNQMP_FSBL=1)
|
||||
// pmufw.elf - Platform Management Unit firmware
|
||||
// pskf.pem - primary secret (private) key
|
||||
// sskf.pem - secondary secret (private) key
|
||||
the_ROM_image:
|
||||
{
|
||||
// Development only: trust the boot-header PPK without the eFuse check.
|
||||
// Remove for production (program the eFuse PPK0 hash + RSA_EN instead).
|
||||
[fsbl_config] bh_auth_enable
|
||||
|
||||
// Primary public key 0, secondary public key id 0
|
||||
[auth_params] ppk_select=0; spk_id=0x00000000
|
||||
|
||||
[pskfile] pskf.pem
|
||||
[sskfile] sskf.pem
|
||||
|
||||
[bootloader, destination_cpu=a53-0, authentication=rsa] wolfboot.elf
|
||||
[pmufw_image, authentication=rsa] pmufw.elf
|
||||
}
|
||||
|
|
@ -287,6 +287,36 @@ START_TEST (test_ramboot_success)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_ramboot_overlap_predicate)
|
||||
{
|
||||
/* wolfBoot occupies [0x1000, 0x2000) for these checks (the two-sided
|
||||
* range-intersection used by wolfBoot_ramboot's overlap guard). */
|
||||
const uintptr_t lo = 0x1000, hi = 0x2000;
|
||||
|
||||
/* No overlap: image entirely below, entirely above, or exactly adjacent. */
|
||||
ck_assert_int_eq(ramboot_region_overlap(0x0100, 0x0900, lo, hi), 0);
|
||||
ck_assert_int_eq(ramboot_region_overlap(0x3000, 0x4000, lo, hi), 0);
|
||||
ck_assert_int_eq(ramboot_region_overlap(0x0800, 0x1000, lo, hi), 0); /* img_hi==wb_lo */
|
||||
ck_assert_int_eq(ramboot_region_overlap(0x2000, 0x2800, lo, hi), 0); /* img_lo==wb_hi */
|
||||
|
||||
/* Overlap: straddle low edge, straddle high edge, image inside wolfBoot,
|
||||
* wolfBoot inside image. */
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x0800, 0x1800, lo, hi), 0);
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x1800, 0x2800, lo, hi), 0);
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x1400, 0x1C00, lo, hi), 0);
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x0800, 0x2800, lo, hi), 0);
|
||||
|
||||
/* Unknown-origin fallback (wb_lo == 0): guard only that img is below wb_hi. */
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x0100, 0x0900, 0, hi), 0);
|
||||
ck_assert_int_eq(ramboot_region_overlap(0x2000, 0x2800, 0, hi), 0);
|
||||
|
||||
/* img_hi overflow (header+size wrapped so img_hi < img_lo) is rejected
|
||||
* conservatively as an overlap, regardless of the wolfBoot range. */
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x0800, 0x0400, lo, hi), 0);
|
||||
ck_assert_int_ne(ramboot_region_overlap(0x3000, 0x0400, lo, hi), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
START_TEST (test_sunnyday_noupdate)
|
||||
{
|
||||
|
|
@ -545,6 +575,7 @@ Suite *wolfboot_suite(void)
|
|||
TCase *ramboot_invalid_header = tcase_create("Ramboot invalid header");
|
||||
TCase *ramboot_oversize = tcase_create("Ramboot oversize");
|
||||
TCase *ramboot_success = tcase_create("Ramboot success");
|
||||
TCase *ramboot_overlap = tcase_create("Ramboot overlap predicate");
|
||||
TCase *sunnyday_noupdate =
|
||||
tcase_create("Sunny day test with no update available");
|
||||
TCase *forward_update_samesize =
|
||||
|
|
@ -573,6 +604,7 @@ Suite *wolfboot_suite(void)
|
|||
tcase_add_test(ramboot_invalid_header, test_ramboot_invalid_header);
|
||||
tcase_add_test(ramboot_oversize, test_ramboot_oversize_rejected);
|
||||
tcase_add_test(ramboot_success, test_ramboot_success);
|
||||
tcase_add_test(ramboot_overlap, test_ramboot_overlap_predicate);
|
||||
tcase_add_test(sunnyday_noupdate, test_sunnyday_noupdate);
|
||||
tcase_add_test(forward_update_samesize, test_forward_update_samesize);
|
||||
tcase_add_test(forward_update_tolarger, test_forward_update_tolarger);
|
||||
|
|
@ -595,6 +627,7 @@ Suite *wolfboot_suite(void)
|
|||
suite_add_tcase(s, ramboot_invalid_header);
|
||||
suite_add_tcase(s, ramboot_oversize);
|
||||
suite_add_tcase(s, ramboot_success);
|
||||
suite_add_tcase(s, ramboot_overlap);
|
||||
suite_add_tcase(s, sunnyday_noupdate);
|
||||
suite_add_tcase(s, forward_update_samesize);
|
||||
suite_add_tcase(s, forward_update_tolarger);
|
||||
|
|
@ -616,6 +649,7 @@ Suite *wolfboot_suite(void)
|
|||
tcase_set_timeout(ramboot_invalid_header, 5);
|
||||
tcase_set_timeout(ramboot_oversize, 5);
|
||||
tcase_set_timeout(ramboot_success, 5);
|
||||
tcase_set_timeout(ramboot_overlap, 5);
|
||||
tcase_set_timeout(sunnyday_noupdate, 5);
|
||||
tcase_set_timeout(forward_update_samesize, 5);
|
||||
tcase_set_timeout(forward_update_tolarger, 5);
|
||||
|
|
|
|||
Loading…
Reference in New Issue