diff --git a/recipes-wolfssl/wolfboot/README.md b/recipes-wolfssl/wolfboot/README.md index 8a489ee..c750df0 100644 --- a/recipes-wolfssl/wolfboot/README.md +++ b/recipes-wolfssl/wolfboot/README.md @@ -83,6 +83,47 @@ Artifacts deployed to `tmp/deploy/images//`: Note: the private signing key is **not** deployed — it stays on the workstation / secrets store you pointed `WOLFBOOT_SIGNING_KEY` at. +## 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 | diff --git a/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb b/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb index 12032f0..31e63c7 100644 --- a/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb +++ b/recipes-wolfssl/wolfboot/wolfboot-keytools-native_git.bb @@ -1,8 +1,8 @@ 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." +SHA3-384 hashes. Uses wolfBoot's bundled wolfCrypt (under lib/wolfssl) by \ +default, or the tree named by WOLFBOOT_WOLFSSL_SRC when that is set." require wolfboot.inc @@ -12,11 +12,23 @@ do_configure[noexec] = "1" do_compile() { # Build the keytools (host-side signing/keygen utilities). + # + # Track wolfboot_git.bb's choice of wolfCrypt: the keytools produce the + # keystore and the image signatures that wolfboot.elf then verifies, so + # building the two halves from different wolfSSL versions risks a format + # mismatch that only shows up as a failed verification on the target. + # tools/keytools/Makefile also emits its objects under + # $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src, hence the staged copy here too. + WOLFSSL_DIR="${S}/lib/wolfssl" + if [ -n "${WOLFBOOT_WOLFSSL_SRC}" ]; then + WOLFSSL_DIR="${WOLFBOOT_WOLFSSL_STAGED_SRC}" + fi + oe_runmake -C tools/keytools \ CC="${CC}" \ LD="${CC}" \ WOLFBOOTDIR=${S} \ - WOLFBOOT_LIB_WOLFSSL=${S}/lib/wolfssl \ + WOLFBOOT_LIB_WOLFSSL="$WOLFSSL_DIR" \ V=1 } diff --git a/recipes-wolfssl/wolfboot/wolfboot.inc b/recipes-wolfssl/wolfboot/wolfboot.inc index b3970e2..462b3fd 100644 --- a/recipes-wolfssl/wolfboot/wolfboot.inc +++ b/recipes-wolfssl/wolfboot/wolfboot.inc @@ -3,7 +3,8 @@ # 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). +# explicit and greppable). Set WOLFBOOT_WOLFSSL_SRC to build against an +# existing wolfSSL source tree instead, see the block below. HOMEPAGE = "https://github.com/wolfssl/wolfBoot" BUGTRACKER = "https://github.com/wolfssl/wolfBoot/issues" @@ -11,6 +12,26 @@ SECTION = "bootloaders" LICENSE = "GPL-3.0-only" LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464" +# --- Optional: build against an existing wolfSSL source tree ---------------- +# +# Set WOLFBOOT_WOLFSSL_SRC to the absolute path of a wolfSSL source tree to +# build wolfCrypt from it instead of the pinned copy fetched below. When it is +# set the wolfSSL entry drops out of SRC_URI entirely and nothing is downloaded +# making SRCREV_wolfssl unused. +# +# NOTE: this reuses a source *tree*, not a built library. wolfBoot compiles the +# wolfCrypt sources itself into a -nostdlib bare-metal image; it can never link +# against the target libwolfssl.so that the wolfssl recipe produces. +WOLFBOOT_WOLFSSL_SRC ?= "" + +# Private copy of that tree, made by do_stage_external_wolfssl below. The copy +# is not an optimisation: wolfBoot compiles objects *alongside* the wolfCrypt +# sources ($(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o, in both options.mk and +# tools/keytools/Makefile), so building in place would write into the caller's +# tree -- and race with it if it belongs to another recipe. Copying also lets +# WOLFBOOT_WOLFSSL_SRC live somewhere read-only or shared. +WOLFBOOT_WOLFSSL_STAGED_SRC = "${WORKDIR}/wolfssl-external" + # 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: @@ -18,11 +39,93 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464" # SRCREV_wolfboot:pn-wolfboot-keytools-native = "" 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 \ + ${@'' if d.getVar('WOLFBOOT_WOLFSSL_SRC') else 'git://github.com/wolfssl/wolfssl.git;protocol=https;branch=master;name=wolfssl;destsuffix=git/lib/wolfssl'} \ " SRCREV_wolfboot ?= "9a667f2a7527da2b8e490ae7923665321af2d3ac" SRCREV_wolfssl ?= "1d363f3adceba9d1478230ede476a37b0dcdef24" -SRCREV_FORMAT = "wolfboot_wolfssl" +SRCREV_FORMAT = "${@'wolfboot' if d.getVar('WOLFBOOT_WOLFSSL_SRC') else 'wolfboot_wolfssl'}" + +def wolfboot_wolfssl_src_id(d): + """Fingerprint the external wolfSSL tree: relative path, size and mtime of + every file. Fed into the task hashes below so that editing the tree rebuilds + wolfBoot. BitBake otherwise hashes only the WOLFBOOT_WOLFSSL_SRC *string*, + and changes to its contents would silently reuse a stale wolfboot.elf. + Returns '' (and costs nothing) when the feature is not in use.""" + import os + import hashlib + + src = d.getVar('WOLFBOOT_WOLFSSL_SRC') + if not src or not os.path.isdir(src): + return '' + + h = hashlib.sha256() + for root, dirs, files in os.walk(src): + # Same pruning as the copy below, so the fingerprint tracks exactly + # what gets staged. + dirs[:] = sorted(x for x in dirs if x not in ('.git', '.libs')) + for name in sorted(files): + path = os.path.join(root, name) + try: + st = os.lstat(path) + except OSError: + continue + entry = '%s %d %d\n' % (os.path.relpath(path, src), + st.st_size, st.st_mtime_ns) + h.update(entry.encode()) + return h.hexdigest() + +WOLFBOOT_WOLFSSL_SRC_ID = "${@wolfboot_wolfssl_src_id(d)}" + +python check_wolfboot_wolfssl_src() { + import os + + src = d.getVar('WOLFBOOT_WOLFSSL_SRC') or '' + if not src: + return + if not os.path.isabs(src): + bb.fatal("WOLFBOOT_WOLFSSL_SRC='%s' must be an absolute path. wolfBoot's " + "Makefile only abspaths WOLFBOOT_LIB_WOLFSSL when the value " + "comes from a makefile; the recipe passes it on the make " + "command line, which overrides that assignment, so a relative " + "path reaches every sub-makefile verbatim and resolves against " + "whichever working directory each one runs in." % src) + if not os.path.isdir(os.path.join(src, 'wolfcrypt', 'src')): + bb.fatal("WOLFBOOT_WOLFSSL_SRC='%s' does not look like a wolfSSL source " + "tree (no wolfcrypt/src directory)." % src) +} + +do_stage_external_wolfssl() { + if [ -z "${WOLFBOOT_WOLFSSL_SRC}" ]; then + return 0 + fi + + rm -rf "${WOLFBOOT_WOLFSSL_STAGED_SRC}" + mkdir -p "${WOLFBOOT_WOLFSSL_STAGED_SRC}" + + # Prebuilt objects are excluded so a natively-configured wolfSSL tree + # (./configure && make) cannot leak host x86 .o/.a files into the cross + # build: wolfBoot's object paths sit inside this tree, and make would + # happily reuse an existing .o that is newer than its .c. + tar -cf - -C "${WOLFBOOT_WOLFSSL_SRC}" \ + --exclude=.git --exclude=.libs \ + --exclude='*.o' --exclude='*.lo' --exclude='*.a' --exclude='*.la' \ + --exclude='*.so' --exclude='*.so.*' \ + . | tar -xf - -C "${WOLFBOOT_WOLFSSL_STAGED_SRC}" + + # Guard against a half-copied tree: the pipeline above reports only the + # extract side's exit status under a plain POSIX shell. + if [ ! -f "${WOLFBOOT_WOLFSSL_STAGED_SRC}/wolfcrypt/src/asn.c" ]; then + bbfatal "Failed to stage WOLFBOOT_WOLFSSL_SRC='${WOLFBOOT_WOLFSSL_SRC}':" \ + "wolfcrypt/src/asn.c is missing from the copy at" \ + "${WOLFBOOT_WOLFSSL_STAGED_SRC}." + fi + + chmod -R u+w "${WOLFBOOT_WOLFSSL_STAGED_SRC}" +} +do_stage_external_wolfssl[prefuncs] += "check_wolfboot_wolfssl_src" +do_stage_external_wolfssl[vardeps] += "WOLFBOOT_WOLFSSL_SRC_ID" +do_compile[vardeps] += "WOLFBOOT_WOLFSSL_SRC_ID" +addtask stage_external_wolfssl after do_unpack before do_compile python () { if d.getVar('UNPACKDIR', False): diff --git a/recipes-wolfssl/wolfboot/wolfboot_git.bb b/recipes-wolfssl/wolfboot/wolfboot_git.bb index 182be2e..b39b8ae 100644 --- a/recipes-wolfssl/wolfboot/wolfboot_git.bb +++ b/recipes-wolfssl/wolfboot/wolfboot_git.bb @@ -133,6 +133,21 @@ do_compile() { # 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)" + + # Build wolfCrypt from a caller-supplied wolfSSL tree when asked. + # WOLFBOOT_LIB_WOLFSSL is wolfBoot's way to set an external wolfSSL source + # location. Leaving it unset keeps the in-tree lib/wolfssl fetched + # by wolfboot.inc. Always point it at the WORKDIR copy staged by + # do_stage_external_wolfssl, never at the caller's tree because the build + # needs to write object files into this directory. Unquoted below so it + # vanishes when empty. + WOLFSSL_LIB_ARG="" + if [ -n "${WOLFBOOT_WOLFSSL_SRC}" ]; then + WOLFSSL_LIB_ARG="WOLFBOOT_LIB_WOLFSSL=${WOLFBOOT_WOLFSSL_STAGED_SRC}" + bbnote "wolfBoot: building wolfCrypt from ${WOLFBOOT_WOLFSSL_SRC}" \ + "(staged at ${WOLFBOOT_WOLFSSL_STAGED_SRC})" + fi + make wolfboot.elf \ CROSS_COMPILE=${TARGET_PREFIX} \ CC="${TARGET_PREFIX}gcc $SYSROOT_FLAG" \ @@ -140,6 +155,7 @@ do_compile() { USER_PRIVATE_KEY="${WOLFBOOT_SIGNING_KEY}" \ USER_PUBLIC_KEY="$PUBKEY_FOR_MAKE" \ KEYGEN_TOOL="$NATIVE_KEYGEN" \ + $WOLFSSL_LIB_ARG \ ${WOLFBOOT_EXTRA_MAKE_FLAGS} \ V=1 }