wolfBoot adds support for the Xilinx Zynq-7000 (ZC702)

We are pleased to announce that wolfBoot now ships an upstream port for the AMD/Xilinx Zynq-7000 SoC, verified end-to-end on the ZC702 Evaluation Kit (XC7Z020). The port covers QSPI cold-boot, SD-card cold-boot, JTAG-loaded development, and signed Linux/U-Boot payload chain-loading. All of this comes from a single TARGET=zynq7000 build target.

This rounds out wolfBoot’s Xilinx coverage. wolfBoot has supported the AArch64 Zynq UltraScale+ (ZynqMP) family and the Versal ACAP series for some time. With Zynq-7000 now in tree, the entire AMD/Xilinx adaptive-SoC lineup can be secured with a single, common wolfBoot codebase, from the 32-bit Cortex-A9 Zynq-7000 through ZynqMP to the latest Versal devices.

What’s in the port

wolfBoot is a standalone bare-metal bootloader with no external dependencies — no Xilinx Vitis BSP, no embeddedsw drivers, no FreeRTOS, no newlib runtime. Every driver below is written from scratch directly against the Zynq-7000 register map, so the bootloader is small, auditable end-to-end, and free of third-party licensing and supply-chain concerns. The full port is just a new HAL (hal/zynq7000.c, hal/zynq7000.h, hal/zynq7000.ld) plus a generalised ARMv7-A startup file, and it delivers:

  • UART console on the ZC702 USB-UART (115200 8N1) for boot logs — direct UART1 register driver, no library calls.
  • QSPI flash driver for the Zynq-7000 Linear/Static QSPI controller, with memory-mapped reads for fast image loading. Written from scratch against the silicon; validated against Micron N25Q128A on the ZC702.
  • SD-card boot via wolfBoot’s own SDHCI driver, adapted to the older Arasan v2.0 controller used on this SoC. No external SD-card stack.
  • Shared ARMv7-A startup. The existing SAMA5D3 (Cortex-A5) startup was generalised so Zynq-7000 (Cortex-A9) and SAMA5D3 now share a single, portable bare-metal startup file.
  • One ABI for both payload types. do_boot always emits the standard ARM Linux boot ABI on this target (r0=0, r1=~0, r2=DTB, r3=0). Bare-metal payloads simply ignore those registers, so a single wolfBoot image chain-loads a signed Linux zImage, a signed U-Boot, or a signed bare-metal payload with no per-config switch.
  • Hardened legacy uImage detection. The optional 64-byte U-Boot uImage header strip in update_ram.c now validates the header CRC32 in addition to the magic word, matching what U-Boot’s own mkimage and bootm do. False-positive probability for a non-uImage payload drops from ~2^-32 to ~2^-64.
  • Test app. A minimal Cortex-A9 banner + heartbeat at test-app/app_zynq7000.c so a successful chain-load is visible on UART.

Two boot media, any payload, one image

The port ships two example configurations, one for each storage option on the ZC702:

  • QSPI boot (config/examples/zynq7000.config) — the on-board QSPI flash, the typical choice for production.
  • SD-card boot (config/examples/zynq7000_sdcard.config) — a removable SD card, handy for development and field updates.

Whichever you pick, the build produces a single wolfBoot image that can verify and chain-load any signed payload you drop into the BOOT_A partition: a bare-metal application, a Linux kernel, a U-Boot, or any other ARM image. There is no per-deployment flag to flip and no separate build for “Linux vs. bare-metal” — the same image handles all of them. The extra Linux-friendly machinery (MMU, device-tree, ELF parsing) adds about 5 KB to the wolfBoot binary, which means one config can ship across product lines that have different payload mixes.

For SD-card boot, a helper script (tools/scripts/zynq7000/prepare_sdcard.sh) lays out the card automatically and refuses to touch system disks, so a typo cannot brick a developer’s workstation. For Linux boot, a second helper (tools/scripts/zynq7000/prepare_linux.sh) signs the kernel + device tree pair and stages them on either medium. The Linux chain was verified end-to-end on the ZC702 with a stock Xilinx 6.1.70 kernel: wolfBoot reads the signed kernel out of flash or SD, verifies the signature, hands off to the decompressor, and Linux brings both Cortex-A9 cores up to full SMP init.

Highlights for security-minded users

wolfBoot’s full algorithm menu is available on Zynq-7000. The default example config uses ECC256 + SHA256 for a good size/perf balance on Cortex-A9, but you can swap in any of the supported options at build time:

  • Classical signatures: ECDSA (P-256/P-384/P-521), RSA (2048/3072/4096), Ed25519, Ed448.
  • Post-quantum signatures: ML-DSA (FIPS 204, formerly Dilithium), LMS/HSS (NIST SP 800-208), and XMSS for stateful-hash-based signing.
  • Hybrid PQC: combine a classical signer with a PQC signer (for example ECDSA P-384 + ML-DSA) so an image is accepted only if both signatures verify. This gives you a smooth migration path. You stay protected by well-understood classical crypto today, while gaining quantum resistance, with no flag-day cutover.

Why a separate port from ZynqMP?

wolfBoot has supported the AArch64 ZynqMP family for some time, and reviewers occasionally ask why the Zynq-7000 needs its own HAL. The short answer: despite the shared name, the two SoCs differ in nearly every block wolfBoot interacts with:

Aspect ZynqMP (hal/zynq.c) Zynq-7000 (hal/zynq7000.c, new)
CPU Cortex-A53 quad, AArch64 Cortex-A9 dual, ARMv7-A 32-bit
QSPI controller GQSPI (XQspiPsu) Linear/Static QSPI (XQspiPs)
SDHCI Arasan v3.0 + Cadence shim Arasan v2.0 + Cadence shim
Boot chain FSBL + PMUFW + BL31 + wolfBoot FSBL + wolfBoot
Linux entry EL2 (hypervisor mode) SVC (no exception levels)
Crypto HW CSU (AES-GCM, SHA3, PUF) none (DevC AES only)
bootgen -arch zynqmp zynq

So while the chassis is similar, the controllers, register layouts, and security model are not interchangeable. Versal goes a step further again, with a different boot ROM, a PLM-based boot chain, and a new register map, which is why it has its own HAL in the existing wolfBoot tree.

Try it

The port is on the zynq7000 topic branch, ready for review. To bring up a board:

git clone https://github.com/wolfssl/wolfboot.git
cd wolfboot
git checkout zynq7000
git submodule update --init
git clone https://github.com/wolfSSL/soc-prebuilt-firmware.git
export PREBUILT_DIR=$(pwd)/soc-prebuilt-firmware/zc702-zynq

cp config/examples/zynq7000.config .config
make keytools && make TARGET=zynq7000

cp ${PREBUILT_DIR}/zynq_fsbl.elf .
source /opt/Xilinx/2025.2/Vitis/settings64.sh
bootgen -arch zynq -image tools/scripts/zc702/zc702_qspi.bif -w -o BOOT.BIN

# Sign the test app, program flash, set SW16-4 ON, power-cycle.
make test-app/image.bin
IMAGE_HEADER_SIZE=1024 ./tools/keytools/sign --ecc256 --sha256 \
    test-app/image.bin wolfboot_signing_private_key.der 1

A full bring-up walkthrough is in docs/Targets.md, including JTAG-loaded development, SD-card programming, the Linux/U-Boot recipe, and a comparison to the ZynqMP port.

We hope the addition unlocks Zynq-7000-based products for the wolfSSL community. Feedback, hardware-review reports, and SAMA5D3 regression test results all welcome on the PR #770

If you have questions about any of the above, please contact us at facts@wolfssl.com or call us at +1 425 245 8247.

Download wolfSSL Now