-
Notifications
You must be signed in to change notification settings - Fork 34
wolfBoot: add Yocto/OE secure bootloader recipes #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dgarske
wants to merge
3
commits into
wolfSSL:master
Choose a base branch
from
dgarske:wolfboot_bb
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
dgarske marked this conversation as resolved.
|
||
| 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.*" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
dgarske marked this conversation as resolved.
|
||
| `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. | ||
|
|
||
|
dgarske marked this conversation as resolved.
|
||
| **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. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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} | ||
|
dgarske marked this conversation as resolved.
|
||
| } | ||
|
|
||
| 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 | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.