wolfBoot: add Yocto/OE secure bootloader recipes

Add wolfBoot secure boot support to meta-wolfssl. Four new recipes
cross-compile wolfboot.elf, build host-side signing/keygen tools,
and sign kernel FIT images with RSA4096+SHA3-384 for verified boot.

New recipes:
  wolfboot.inc                    - shared SRC_URI, LICENSE, SRCREVs
  wolfboot_git.bb                 - cross-compiles wolfboot.elf from a
                                    user-selected config/examples/ template;
                                    embeds a user-supplied RSA4096 public key
  wolfboot-keytools-native_git.bb - native wolfboot-keygen / wolfboot-sign
  wolfboot-signed-image.bb        - signs kernel FIT with RSA4096+SHA3-384

Supporting files:
  xilinx-bootbin_%.bbappend       - replaces U-Boot with wolfBoot in BOOT.BIN
                                    on ZynqMP (gated by WOLFBOOT_ENABLE="1");
                                    uses BBFILES_DYNAMIC for meta-xilinx-tools
  conf/layer.conf                 - registers wolfboot BBFILES globs and
                                    PREFERRED_PROVIDER
  README.md                       - usage guide, key provisioning, ZynqMP notes

Design decisions:
- Signing key is user-supplied out-of-band (WOLFBOOT_SIGNING_KEY) to
  avoid leaking private key material through sstate or DEPLOY_DIR_IMAGE.
- wolfboot_git.bb uses raw make (not oe_runmake) because wolfBoot is a
  bare-metal bootloader with its own -nostdlib/-ffreestanding toolchain
  flags that conflict with Yocto's CC/CFLAGS/LDFLAGS injection.
- SRCREVs use weak assignment (?=) so downstream users can override via
  local.conf to track unreleased upstream fixes.

Tested on AMD/Xilinx ZCU102 hardware with PetaLinux 2025.2:
  FSBL -> PMU FW -> ATF -> wolfBoot (EL2) -> signed FIT -> Linux
  WOLFBOOT_CONFIG="zynqmp_sdcard.config"
  WOLFBOOT_LINUX_BOOTARGS_ROOT="/dev/mmcblk0p4"
pull/164/head
David Garske 2026-04-16 16:14:39 -07:00
parent e710f093ac
commit 1b58000aa4
7 changed files with 554 additions and 0 deletions

View File

@ -26,6 +26,8 @@ BBFILES += "${LAYERDIR}/recipes-wolfssl/wolfssl/*.bb \
${LAYERDIR}/recipes-wolfssl/wolfprovider/wolfssl*.bbappend \
${LAYERDIR}/recipes-wolfssl/wolfengine/wolfengine*.bb \
${LAYERDIR}/recipes-wolfssl/wolfengine/wolfssl*.bbappend \
${LAYERDIR}/recipes-wolfssl/wolfboot/*.bb \
${LAYERDIR}/recipes-wolfssl/wolfboot/*.bbappend \
${LAYERDIR}/recipes-examples/wolfcrypt/wolfcryptbenchmark/*.bb \
${LAYERDIR}/recipes-examples/wolfcrypt/wolfcryptbenchmark/*.bbappend \
${LAYERDIR}/recipes-examples/wolfcrypt/wolfcrypttest/*.bb \
@ -45,6 +47,9 @@ BBFILES += "${LAYERDIR}/recipes-wolfssl/wolfssl/*.bb \
${LAYERDIR}/recipes-support/gnutls/*.bbappend \
${LAYERDIR}/recipes-support/gnutls/wolfssl-gnutls-wrapper_git.bb"
# xilinx-bootbin bbappend requires meta-xilinx-tools layer
BBFILES_DYNAMIC += "xilinx-tools:${LAYERDIR}/recipes-bsp/bootbin/*.bbappend"
# Uncomment if building bind with wolfSSL.
#BBFILES += "${LAYERDIR}/recipes-connectivity/bind/*.bbappend"
@ -99,6 +104,7 @@ PREFERRED_PROVIDER_wolfssl-py ??= "wolfssl-py"
PREFERRED_PROVIDER_wolfcrypt-py ??= "wolfcrypt-py"
PREFERRED_PROVIDER_wolfprovider ??= "wolfprovider"
PREFERRED_PROVIDER_wolfengine ??= "wolfengine"
PREFERRED_PROVIDER_wolfboot ??= "wolfboot"
BBFILES += "${@bb.utils.contains('WOLFSSL_TYPE', \
'fips', \

View File

@ -0,0 +1,42 @@
# Replace U-Boot with wolfBoot as second-stage bootloader in BOOT.BIN
#
# Only activates when WOLFBOOT_ENABLE = "1" is set in configuration (e.g.
# local.conf or a machine .conf). Without that flag this bbappend is a
# no-op and xilinx-bootbin keeps using U-Boot.
#
# A dedicated variable is used here instead of inspecting EXTRA_IMAGEDEPENDS:
# EXTRA_IMAGEDEPENDS is frequently set inside image recipes, which are
# parsed in a different context than xilinx-bootbin, so the anonymous
# python() below would see an empty value and silently stay inert.
#
# Boot chain with wolfBoot:
# FSBL -> PMU FW -> (bitstream) -> ATF (EL3) -> wolfBoot (EL2) -> signed FIT
#
# Default ZynqMP BIF order (machine-xilinx-zynqmp.inc):
# FSBL -> PMU FW -> bitstream (optional) -> ATF -> DTB -> U-Boot
#
# wolfBoot BIF order:
# FSBL -> PMU FW -> bitstream (optional) -> ATF -> wolfBoot
# (DTB is inside the signed FIT image, loaded by wolfBoot at runtime.)
python () {
if (d.getVar('WOLFBOOT_ENABLE') or '') != '1':
# wolfBoot not requested -- this bbappend is inert.
return
# Apply wolfBoot BIF overrides below.
d.setVar('BIF_SSBL_ATTR', 'wolfboot')
d.setVarFlag('BIF_PARTITION_ATTR', 'wolfboot',
'destination_cpu=a53-0,exception_level=el-2')
d.setVarFlag('BIF_PARTITION_IMAGE', 'wolfboot',
d.expand('${RECIPE_SYSROOT}/boot/wolfboot.elf'))
d.setVar('BIF_DEVICETREE_ATTR', '')
# BIF_PARTITION_IMAGE[wolfboot] points at ${RECIPE_SYSROOT}/boot/wolfboot.elf,
# which is populated by do_populate_sysroot, not do_deploy. Depend on
# the right task so xilinx-bootbin blocks until wolfboot.elf is staged.
d.appendVar('DEPENDS', ' wolfboot')
existing = d.getVarFlag('do_compile', 'depends') or ''
d.setVarFlag('do_compile', 'depends', existing + ' wolfboot:do_populate_sysroot')
}
# Only meaningful on ZynqMP (Versal has its own PLM-based flow; not handled here)
COMPATIBLE_MACHINE:append = "|xilinx-zynqmp.*"

View File

@ -0,0 +1,181 @@
# wolfBoot (Yocto/OE recipes)
Recipes that build [wolfBoot](https://github.com/wolfssl/wolfBoot), wolfSSL's
portable secure bootloader, as part of a Yocto / OpenEmbedded image.
## Recipes
| Recipe | Purpose |
|---|---|
| `wolfboot-keytools-native_git.bb` | Host-side `wolfboot-keygen` and `wolfboot-sign` utilities (RSA4096 key generation, RSA4096+SHA3-384 image signing). |
| `wolfboot_git.bb` | Cross-compiles `wolfboot.elf` (bare-metal, AArch64 / AArch32 / RISC-V). Picks a config from `${S}/config/examples/` per the `WOLFBOOT_CONFIG` variable. Consumes a user-supplied signing key via `WOLFBOOT_SIGNING_KEY` (see "Signing-key provisioning" below). |
| `wolfboot-signed-image.bb` | Signs the kernel FIT image (default `fitImage`) with RSA4096+SHA3-384 and emits `image_v${WOLFBOOT_IMAGE_VERSION}_signed.bin` into `DEPLOY_DIR_IMAGE`. |
A companion `recipes-bsp/bootbin/xilinx-bootbin_%.bbappend` overrides the
AMD/Xilinx `xilinx-bootbin` recipe to swap U-Boot for `wolfboot.elf` in
`BOOT.BIN` on ZynqMP. It only activates when `WOLFBOOT_ENABLE = "1"` is
set in configuration (`local.conf` or a machine `.conf`).
## Signing-key provisioning (required)
`wolfboot_git.bb` and `wolfboot-signed-image.bb` both require you to
supply a pre-generated RSA4096 private key via `WOLFBOOT_SIGNING_KEY`.
The recipes will **not** auto-generate one — that would leak the
private half through sstate and DEPLOY_DIR_IMAGE.
Generate the key pair once on your workstation:
```sh
# One-time host-side build of the keytools (outside Yocto):
make -C /path/to/wolfBoot/tools/keytools keygen sign
# Generate the RSA4096 key pair:
/path/to/wolfBoot/tools/keytools/keygen --rsa4096 \
-g /secure/path/wolfboot_signing_private_key.der
```
Store `wolfboot_signing_private_key.der` **outside** the build tree — a
secrets manager, an encrypted volume, or at minimum a `.gitignore`d
directory. Back it up: losing the private key means you can never sign
another A/B update image that your deployed `wolfboot.elf` will accept.
## Quick start (AMD/Xilinx ZynqMP, SD-card boot)
Set the following in `local.conf` (or a machine `.conf`). These must live
in configuration, not inside an image recipe: the `xilinx-bootbin`
bbappend reads `WOLFBOOT_ENABLE` at parse time, which runs before any
image recipe is evaluated.
```bitbake
# Activate the xilinx-bootbin bbappend (swap U-Boot for wolfBoot in BOOT.BIN)
WOLFBOOT_ENABLE = "1"
# Ensure the signed FIT image is built alongside the image.
EXTRA_IMAGEDEPENDS:append = " wolfboot-signed-image"
# REQUIRED: absolute path to the pre-generated signing private key.
WOLFBOOT_SIGNING_KEY = "/secure/path/wolfboot_signing_private_key.der"
# Pick a config template from wolfBoot/config/examples/
WOLFBOOT_CONFIG = "zynqmp_sdcard.config"
# Override the Linux rootfs partition in the DTB bootargs fixup.
# Default in the ZynqMP SD-card example is /dev/mmcblk0p4.
WOLFBOOT_LINUX_BOOTARGS_ROOT = "/dev/mmcblk0p4"
# Bump to "2" for A/B update images, etc.
WOLFBOOT_IMAGE_VERSION = "1"
```
Build:
```
bitbake <your-image>
```
Artifacts deployed to `tmp/deploy/images/<MACHINE>/`:
- `BOOT.BIN` - FSBL + PMUFW + ATF + wolfBoot.elf
- `wolfboot.elf` - bare-metal bootloader
- `wolfboot_signing_public_key.der` - verifying key (safe to publish; only if `WOLFBOOT_PUBLIC_KEY` is set)
- `image_v1_signed.bin` - signed FIT image (for OFP_A partition)
Note: the private signing key is **not** deployed — it stays on the
workstation / secrets store you pointed `WOLFBOOT_SIGNING_KEY` at.
## SD card layout (wolfBoot A/B scheme)
| Partition | Size | Type | Contents |
|---|---|---|---|
| p1 | 128 MB | FAT32 | `BOOT.BIN` |
| p2 | 200 MB | raw | `image_v1_signed.bin` (OFP_A = primary) |
| p3 | 200 MB | raw | update slot (OFP_B) |
| p4 | rest | ext4 | Linux rootfs |
See `wolfBoot/tools/scripts/` for helper scripts to create the SD card
and flash update images (once upstreamed).
## Key management
The signing key pair is **user-supplied**, not generated inside the build.
`WOLFBOOT_SIGNING_KEY` must point at a pre-generated RSA4096 private key
file (DER format) — see the "Signing-key provisioning" section at the top
of this README. The recipe:
- Stages the private key at `${S}/wolfboot_signing_private_key.der` so the
wolfBoot Makefile can derive the public half and compile it into
`wolfboot.elf` via `src/keystore.c`.
- Does **not** deploy the private key to `DEPLOY_DIR_IMAGE`. Only
`wolfboot.elf` (and optionally the public key, if `WOLFBOOT_PUBLIC_KEY`
is set) is deployed.
- `wolfboot-signed-image.bb` reads the same `WOLFBOOT_SIGNING_KEY`
directly to sign the FIT, so there is no on-disk hand-off of the
private key between recipes.
To rotate keys:
1. Generate a new RSA4096 key pair with `wolfboot-keygen --rsa4096 -g …`.
Prefer a **new filename/path** for each rotation (e.g.
`wolfboot_signing_private_key_v2.der`) rather than overwriting the
existing `.der` in place — see the caveat below.
2. Update `WOLFBOOT_SIGNING_KEY` to the new private key path.
3. Build: `wolfboot.elf` embeds the new public key, and
`image_v<N>_signed.bin` is signed with the new private key.
4. Flash the new `BOOT.BIN` (contains new `wolfboot.elf`) to the device,
followed by a signed update image signed with the new key.
**sstate caveat.** `WOLFBOOT_SIGNING_KEY` points at an absolute path
outside the recipe tree, so BitBake does not checksum the key file's
contents into the task hash — only the path string participates. If you
overwrite the same `.der` file in place with new key material, sstate
and stamps may reuse the previously-built `wolfboot.elf` and signed
image, leaving you with a mismatched public/private pair. To stay safe:
- **Preferred**: rotate to a *new filename/path*. Changing the value of
`WOLFBOOT_SIGNING_KEY` invalidates the task hash naturally.
- **Fallback** (if you must reuse the same path): force a rebuild with
`bitbake -c cleansstate wolfboot wolfboot-signed-image` before the
next `bitbake <your-image>`.
## Swapping U-Boot for wolfBoot
The `recipes-bsp/bootbin/xilinx-bootbin_%.bbappend` rewrites the BIF so
`BOOT.BIN` contains `wolfboot.elf` instead of `u-boot.elf` at EL2, and
drops the standalone DTB partition (wolfBoot loads the DTB out of the
signed FIT image). It activates only when `WOLFBOOT_ENABLE = "1"` is set
in configuration (`local.conf` or a machine `.conf`). Setting it inside
an image recipe is **not** sufficient: the bbappend's anonymous python
runs at parse time, before image recipes are evaluated.
## ZynqMP caveats
See the comments in `wolfBoot/config/examples/zynqmp_sdcard.config` for
two non-obvious points:
1. **Root device**: when the XSA disables `sdhci0` (common on ZCU102
boards where only the external SD slot is populated), SD1 enumerates
as `/dev/mmcblk0`, not `/dev/mmcblk1`. The template default is
`mmcblk0p4`; flip to `mmcblk1p4` only if both sdhci0 + sdhci1 are
enabled. Controlled via `WOLFBOOT_LINUX_BOOTARGS_ROOT`.
2. **BOOT_EL1**: `BOOT_EL1?=1` in the example is currently inert on
ZynqMP because `EL2_HYPERVISOR` is defined in `hal/zynq.h` (included
by `src/boot_aarch64_start.S`) but not in `src/boot_aarch64.c`. wolfBoot
stays at EL2 before jumping to Linux, which matches standard PetaLinux
U-Boot behavior. See the comments in the template.
## Future work
- **`PROVIDES += "virtual/bootloader"`** — On vanilla Yocto where U-Boot
is the sole bootloader, `PREFERRED_PROVIDER_virtual/bootloader = "wolfboot"`
would be a cleaner selector than `WOLFBOOT_ENABLE = "1"` plus the
`xilinx-bootbin` bbappend. We deliberately do not declare that PROVIDES
yet, because on AMD/Xilinx ZynqMP — the only target this layer has been
validated against — it does not actually simplify the integration:
`xilinx-bootbin` references U-Boot by PN (`u-boot-xlnx`) inside its BIF
generation rather than going through `virtual/bootloader`, and
`IMAGE_BOOT_FILES` lists U-Boot artifacts by literal name. The current
bbappend rewrites the BIF entry surgically; that swap would still be
required regardless of who provides `virtual/bootloader`. The right
time to add this PROVIDES is when a non-Xilinx BSP integration lands,
so the contract can be tested end-to-end.

View File

@ -0,0 +1,27 @@
SUMMARY = "wolfBoot signing and key generation tools (native)"
DESCRIPTION = "Host-side keygen and sign utilities for wolfBoot secure-boot \
image signing. Builds RSA4096 signing keys and signs firmware images with \
SHA3-384 hashes. Uses wolfBoot's bundled wolfCrypt (under lib/wolfssl) -- \
no external wolfSSL dependency."
require wolfboot.inc
inherit native
do_configure[noexec] = "1"
do_compile() {
# Build the keytools (host-side signing/keygen utilities).
oe_runmake -C tools/keytools \
CC="${CC}" \
LD="${CC}" \
WOLFBOOTDIR=${S} \
WOLFBOOT_LIB_WOLFSSL=${S}/lib/wolfssl \
V=1
}
do_install() {
install -d ${D}${bindir}
install -m 0755 ${S}/tools/keytools/sign ${D}${bindir}/wolfboot-sign
install -m 0755 ${S}/tools/keytools/keygen ${D}${bindir}/wolfboot-keygen
}

View File

@ -0,0 +1,94 @@
SUMMARY = "wolfBoot signed kernel FIT image"
DESCRIPTION = "Signs the kernel FIT image (fitImage / image.ub) with \
wolfBoot RSA4096+SHA3-384 for verified secure boot. The signed output is \
placed in DEPLOY_DIR_IMAGE as image_v<version>_signed.bin, ready for \
flashing to the OFP_A / OFP_B partitions of an A/B-enabled SD card or QSPI."
HOMEPAGE = "https://github.com/wolfssl/wolfBoot"
SECTION = "bootloaders"
LICENSE = "GPL-3.0-only"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-3.0-only;md5=c79ff39f19dfec6d293b95dea7b07891"
inherit deploy
DEPENDS = "wolfboot-keytools-native"
# REQUIRED: absolute path to the same wolfBoot signing private key used by
# wolfboot_git.bb to embed the public key into wolfboot.elf. User-supplied
# out-of-band (never sourced from DEPLOY_DIR_IMAGE or sstate).
WOLFBOOT_SIGNING_KEY ?= ""
# Kernel recipe name -- default matches linux-xlnx on ZynqMP / Versal.
# Override in local.conf for non-Xilinx kernels (e.g. KERNEL_PN = "linux-yocto").
KERNEL_PN ?= "linux-xlnx"
# Only depend on the kernel being deployed. The signing key is supplied
# directly by the user, NOT pulled from wolfboot:do_deploy, so there is no
# wolfboot dep here.
do_compile[depends] += "${KERNEL_PN}:do_deploy"
do_configure[noexec] = "1"
# Version stamped into the signed image header. Increment for A/B updates
# (e.g. set to "2" for the next A/B update image).
WOLFBOOT_IMAGE_VERSION ?= "1"
# Name of the FIT image produced by the kernel recipe. Defaults match
# linux-xlnx on ZynqMP / Versal.
WOLFBOOT_FIT_IMAGE ?= "fitImage"
# Validate WOLFBOOT_SIGNING_KEY only when this recipe actually builds
# (see wolfboot_git.bb for the rationale re: parse-time vs task-time).
python check_wolfboot_signing_key() {
key = d.getVar('WOLFBOOT_SIGNING_KEY') or ''
if not key:
bb.fatal("WOLFBOOT_SIGNING_KEY is not set. Generate a signing key "
"with 'wolfboot-keygen --rsa4096 -g <path>.der' and point "
"WOLFBOOT_SIGNING_KEY at it (absolute path). The SAME key "
"must also be set in wolfboot_git.bb so public/private "
"halves match. See recipes-wolfssl/wolfboot/README.md.")
import os
if not os.path.isfile(key):
bb.fatal("WOLFBOOT_SIGNING_KEY='%s' does not exist or is not a "
"regular file." % key)
}
do_compile[prefuncs] += "check_wolfboot_signing_key"
do_compile() {
install -d ${B}
# Validate the upstream FIT image exists before invoking the signing
# tool, so a missing/renamed artifact fails with an actionable message
# instead of a cryptic wolfboot-sign error.
fit_image="${DEPLOY_DIR_IMAGE}/${WOLFBOOT_FIT_IMAGE}"
if [ ! -f "$fit_image" ]; then
bbfatal "FIT image '$fit_image' not found. Check WOLFBOOT_FIT_IMAGE and that ${KERNEL_PN}:do_deploy produced it."
fi
# Sign a local copy under ${B} instead of operating directly on the
# shared DEPLOY_DIR_IMAGE. wolfboot-sign writes its output next to the
# input; keeping both input and output under ${WORKDIR} avoids races
# with the kernel's do_deploy (or parallel multiconfig builds) and
# leaves publishing to do_deploy below.
cp "$fit_image" ${B}/${WOLFBOOT_FIT_IMAGE}
# Sign the FIT image with RSA4096 + SHA3-384 using the user-supplied
# signing key. wolfboot-sign emits the output NEXT TO the input file,
# naming it <input>_v<version>_signed.bin. Run from ${B} with a
# relative path so the output lands inside ${B} predictably.
cd ${B}
wolfboot-sign --rsa4096 --sha3 \
${WOLFBOOT_FIT_IMAGE} \
${WOLFBOOT_SIGNING_KEY} \
${WOLFBOOT_IMAGE_VERSION}
}
do_install[noexec] = "1"
do_deploy() {
install -d ${DEPLOYDIR}
install -m 0644 ${B}/${WOLFBOOT_FIT_IMAGE}_v${WOLFBOOT_IMAGE_VERSION}_signed.bin \
${DEPLOYDIR}/image_v${WOLFBOOT_IMAGE_VERSION}_signed.bin
}
addtask deploy before do_build after do_compile

View File

@ -0,0 +1,27 @@
# Shared include for wolfBoot recipes
#
# Pulls the wolfBoot source tree and the wolfSSL submodule source side-by-side.
# wolfBoot bundles wolfSSL under lib/wolfssl, so we stage wolfSSL there instead
# of fetching it from the wolfBoot submodule pointer (keeps the two SRCREVs
# explicit and greppable).
HOMEPAGE = "https://github.com/wolfssl/wolfBoot"
BUGTRACKER = "https://github.com/wolfssl/wolfBoot/issues"
SECTION = "bootloaders"
LICENSE = "GPL-3.0-only"
LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464"
# NOTE: SRCREVs below are pinned to wolfSSL/wolfBoot master and
# wolfSSL/wolfssl master tips at the time of writing. Bump these as
# upstream evolves. Downstream users can override via local.conf:
# SRCREV_wolfboot:pn-wolfboot = "<sha>"
# SRCREV_wolfboot:pn-wolfboot-keytools-native = "<sha>"
SRC_URI = " \
git://github.com/wolfssl/wolfBoot.git;protocol=https;branch=master;name=wolfboot;destsuffix=git \
git://github.com/wolfssl/wolfssl.git;protocol=https;branch=master;name=wolfssl;destsuffix=git/lib/wolfssl \
"
SRCREV_wolfboot ?= "25c0aaa8bd5a64f85f4d1eef9fab698275d32f20"
SRCREV_wolfssl ?= "c4c71eece3e6eae7042a16fffdebf9bd80a4f2a1"
SRCREV_FORMAT = "wolfboot_wolfssl"
S = "${WORKDIR}/git"

View File

@ -0,0 +1,177 @@
SUMMARY = "wolfBoot secure bootloader"
DESCRIPTION = "wolfBoot is a portable, OS-agnostic secure bootloader for \
32-bit and 64-bit targets. It provides verified secure boot with A/B \
update / rollback support. On AMD/Xilinx ZynqMP it replaces U-Boot as \
the second-stage bootloader (FSBL -> PMU FW -> ATF (EL3) -> wolfBoot \
(EL2) -> signed Linux kernel)."
require wolfboot.inc
inherit deploy
# Which config/examples/*.config template to build against. Override in
# local.conf / image recipe to target a different board or boot medium.
# Examples: zynqmp_sdcard.config, zynqmp.config (QSPI), versal_sdcard.config
WOLFBOOT_CONFIG ??= "zynqmp_sdcard.config"
# Linux rootfs device passed via the DTB /chosen/bootargs root= patch in
# src/fdt.c. Override to match your partition layout. The wolfBoot ZynqMP
# example configs hard-code a sensible default but it is far easier to
# flip this single variable than to ship a custom .config fork.
WOLFBOOT_LINUX_BOOTARGS_ROOT ??= ""
# REQUIRED: absolute path to a pre-generated wolfBoot signing private key
# (DER format, RSA4096 by default). Generate once via
# `wolfboot-keygen --rsa4096 -g wolfboot_signing_private_key.der` and store
# outside the build tree (never inside DEPLOY_DIR_IMAGE or sstate).
# See README.md for the full provisioning workflow.
WOLFBOOT_SIGNING_KEY ?= ""
# OPTIONAL: absolute path to a pre-generated wolfBoot signing public key
# (DER format). If unset, wolfBoot's keygen derives the public key from the
# private key in-memory to populate src/keystore.c. Only set this if you
# want to pin a public key independent of the private key file (e.g. HSM).
WOLFBOOT_PUBLIC_KEY ?= ""
# keytools-native provides wolfboot-keygen (used only when deriving the
# public half from a supplied private key) and wolfboot-sign (used by
# wolfboot-signed-image.bb for FIT image signing).
DEPENDS = "wolfboot-keytools-native"
# Additional flags passed to 'make wolfboot.elf'. These are command-line
# overrides, so they take precedence over .config assignments. WARNING:
# do NOT use this for CFLAGS_EXTRA command-line CFLAGS_EXTRA+= would
# replace ALL file-level CFLAGS_EXTRA lines, wiping out critical defines
# like SDHCI_FORCE_CARD_DETECT. Use WOLFBOOT_EXTRA_CONFIG_LINES instead.
WOLFBOOT_EXTRA_MAKE_FLAGS ?= ""
# Extra lines appended to the .config after the template is copied.
# Use this for CFLAGS_EXTRA additions (e.g. "CFLAGS_EXTRA+=-O2") so they
# coexist with the template's own CFLAGS_EXTRA lines.
WOLFBOOT_EXTRA_CONFIG_LINES ?= ""
COMPATIBLE_MACHINE = ".*"
PACKAGE_ARCH = "${MACHINE_ARCH}"
do_configure[noexec] = "1"
# Validate WOLFBOOT_SIGNING_KEY only when this recipe actually builds.
# A parse-time anonymous python () {} would fail any bitbake invocation
# that merely parses meta-wolfssl (e.g. unrelated CI running
# `bitbake -c cleanall wolfssl`); a named prefunc runs only when
# do_compile is scheduled.
python check_wolfboot_signing_key() {
import os
key = d.getVar('WOLFBOOT_SIGNING_KEY') or ''
if not key:
bb.fatal("WOLFBOOT_SIGNING_KEY is not set. Generate a signing key "
"with 'wolfboot-keygen --rsa4096 -g <path>.der' and point "
"WOLFBOOT_SIGNING_KEY at it (absolute path). See "
"recipes-wolfssl/wolfboot/README.md for the full workflow.")
if not os.path.isfile(key):
bb.fatal("WOLFBOOT_SIGNING_KEY='%s' does not exist or is not a "
"regular file." % key)
pub = d.getVar('WOLFBOOT_PUBLIC_KEY') or ''
if not pub:
bb.fatal("WOLFBOOT_PUBLIC_KEY is not set. Extract the public half "
"from the private key with: openssl rsa -in <privkey>.der "
"-inform DER -pubout -outform DER -out <pubkey>.der")
if not os.path.isfile(pub):
bb.fatal("WOLFBOOT_PUBLIC_KEY='%s' does not exist or is not a "
"regular file." % pub)
}
do_compile[prefuncs] += "check_wolfboot_signing_key"
do_compile() {
# Seed wolfBoot's Makefile with the requested example .config.
if [ ! -f ${S}/config/examples/${WOLFBOOT_CONFIG} ]; then
bbfatal "WOLFBOOT_CONFIG='${WOLFBOOT_CONFIG}' not found under ${S}/config/examples/"
fi
cp ${S}/config/examples/${WOLFBOOT_CONFIG} ${S}/.config
# Append any extra config lines (e.g. CFLAGS_EXTRA+=-O2 from a bbappend).
if [ -n "${WOLFBOOT_EXTRA_CONFIG_LINES}" ]; then
echo "${WOLFBOOT_EXTRA_CONFIG_LINES}" >> ${S}/.config
fi
# Optionally override the Linux rootfs device in the compiled-in bootargs.
if [ -n "${WOLFBOOT_LINUX_BOOTARGS_ROOT}" ]; then
sed -i -e 's|-DLINUX_BOOTARGS_ROOT=\\"[^"]*\\"|-DLINUX_BOOTARGS_ROOT=\\"${WOLFBOOT_LINUX_BOOTARGS_ROOT}\\"|' \
${S}/.config
fi
# Cross-compile wolfboot.elf.
# wolfBoot is a bare-metal bootloader (-nostdlib -ffreestanding), so we
# use raw make (not oe_runmake) to prevent Yocto's CC/CFLAGS/LDFLAGS
# from overriding wolfBoot's own toolchain settings. The Yocto cross
# compiler still needs --sysroot to find headers and libgcc; we embed
# it in CC so it applies to both compilation and linking.
#
# USER_PRIVATE_KEY + USER_PUBLIC_KEY tell wolfBoot's Makefile to use a
# pre-generated key pair instead of regenerating one inside the build
# tree (upstream contract, see wolfBoot/Makefile:371). This sidesteps
# the fact that wolfBoot's in-tree keytools target would otherwise try
# to cross-compile keygen for AArch64 and then run the resulting
# AArch64 binary on the x86_64 build host -- which fails under qemu
# inside Docker if AArch64 binfmt isn't set up.
#
# wolfBoot's Makefile requires both USER_PRIVATE_KEY and USER_PUBLIC_KEY.
# Both must be supplied by the user (generate via wolfboot-keygen).
if [ -z "${WOLFBOOT_PUBLIC_KEY}" ] || [ ! -f "${WOLFBOOT_PUBLIC_KEY}" ]; then
bbfatal "WOLFBOOT_PUBLIC_KEY is not set or the file does not exist. " \
"Generate both keys with 'wolfboot-keygen --rsa4096 -g <privkey>.der' " \
"then extract the public half with 'openssl rsa -in <privkey>.der " \
"-inform DER -pubout -outform DER -out <pubkey>.der'. Point both " \
"WOLFBOOT_SIGNING_KEY and WOLFBOOT_PUBLIC_KEY at the respective files."
fi
PUBKEY_FOR_MAKE=${WOLFBOOT_PUBLIC_KEY}
unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS
SYSROOT_FLAG="--sysroot=${RECIPE_SYSROOT}"
# KEYGEN_TOOL override: wolfBoot's Makefile otherwise tries to build
# tools/keytools/keygen using the target cross-compiler and then run
# the resulting AArch64 binary on the x86_64 build host. Point it at
# the native keygen from wolfboot-keytools-native instead.
NATIVE_KEYGEN="$(command -v wolfboot-keygen)"
make wolfboot.elf \
CROSS_COMPILE=${TARGET_PREFIX} \
CC="${TARGET_PREFIX}gcc $SYSROOT_FLAG" \
LD="${TARGET_PREFIX}gcc $SYSROOT_FLAG" \
USER_PRIVATE_KEY="${WOLFBOOT_SIGNING_KEY}" \
USER_PUBLIC_KEY="$PUBKEY_FOR_MAKE" \
KEYGEN_TOOL="$NATIVE_KEYGEN" \
${WOLFBOOT_EXTRA_MAKE_FLAGS} \
V=1
}
do_install() {
# Install wolfboot.elf into sysroot for xilinx-bootbin BIF consumption
# (BIF_PARTITION_IMAGE[wolfboot] points at ${RECIPE_SYSROOT}/boot/wolfboot.elf).
install -d ${D}/boot
install -m 0644 ${S}/wolfboot.elf ${D}/boot/wolfboot.elf
}
do_deploy() {
install -d ${DEPLOYDIR}
install -m 0644 ${S}/wolfboot.elf ${DEPLOYDIR}/wolfboot.elf
# Optionally deploy the public signing key (safe to publish). This is
# the verifying key embedded in wolfboot.elf; having it in DEPLOYDIR
# lets CI and downstream tooling verify signed images without access
# to the private key.
if [ -n "${WOLFBOOT_PUBLIC_KEY}" ] && [ -f "${WOLFBOOT_PUBLIC_KEY}" ]; then
install -m 0644 ${WOLFBOOT_PUBLIC_KEY} ${DEPLOYDIR}/wolfboot_signing_public_key.der
fi
# NOTE: the private key (wolfboot_signing_private_key.der) is
# intentionally NOT deployed. User-supplied out-of-band via
# WOLFBOOT_SIGNING_KEY; publishing it would defeat the point of signed
# boot.
}
addtask deploy before do_build after do_compile
# wolfBoot is a bare-metal bootloader -- skip QA checks that assume a
# hosted Linux binary.
INSANE_SKIP:${PN} = "ldflags textrel buildpaths"
FILES:${PN} = "/boot/wolfboot.elf"
SYSROOT_DIRS += "/boot"