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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ include/target.h: $(TARGET_H_TEMPLATE) FORCE
sed -e "s/@WOLFBOOT_LOAD_ADDRESS@/$(WOLFBOOT_LOAD_ADDRESS)/g" | \
sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" | \
sed -e "s/@WOLFBOOT_LOAD_RAMDISK_ADDRESS@/$(WOLFBOOT_LOAD_RAMDISK_ADDRESS)/g" | \
sed -e "s/@WOLFBOOT_LOAD_FPGA_ADDRESS@/$(WOLFBOOT_LOAD_FPGA_ADDRESS)/g" | \
sed -e "s|@WOLFBOOT_RAMBOOT_MAX_SIZE_DEFINE@|$(if $(strip $(WOLFBOOT_RAMBOOT_MAX_SIZE)),#define WOLFBOOT_RAMBOOT_MAX_SIZE $(WOLFBOOT_RAMBOOT_MAX_SIZE),/* WOLFBOOT_RAMBOOT_MAX_SIZE undefined */)|g" | \
sed -e "s/@WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@/$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS)/g" \
> $@
Expand Down
7 changes: 7 additions & 0 deletions arch.mk
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ ifeq ($(ARCH),AARCH64)
# Support detection and skip of U-Boot legacy header */
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
CFLAGS+=-DWOLFBOOT_DUALBOOT
# FPGA bitstream-from-FIT is supported via the PMU firmware
# (PM_FPGA_LOAD). Enable with FPGA_BITSTREAM=1 (opt-in).

ifeq ($(HW_SHA3),1)
# Use HAL for hash (see zynqmp.c)
Expand All @@ -90,6 +92,9 @@ ifeq ($(ARCH),AARCH64)
CFLAGS+=-DWOLFBOOT_DUALBOOT
# Support detection and skip of U-Boot legacy header
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
# NOTE: FPGA_BITSTREAM is stubbed on Versal (PL config is a PDI loaded
# by the PLM via XilLoader Load-PDI IPI - not yet implemented). Leave
# FPGA_BITSTREAM off until that path lands.
# PLM owns RVBAR on Versal in JTAG boot; skip RVBAR writes
CFLAGS+=-DSKIP_RVBAR=1
# Disable SDMA for multi-block transfers - use PIO instead.
Expand Down Expand Up @@ -350,6 +355,8 @@ ifeq ($(ARCH),ARM)
# positive probability from ~2^-32 to ~2^-64, matching what U-Boot's
# own mkimage/bootm does.
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
# FPGA bitstream-from-FIT is supported via the DevC/PCAP DMA engine
# (full bitstream). Enable with FPGA_BITSTREAM=1 (opt-in).
endif

ifeq ($(TARGET),va416x0)
Expand Down
8 changes: 8 additions & 0 deletions config/examples/zynqmp.config
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ WOLFBOOT_LOAD_DTS_ADDRESS?=0x11800000
WOLFBOOT_DTS_BOOT_ADDRESS?=0x7B0000
WOLFBOOT_DTS_UPDATE_ADDRESS?=0x39B0000

# Optional: program the PL from an "fpga" sub-image in the FIT before boot
# (handed to the PMU firmware via PM_FPGA_LOAD). Requires GZIP=1 when the
# bitstream node uses compression="gzip" (the typical mkimage output). The
# fpga node usually has no `load` property, so set a DDR staging address
# clear of the FIT (0x10000000), kernel (0x200000), and DTS (0x11800000).
#FPGA_BITSTREAM?=1
#WOLFBOOT_LOAD_FPGA_ADDRESS?=0x40000000


CROSS_COMPILE=aarch64-none-elf-
#CROSS_COMPILE=aarch64-linux-gnu-
Expand Down
57 changes: 57 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -4070,6 +4070,63 @@ FDT: Set chosen (...), linux,initrd-start=1073741824
FDT: Set chosen (...), linux,initrd-end=...
```

**FPGA bitstream from FIT**

wolfBoot can program the PL (FPGA fabric) from an `fpga` sub-image carried in the same signed FIT, using the standard U-Boot convention: a sub-image with `type = "fpga"`, referenced from the configuration node via an optional `fpga = "<node>"` property, with a `compatible` string naming the load method. The PL is programmed before the kernel/DTB are loaded so PL-dependent clocks and peripherals come up first. The outer wolfBoot signature authenticates the whole FIT (bitstream included), so no per-image hashing is required.

Enable with `FPGA_BITSTREAM=1` (off by default). A failed PL load is fatal (`wolfBoot_panic`) unless `FPGA_NONFATAL=1` is also set, which downgrades it to a logged warning that continues the boot.

As produced by `mkimage`, the fpga sub-image is typically gzip-compressed (`compression = "gzip"`) and carries NO `load` property (U-Boot decompresses it into a scratch buffer before programming). wolfBoot mirrors this: set `WOLFBOOT_LOAD_FPGA_ADDRESS` to a DDR staging address (clear of the FIT at `WOLFBOOT_LOAD_ADDRESS`, the kernel, and the DTS), and the bitstream is decompressed there before the PL is programmed. Build with `GZIP=1` (already default in the example configs). If the fpga node does provide its own `load`, leave `WOLFBOOT_LOAD_FPGA_ADDRESS=0` to honor it.

```sh
cp config/examples/zynqmp.config .config
make FPGA_BITSTREAM=1 WOLFBOOT_LOAD_FPGA_ADDRESS=0x40000000
```

Per-target support:
- **ZynqMP** (`TARGET=zynq`): supported. The bitstream (a bootgen `.bin` with the `66 55 99 aa` sync word, not a Vivado `.bit`) is staged in DDR and handed to the PMU firmware via the `PM_FPGA_LOAD` EEMI call (xilfpga over the CSU DMA / PCAP). The size is passed in bytes and the flags are 0 for a full legacy bitstream, matching stock Xilinx U-Boot; no software byte-swap is applied.
- **Zynq-7000** (`TARGET=zynq7000`): supported (full bitstream). Programmed directly through the DevC/PCAP DMA engine (UG585 ch.6). Partial reconfiguration is not yet implemented.
- **Versal** (`TARGET=versal`): not yet implemented - Versal programs the PL with a PDI loaded by the PLM (XilLoader Load-PDI IPI), not a raw bitstream. `hal_fpga_load` is a stub; leave `FPGA_BITSTREAM` off on Versal until that path lands.

The `compatible` string selects full vs partial: any value containing `partial` requests partial reconfiguration, otherwise a full bitstream is loaded. Typical full-bitstream values are `u-boot,fpga-legacy`, `u-boot,zynqmp-fpga-ddrauth`, or `u-boot,zynqmp-fpga-enc`.

Decompressed-size validation: the fpga node carries no explicit uncompressed-size property, but for `compression = "gzip"` the gzip trailer encodes it. wolfBoot verifies the gzip CRC32 and ISIZE (decompressed length) on completion and bounds the output by `WOLFBOOT_FIT_MAX_FPGA`, so a truncated or corrupt bitstream is rejected (fatal, or skipped under `FPGA_NONFATAL=1`). The byte count handed to `PM_FPGA_LOAD` is the validated decompressed length. Note that `WOLFBOOT_FIT_MAX_FPGA` is a compile-time cap only (set it with `CFLAGS_EXTRA+=-DWOLFBOOT_FIT_MAX_FPGA=...`; it defaults to `WOLFBOOT_FIT_MAX_DECOMP`, 256 MB) - it is a sanity ceiling, NOT the size of your DDR staging region. It does not by itself stop the bitstream from overrunning the kernel/DTB/FIT areas, so choosing a `WOLFBOOT_LOAD_FPGA_ADDRESS` clear of those regions (and, if you want a tight bound, lowering `WOLFBOOT_FIT_MAX_FPGA` to your staging window) is the integrator's responsibility.

Multiple configurations: wolfBoot boots the FIT's `default` configuration. For a FIT that carries several boards' configurations selected at runtime (e.g. U-Boot's `bootm <addr>#conf-<board>`), build with `FIT_CONFIG_SELECT=1` and provide a `hal_fit_config_name()` in your integration that returns the config node name to boot (e.g. `"conf-fcm"`), or NULL to fall back to `default`. wolfBoot ships only the weak default (returns NULL) - the board-detection logic (PS GPIO, CHIPID, etc.) is intentionally left to the integrator and is not part of upstream.

Example FIT layout (matches `mkimage` output - gzip, no `load`):

```dts
images {
kernel-1 { ... };
fdt-1 { ... };
fpga-1 {
description = "FPGA bitstream";
data = /incbin/("system.bit.bin.gz"); /* gzip of bootgen .bin */
type = "fpga";
arch = "arm64"; /* informational; not read by wolfBoot */
compression = "gzip";
compatible = "u-boot,fpga-legacy";
hash-1 { algo = "sha256"; }; /* covered by outer sig */
};
};
configurations {
default = "conf-zcu102";
conf-zcu102 {
kernel = "kernel-1";
fdt = "fdt-1";
fpga = "fpga-1";
};
};
```

Successful programming prints (ZynqMP):
```
FIT: programming FPGA 'fpga-1' (N bytes, full)
FPGA status: 0x...
FIT: FPGA programmed
```


## Xilinx Zynq-7000 (ZC702)

Expand Down
20 changes: 20 additions & 0 deletions hal/hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,23 @@ WEAKFUNCTION int hal_dice_get_attest_pubkey(uint8_t *buf, size_t *len)
return -1;
}
#endif /* WOLFBOOT_DICE_HW */

#ifdef WOLFBOOT_FPGA_BITSTREAM
WEAKFUNCTION int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
{
/* No FPGA loader for this target; implement in the target hal. */
(void)flags;
(void)addr;
(void)size;
return -1;
}
#endif /* WOLFBOOT_FPGA_BITSTREAM */

#ifdef WOLFBOOT_FIT_CONFIG_SELECT
WEAKFUNCTION const char* hal_fit_config_name(void)
{
/* Default: use the FIT's own `default` configuration. A target
* implements this to select a per-board configuration at runtime. */
return NULL;
}
#endif /* WOLFBOOT_FIT_CONFIG_SELECT */
19 changes: 19 additions & 0 deletions hal/versal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,25 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
return -1;
}

#ifdef WOLFBOOT_FPGA_BITSTREAM
/* Versal programs the PL with a PDI (Programmable Device Image), not a raw
* bitstream. Runtime PL configuration is done by the PLM via the XilLoader
* "Load PDI" command (XLOADER_CMD_ID_LOAD_PDI), reached by building an IPI
* request to the PLM channel with the DDR PDI address/size and polling the
* PLM response. That path is not yet implemented; until it lands, leave
* FPGA_BITSTREAM disabled for Versal (the FIT loader treats a failed load
* as fatal unless FPGA_NONFATAL is set). */
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
{
(void)flags;
(void)addr;
(void)size;
wolfBoot_printf("Versal FPGA/PDI load not implemented "
"(needs PLM XilLoader Load-PDI IPI)\n");
return -1;
}
#endif /* WOLFBOOT_FPGA_BITSTREAM */


/* ============================================================================
* External Flash Interface
Expand Down
25 changes: 25 additions & 0 deletions hal/versal.its
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,38 @@
algo = "sha256";
};
};
/* FPGA bitstream sub-image (requires FPGA_BITSTREAM=1). Add the
* "fpga = ..." reference to the configuration node below. The
* bitstream must be a bootgen .bin staged to its load address.
* NOTE: Versal PL config is a PDI loaded by the PLM (XilLoader
* Load-PDI IPI) - the wolfBoot Versal hal_fpga_load is currently
* a stub, so leave this disabled on Versal. On ZynqMP/Zynq-7000
* this is the working convention.
*
* As emitted by mkimage the fpga node is usually gzip-compressed
* and has no "load" property; set WOLFBOOT_LOAD_FPGA_ADDRESS to a
* DDR staging address and build with GZIP=1.
*
* fpga-1 {
* description = "FPGA bitstream";
* data = /incbin/("../system.bit.bin.gz");
* type = "fpga";
* arch = "arm64";
* compression = "gzip";
* compatible = "u-boot,fpga-legacy";
* hash-1 {
* algo = "sha256";
* };
* };
*/
};
configurations {
default = "conf1";
conf1 {
description = "Linux kernel and FDT blob";
kernel = "kernel-1";
fdt = "fdt-1";
/* fpga = "fpga-1"; */
hash-1 {
algo = "sha256";
};
Expand Down
54 changes: 54 additions & 0 deletions hal/zynq.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <target.h>
#include "image.h"
#include "printf.h"
#include "hal_fpga.h"

#include <stdint.h>
#include <string.h>
Expand Down Expand Up @@ -197,6 +198,13 @@ static void smc_call(struct pt_regs *args)
#define PM_MMIO_WRITE 0x13
#define PM_MMIO_READ 0x14

/* FPGA / PL programming (xilfpga via PMU firmware / TF-A) */
#define PM_FPGA_LOAD 0x16 /* 22 */
#define PM_FPGA_GET_STATUS 0x17 /* 23 */
/* pm_fpga_load flags (bit 0 selects full vs partial bitstream) */
#define XFPGA_FULLBIT_EN 0x0
#define XFPGA_PARTIAL_EN 0x1

/* AES */
/* requires PMU built with -DENABLE_SECURE_VAL=1 */
#define PM_SECURE_AES 0x2F
Expand Down Expand Up @@ -1713,6 +1721,52 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
return 0;
}

#ifdef WOLFBOOT_FPGA_BITSTREAM
/* Program the PL by handing the bitstream to the PMU firmware (xilfpga)
* via the PM_FPGA_LOAD EEMI call. The bitstream must be a bootgen .bin
* resident in DDR; it is flushed from the D-cache so the CSU DMA sees
* the committed bytes. */
int hal_fpga_load(uint32_t flags, uintptr_t addr, size_t size)
{
uint32_t ret_payload[PM_ARGS_CNT];
uint32_t pmflags;

/* PM_FPGA_LOAD takes the bitstream size in BYTES (the PMU firmware
* divides by the word length internally for the CSU DMA). This
* matches stock Xilinx U-Boot (drivers/fpga/zynqmppl.c passes
* bsize verbatim). For a legacy full bitstream the flags argument
* is 0; bit 0 selects partial. */
pmflags = (flags == HAL_FPGA_PARTIAL) ? XFPGA_PARTIAL_EN : XFPGA_FULLBIT_EN;

/* Ensure the bitstream is committed to DDR before the CSU DMA reads it. */
flush_dcache_range((unsigned long)addr, (unsigned long)(addr + size));

memset(ret_payload, 0, sizeof(ret_payload));
/* arg0=addr_low, arg1=addr_high, arg2=size(bytes), arg3=flags */
pmu_request(PM_FPGA_LOAD,
(uint32_t)(addr & 0xFFFFFFFF), (uint32_t)((uint64_t)addr >> 32),
(uint32_t)size, pmflags, ret_payload);
if (ret_payload[0] != 0) {
wolfBoot_printf("PM_FPGA_LOAD failed: %u\n", ret_payload[0]);
return -1;
}

/* Confirm the PL reports configured (PCAP status). This is
* informational - the load already succeeded above - so a failed
* query is logged but does not fail the call. */
memset(ret_payload, 0, sizeof(ret_payload));
pmu_request(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload);
if (ret_payload[0] == 0) {
wolfBoot_printf("FPGA status: 0x%x\n", ret_payload[1]);
}
else {
wolfBoot_printf("FPGA status query failed: %u\n", ret_payload[0]);
}

return 0;
}
#endif /* WOLFBOOT_FPGA_BITSTREAM */

/* Xilinx Write uses SPI mode and Page Program 0x02 */
/* Issues using write with QSPI mode */
int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data, int len)
Expand Down
Loading
Loading