meta-wolfssl/recipes-wolfssl/wolfboot
Chris Conlon 16aae6f302
Merge pull request #170 from MarkAtwood/fix/license-identifiers
fix: correct recipe licenses to match upstream sources
2026-10-23 11:42:29 -06:00
..
README.md wolfboot: build the PolarFire SoC M-mode target with the Yocto RISC-V toolchain and sign its FIT with ECC384 2026-10-09 15:33:32 -07:00
wolfboot-keytools-native_git.bb add option to use external wolfSSL source with wolfBOOT build 2026-07-28 10:14:03 -06:00
wolfboot-signed-image.bb Merge pull request #170 from MarkAtwood/fix/license-identifiers 2026-10-23 11:42:29 -06:00
wolfboot.inc Merge pull request #170 from MarkAtwood/fix/license-identifiers 2026-10-23 11:42:29 -06:00
wolfboot_git.bb wolfboot: build the PolarFire SoC M-mode target with the Yocto RISC-V toolchain and sign its FIT with ECC384 2026-10-09 15:33:32 -07:00

README.md

wolfBoot (Yocto/OE recipes)

Recipes that build 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 (key generation and image signing: RSA4096+SHA3-384 by default, ECC384+SHA-384 for the PolarFire config).
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 WOLFBOOT_SIGN_ALGO + WOLFBOOT_HASH_ALGO (RSA4096+SHA3-384 by default) 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 private key via WOLFBOOT_SIGNING_KEY (RSA4096 by default; ECC384 for the PolarFire config, see the PolarFire SoC quick start below). 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:

# 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 .gitignored 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.

# 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.

Quick start (Microchip PolarFire SoC, standalone M-mode, SD-card boot)

On PolarFire SoC wolfBoot can replace the Hart Software Services (HSS) entirely: it boots from eNVM on the E51, initialises the PLL and LPDDR4, loads a signed Linux FIT from the SD card and hands off to S-mode (polarfire_mpfs250_m.config). The eNVM image is programmed with Microchip's mpfsBootmodeProgrammer, so the recipe deploys wolfboot.elf and leaves the eNVM programming to the user.

Three things differ from the ZynqMP flow:

  • The M-mode build needs the Libero design's fpga_design_config header directory (DDR, clock and SEG settings). Pass it through WOLFBOOT_EXTRA_MAKE_FLAGS; the build has no way to generate it.
  • The PolarFire example config signs with ECC384 / SHA-384, so the signing key is an ECC384 pair from wolfboot-keygen --ecc384 -g <key>.der. The private file keygen writes is Qx || Qy || d; the public half for WOLFBOOT_PUBLIC_KEY is its first 96 bytes (head -c 96 <key>.der > <pub>.der), which keygen -i reads as a raw public key.
  • Partitions 1 and 2 of the SD card are the raw A/B slots (BOOT_PART_A = 0, BOOT_PART_B = 1); files/wic/mpfs-wolfboot.wks fills slot A with the signed FIT, leaves slot B empty, keeps the rootfs at partition 3 and moves the BSP's GUID-tagged HSS payload to partition 4, so the same card still boots under HSS + U-Boot if the eNVM is reprogrammed.
  • The PolarFire config is in wolfBoot v2.10.0 and later, and the link flags the recipe adds need wolfBoot's LDFLAGS_EXTRA (wolfSSL/wolfBoot#921; on older revisions the recipe falls back to CC and LD overrides). Point SRCREV_wolfboot at a revision with both, as shown below.
WOLFBOOT_CONFIG = "polarfire_mpfs250_m.config"
WOLFBOOT_SIGNING_KEY = "/secure/path/wolfboot_signing_private_key.der"
WOLFBOOT_PUBLIC_KEY  = "/secure/path/wolfboot_signing_public_key.der"
WOLFBOOT_EXTRA_MAKE_FLAGS = "LIBERO_FPGA_CONFIG_DIR=/path/to/boards/mpfs-video-kit/fpga_design_config"
# The M-mode image is soft-float lp64; the Linux toolchain's libgcc is lp64d
# and cannot be linked, and wolfBoot needs nothing from it on this target.
WOLFBOOT_NOSTDLIB = "1"
# The Microchip BSP builds its FIT with a gzip-compressed kernel.
WOLFBOOT_EXTRA_CONFIG_LINES = "GZIP=1"

# Match the config's SIGN / HASH.
WOLFBOOT_SIGN_ALGO = "ecc384"
WOLFBOOT_HASH_ALGO = "sha384"
KERNEL_PN:pn-wolfboot-signed-image = "linux-mchp"

EXTRA_IMAGEDEPENDS:append = " wolfboot wolfboot-signed-image"
WKS_FILE = "mpfs-wolfboot.wks"
WKS_FILE_DEPENDS:append = " wolfboot-signed-image"
# Releases before wrynose search <layer>/wic, not <layer>/files/wic, for .wks files.
WKS_SEARCH_PATH:prepend = "${WOLFSSL_LAYERDIR}/files/wic:"

Build with MACHINE=mpfs-video-kit bitbake mchp-base-image. The .wic is written to the SD card as-is; wolfboot.elf goes to eNVM with mpfsBootmodeProgrammer.jar --bootmode 1.

To build a wolfBoot branch that is not on wolfssl/wolfBoot master, point the fetcher at it instead of editing the recipe:

WOLFBOOT_GIT_URI = "git://github.com/<fork>/wolfBoot.git;protocol=https"
WOLFBOOT_GIT_BRANCH = "<branch>"
SRCREV_wolfboot = "<full commit sha on that branch>"
SRCREV_wolfssl  = "<the branch's lib/wolfssl submodule commit>"

Using an existing wolfSSL source tree

By default wolfboot.inc fetches a pinned wolfSSL (SRCREV_wolfssl) into lib/wolfssl alongside wolfBoot. To build wolfCrypt from a tree you already have instead, set an absolute path in local.conf:

WOLFBOOT_WOLFSSL_SRC = "/path/to/wolfssl"

This drops the wolfSSL entry from SRC_URI entirely — nothing is downloaded and SRCREV_wolfssl is unused. Both wolfboot_git.bb and wolfboot-keytools-native_git.bb pick it up, so the bootloader and the signing tools stay on one wolfCrypt version.

Things worth knowing:

  • It is a source tree, not a library. wolfBoot compiles the wolfCrypt sources into a -nostdlib bare-metal image; it cannot link the target libwolfssl.so that the wolfssl recipe builds. Point this at wolfSSL sources.
  • The tree is copied into ${WORKDIR} before use, by the do_stage_external_wolfssl task. wolfBoot writes its object files next to the wolfCrypt sources, so an in-place build would dirty your tree. The original may therefore be read-only or shared, but the copy is a full one — prefer a clean source tree over a working directory with a large .git and build output in it.
  • Prebuilt objects are excluded from that copy (*.o, *.a, *.lo, *.la, *.so*, .libs, .git). Without this, a natively configured tree (./configure && make) would hand the cross build host x86 objects that make considers newer than their sources.
  • Changes to the tree do trigger a rebuild. The recipe fingerprints every file's path, size and mtime into the task hashes, since BitBake would otherwise hash only the WOLFBOOT_WOLFSSL_SRC string and happily reuse a stale wolfboot.elf.
  • The path must be absolute. wolfBoot's Makefile abspaths WOLFBOOT_LIB_WOLFSSL only for its own default; a command-line override — how the recipe passes it — reaches every sub-makefile verbatim. The recipe rejects relative paths rather than let them resolve against whichever directory each sub-make runs in.

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.