diff --git a/README.md b/README.md index 75cc656..7b74d57 100644 --- a/README.md +++ b/README.md @@ -784,7 +784,7 @@ This repository contains additional README files with detailed information: - [recipes-connectivity/README.md](recipes-connectivity/README.md) - BIND, OpenSSH, Socat - [recipes-support/README.md](recipes-support/README.md) - curl, libssh2, - strongSwan, tcpdump + strongSwan, tcpdump, libfcs (Altera Agilex 5 SDM) - [recipes-protocols/README.md](recipes-protocols/README.md) - net-snmp - [recipes-extended/README.md](recipes-extended/README.md) - rsyslog diff --git a/conf/layer.conf b/conf/layer.conf index 24ac926..ff5b2b4 100644 --- a/conf/layer.conf +++ b/conf/layer.conf @@ -55,6 +55,23 @@ BBFILES += "${LAYERDIR}/recipes-wolfssl/wolfssl/*.bb \ # xilinx-bootbin bbappend requires meta-xilinx-tools layer BBFILES_DYNAMIC += "xilinx-tools:${LAYERDIR}/recipes-bsp/bootbin/*.bbappend" +# SoCFPGA wolfBoot integration requires the Altera BSP layer. The dynamic +# collection keeps its modern BitBake syntax out of legacy builds where the +# Altera layer is absent. +BBFILES_DYNAMIC += "altera-bsp:${LAYERDIR}/recipes-bsp/u-boot-socfpga/*.bbappend" +# The Agilex 5 wolfBoot image uses an uncompressed FIT kernel. The GSRD +# kernel bbappend otherwise hard-codes LZMA, which wolfBoot does not load. +BBFILES_DYNAMIC += "altera-bsp:${LAYERDIR}/recipes-kernel/linux/*.bbappend" + +# Enable with WOLFSSL_ALTERA_FCS = "1" in the build configuration. +WOLFSSL_ALTERA_FCS ?= "0" +WOLFSSL_FCS_PROVIDER ?= "libfcs" +WOLFSSL_ALTERA_FCS_BBFILES = \ + "${LAYERDIR}/recipes-support/libfcs/libfcs_git.bb \ + ${LAYERDIR}/recipes-support/libfcs/wolfssl_%.bbappend \ + ${@'${LAYERDIR}/recipes-support/libfcs/gsrd-intel-fcs-lib_%.bbappend' if d.getVar('WOLFSSL_FCS_PROVIDER') == 'gsrd-intel-fcs-lib' else ''}" +BBFILES += "${@d.getVar('WOLFSSL_ALTERA_FCS_BBFILES') if d.getVar('WOLFSSL_ALTERA_FCS') == '1' else ''}" + # Uncomment if building bind with wolfSSL. #BBFILES += "${LAYERDIR}/recipes-connectivity/bind/*.bbappend" diff --git a/files/wic/agilex5-wolfboot.wks b/files/wic/agilex5-wolfboot.wks new file mode 100644 index 0000000..e6634e3 --- /dev/null +++ b/files/wic/agilex5-wolfboot.wks @@ -0,0 +1,12 @@ +# short-description: Agilex 5 SD image with wolfBoot A/B partitions +# long-description: Keeps the Altera FAT boot partition, initializes both +# wolfBoot raw firmware slots, and places the Linux root filesystem in p4. + +part --source bootimg-partition --ondisk mmcblk --fstype=vfat --mkfs-extraopts "-F 32" --label boot --active --align 1024 --fixed-size 128M --system-id b +# WOLFBOOT_IMAGE_VERSION is supplied by the Kas/image configuration so an +# update version cannot silently leave the A/B slots at version 1. +part --source rawcopy --sourceparams="file=image_v${WOLFBOOT_IMAGE_VERSION}_signed.bin" --ondisk mmcblk --fstype=none --align 1024 --fixed-size 200M +part --source rawcopy --sourceparams="file=image_v${WOLFBOOT_IMAGE_VERSION}_signed.bin" --ondisk mmcblk --fstype=none --align 1024 --fixed-size 200M +# Keep the rootfs cap: the Agilex SD contract reserves exactly 1.5 GiB for p4 +# and the resulting WIC geometry is part of the documented deployment flow. +part / --source rootfs --ondisk mmcblk --fstype=ext4 --label root --align 1024 --fixed-size 1500M diff --git a/recipes-bsp/u-boot-socfpga/u-boot-socfpga_%.bbappend b/recipes-bsp/u-boot-socfpga/u-boot-socfpga_%.bbappend new file mode 100644 index 0000000..da67abe --- /dev/null +++ b/recipes-bsp/u-boot-socfpga/u-boot-socfpga_%.bbappend @@ -0,0 +1,48 @@ +# Use wolfBoot as BL33 in the SoCFPGA TF-A FIT when explicitly enabled. +# +# The Altera BSP still builds U-Boot SPL so DDR, clocks, pinmux and the SD +# controller are initialized by the supported platform first stage. Only the +# U-Boot-proper blob in u-boot.itb is replaced. TF-A BL31 and the board DTB +# remain in their existing FIT nodes. + +WOLFBOOT_ENABLE ??= "0" + +python __anonymous() { + if (d.getVar('WOLFBOOT_ENABLE') or '') != '1': + return + + d.appendVar('DEPENDS', ' wolfboot') + depends = d.getVarFlag('do_compile', 'depends') or '' + d.setVarFlag('do_compile', 'depends', + depends + ' wolfboot:do_deploy') +} + +do_compile:prepend() { + if [ "${WOLFBOOT_ENABLE}" = "1" ]; then + fit_dtsi="${S}/arch/arm/dts/socfpga_soc64_fit-u-boot.dtsi" + if [ ! -f "$fit_dtsi" ]; then + bbfatal "SoCFPGA FIT description not found: $fit_dtsi" + fi + + # Keep the source tree pristine. The backup/trap covers both normal + # completion and compile failures, including workdirs reused from + # sstate, while still letting U-Boot's normal FIT build consume the + # generated description. + fit_dtsi_backup="${B}/socfpga_soc64_fit-u-boot.dtsi.orig" + install -m 0644 "$fit_dtsi" "$fit_dtsi_backup" + trap 'install -m 0644 "$fit_dtsi_backup" "$fit_dtsi"; rm -f "$fit_dtsi_backup"' EXIT + sed -i \ + -e 's/description = "U-Boot SoC64";/description = "wolfBoot secure boot";/' \ + -e 's/filename = "u-boot-nodtb.bin";/filename = "wolfboot.bin";/' \ + "$fit_dtsi" + wolfboot_payload="${DEPLOY_DIR_IMAGE}/wolfboot.bin" + if [ ! -f "$wolfboot_payload" ]; then + bbfatal "wolfBoot BL33 payload not found: $wolfboot_payload" + fi + + install -m 0644 "$wolfboot_payload" "${S}/wolfboot.bin" + install -d "${B}/${UBOOT_DEFCONFIG}" + install -m 0644 "$wolfboot_payload" \ + "${B}/${UBOOT_DEFCONFIG}/wolfboot.bin" + fi +} diff --git a/recipes-kernel/linux/linux-socfpga-lts_%.bbappend b/recipes-kernel/linux/linux-socfpga-lts_%.bbappend new file mode 100644 index 0000000..a7f272d --- /dev/null +++ b/recipes-kernel/linux/linux-socfpga-lts_%.bbappend @@ -0,0 +1,25 @@ +# wolfBoot's Agilex 5 FIT loader accepts an uncompressed kernel image. The +# GSRD kernel append creates Image.lzma unconditionally even though the machine +# configuration requests FIT_KERNEL_COMP_ALG = "none". Rebuild only the FIT +# payload after the GSRD deploy task when wolfBoot is enabled. + +do_deploy:append() { + if [ "${WOLFBOOT_ENABLE}" = "1" ]; then + fit_its="${B}/fit_${MACHINE_STRIP}_kernel.its" + if [ "${FPGA_CORE_PGM_ENABLE}" != "1" ]; then + fit_its="${B}/fit_${MACHINE_STRIP}_kernel_no_rbf.its" + fi + + if [ -f "${LINUXDEPLOYDIR}/Image" ] && [ -f "$fit_its" ]; then + cp "${LINUXDEPLOYDIR}/Image" "${B}/Image" + sed -i \ + -e 's#Image\.lzma#Image#g' \ + -e 's/compression = "lzma"/compression = "none"/g' \ + "$fit_its" + (cd "${B}" && mkimage -f "$(basename "$fit_its")" "${B}/kernel.itb") + install -m 0644 "${B}/kernel.itb" "${DEPLOYDIR}/kernel.itb" + else + bbfatal "wolfBoot FIT rebuild requires ${LINUXDEPLOYDIR}/Image and $fit_its" + fi + fi +} diff --git a/recipes-support/README.md b/recipes-support/README.md index bdcd5a1..bd027e4 100644 --- a/recipes-support/README.md +++ b/recipes-support/README.md @@ -86,3 +86,9 @@ Zeus, Dunfell, and Gatesgarth Then just compile the image that use's `tcpdumb` and include the `wolfSSL` package or preform `bitbake tcpdumb` + +libfcs (Altera Agilex 5 SDM crypto) +----- + +See [libfcs/agilex5/README.md](libfcs/agilex5/README.md) for the complete +Agilex 5 integration, image deployment, and on-target test procedure. diff --git a/recipes-support/libfcs/agilex5/README.md b/recipes-support/libfcs/agilex5/README.md new file mode 100644 index 0000000..1c7e82d --- /dev/null +++ b/recipes-support/libfcs/agilex5/README.md @@ -0,0 +1,305 @@ +# wolfSSL FCS offload on Altera Agilex 5 + +This guide builds wolfSSL with Secure Device Manager crypto offload, includes +the result in an Agilex 5 Linux image, deploys that image, and verifies the +packaged wolfCrypt test on the board. + +## Prerequisites + +- An Agilex 5 GSRD Yocto build configured for the exact board and release. +- This `meta-wolfssl` layer in `BBLAYERS`. +- A kernel exposing `/sys/kernel/fcs_sysfs`. +- An owner root key hash provisioned in the SDM. + +Provisioning is outside this layer's scope. Follow Altera's device-security +procedure and distinguish recoverable virtual-key programming from permanent +eFuse programming before changing a board. A virtual owner key is cleared when +the board loses power and must be reapplied before each cold-boot test. Once an +owner key is active, boot and FPGA configuration artifacts must be signed by +that owner. + +### Development-board virtual-owner provisioning + +Before a cold-boot FCS test, provision a recoverable SDM virtual owner key and +load the matching signed HPS image using the platform team’s Quartus/JTAG +procedure. Keep keys, signed RBF files, and machine-specific scripts outside +this repository; never use permanent eFuse provisioning for development tests. +Virtual provisioning is cleared by power loss, so it must be repeated before +each cold-boot test. Acceptance requires `/sys/kernel/fcs_sysfs` and a +`wolfcrypttest` result containing `ALTERA-FCS test passed!`; a skipped test or +SDM error 133 is not hardware validation. + +Run BitBake on a Linux build server with the memory and storage required by the +GSRD release. Do not run BitBake on the target board. + +The 26.1 GSRD source and board-specific build instructions are published in +Altera's +[DK-A5E013BM16AEA GSRD guide](https://altera-fpga.github.io/rel-26.1/embedded-designs/agilex-5/e-series/013B/gsrd/ug-gsrd-agx5e-013b/). +Install `python3-venv` and Kas as described there. If the build server cannot +install Python packages, the official Kas container is an alternative: + +```sh +docker pull ghcr.io/siemens/kas/kas:4.8 +``` + +The 26.1 GSRD kernel append uses Bash conditionals in a BitBake task that runs +under `/bin/sh`. Make those conditionals portable before building: + +```sh +sed -i \ + -e 's/if \[\[/if [/g' \ + -e 's/\]\]; then/]; then/g' \ + -e 's/" == "/" = "/g' \ + meta-altera-fpga/meta-altera-bsp/recipes-kernel/linux/\ +linux-socfpga-lts_%.bbappend +``` + +Without this correction, `linux-socfpga-lts:do_deploy` reports `[[: not +found` and selects a nonexistent `fit_agilex5_kernel_no_rbf.its` file. + +## Add the layer to an Agilex 5 GSRD build + +The 26.1 GSRD for the DK-A5E013BM16AEA uses Kas. Add `meta-wolfssl` to the +GSRD `kas.yml` or to a Kas configuration fragment: + +```yaml +header: + version: 17 + +repos: + meta-wolfssl: + url: https://github.com/wolfSSL/meta-wolfssl.git + branch: master + layers: + .: + +local_conf_header: + wolfssl-altera-fcs: | + WOLFSSL_ALTERA_FCS = "1" + WOLFSSL_FCS_PROVIDER = "gsrd-intel-fcs-lib" + IMAGE_INSTALL:append = " wolfssl wolfcrypttest wolfcryptbenchmark " +``` + +The same fragment is supplied as `agilex5/kas-wolfssl.yml`. Kas +configurations can be combined without changing the GSRD's supplied +`kas.yml`: + +```sh +cp meta-wolfssl/recipes-support/libfcs/agilex5/kas-wolfssl.yml ./kas-wolfssl.yml +kas build kas.yml:kas/image/gsrd-console-image.yaml:kas-wolfssl.yml +``` + +Kas requires concatenated files to come from one checkout. Keep the copied +file local and untracked; do not commit private signing settings or generated +machine paths. + +The validation fragment pins the matching wolfSSL Agilex 5 port commit while +that wolfSSL change is under review. After the wolfSSL port is merged, replace +the temporary fork URL and revision with the corresponding upstream commit; +do not silently test a stock wolfSSL release and call it an offload build. + +For a local `meta-wolfssl` checkout under the GSRD Yocto directory, replace the +repository URL and branch with: + +```yaml + meta-wolfssl: + path: meta-wolfssl + layers: + .: +``` + +The 26.1 GSRD already provides the required headers and versioned runtime +library through `gsrd-intel-fcs-lib`. This layer adds the unversioned +`libFCS.so` linker name required by dependent recipes. Selecting the GSRD +provider prevents two recipes from installing the same library. A non-GSRD +build can omit the override and use meta-wolfssl's `libfcs` recipe instead. + +```bitbake +WOLFSSL_FCS_PROVIDER = "gsrd-intel-fcs-lib" +``` + +Use only one provider for `libFCS.so`. + +## Build the Agilex 5 image + +From the 26.1 DK-A5E013BM16AEA GSRD `software/yocto_linux` directory, build the +same `gsrd-console-image` target documented by Altera: + +```sh +source venv/bin/activate +kas build kas.yml:kas-wolfssl.yml gsrd-console-image +``` + +With the Kas container, run the equivalent command from the same directory: + +```sh +docker run --rm --user "$(id -u):$(id -g)" \ + -e HOME=/work -v "$PWD:/work" -w /work \ + ghcr.io/siemens/kas/kas:4.8 \ + build kas.yml:kas-wolfssl.yml gsrd-console-image +``` + +The expected SD card image is: + +```text +build/tmp/deploy/images/agilex5e_013b/gsrd-console-image-agilex5e_013b.rootfs.wic +``` + +Fail the validation if that file is absent or empty: + +```sh +test -s build/tmp/deploy/images/agilex5e_013b/\ +gsrd-console-image-agilex5e_013b.rootfs.wic +sha256sum build/tmp/deploy/images/agilex5e_013b/\ +gsrd-console-image-agilex5e_013b.rootfs.wic +``` + +Inspect the partition table and the installed test from an initialized +OpenEmbedded shell: + +```sh +( + source poky/oe-init-build-env build + image=tmp/deploy/images/agilex5e_013b/\ +gsrd-console-image-agilex5e_013b.rootfs.wic + native="$(find "$PWD/tmp/work" -type d \ + -path '*/gsrd-console-image/*/recipe-sysroot-native' \ + -print -quit)" + test -n "$native" + wic ls -n "$native" "$image" + wolfcrypt_bins="$(wic ls -n "$native" "$image:2/usr/bin/")" + printf '%s\n' "$wolfcrypt_bins" + printf '%s\n' "$wolfcrypt_bins" | grep -q 'wolfcrypttest' + printf '%s\n' "$wolfcrypt_bins" | grep -q 'wolfcryptbenchmark' + oe-pkgdata-util find-path /usr/bin/wolfcrypttest +) +``` + +When the image was built with the Kas container, run the same checks through +`kas shell` so Yocto's native tools retain the `/work` path used at build time: + +```sh +docker run --rm --user "$(id -u):$(id -g)" \ + -e HOME=/work -v "$PWD:/work" -w /work \ + ghcr.io/siemens/kas/kas:4.8 \ + shell kas.yml:kas-wolfssl.yml -c ' + image=tmp/deploy/images/agilex5e_013b/\ +gsrd-console-image-agilex5e_013b.rootfs.wic + native="$(find "$PWD/tmp/work" -type d \ + -path "*/gsrd-console-image/*/recipe-sysroot-native" \ + -print -quit)" + test -n "$native" + wic ls -n "$native" "$image" + wolfcrypt_bins="$(wic ls -n "$native" "$image:2/usr/bin/")" + printf "%s\n" "$wolfcrypt_bins" + printf "%s\n" "$wolfcrypt_bins" | grep -q wolfcrypttest + printf "%s\n" "$wolfcrypt_bins" | grep -q wolfcryptbenchmark + oe-pkgdata-util find-path /usr/bin/wolfcrypttest + ' +``` + +The image must contain a FAT boot partition and an ext4 root partition. The +second `wic ls` command must show both `wolfcrypttest` and +`wolfcryptbenchmark`, and the package lookup must report `wolfssl`. + +Confirm that BitBake also staged the packaged test: + +```sh +test -n "$(find build/tmp/work \ + -path '*/wolfssl/*/packages-split/*/usr/bin/wolfcrypttest' \ + -print -quit)" +``` + +## Deploy the image + +Power off the board and remove its microSD card. Attach the card to the build +server, identify the whole device by its capacity, and unmount any mounted +partitions. Device names vary: USB readers commonly appear as `/dev/sdX`, while +built-in readers may appear as `/dev/mmcblkN`. The example below uses the +device observed on the build server; replace it only after checking `lsblk`: + +```sh +image=build/tmp/deploy/images/agilex5e_013b/\ +gsrd-console-image-agilex5e_013b.rootfs.wic +card=/dev/mmcblk0 + +lsblk -o NAME,SIZE,TYPE,TRAN,RM,MODEL,MOUNTPOINTS +test -b "$card" +for part in $(lsblk -lnpo NAME "$card" | tail -n +2); do + if findmnt -rn -S "$part" >/dev/null; then + sudo umount "$part" + fi +done + +sudo dd if="$image" of="$card" bs=4M status=progress conv=fsync +image_size=$(stat -Lc %s "$image") +sudo cmp -n "$image_size" "$image" "$card" && echo "WIC verified" +sync +sudo blockdev --flushbufs "$card" +``` + +Verify the destination with `lsblk` before writing. This operation replaces the +card contents. Do not use a disk containing the build server's root filesystem. +Use a spare card or make a full-device backup first if the existing image must +be recoverable. `cmp` is silent on success; do not remove the card unless it +returns zero and prints `WIC verified`. A built-in MMC reader may not implement +the `eject` command. Once the partitions are unmounted and `blockdev` has +flushed the device, it is safe to remove the card physically. + +If the target image enables a package manager and a compatible package feed, +an incremental update is also valid: + +```sh +bitbake wolfssl wolfcrypttest +bitbake package-index +``` + +Publish the generated package feed, refresh the target package index, and +install or upgrade `wolfssl` and `wolfcrypttest` with the target's package +manager. Do not copy a package from a different machine, tune, C library, or +Yocto release. + +## Verify on the board + +Boot the deployed image and confirm that its packaged files are present: + +```sh +test -x /usr/bin/wolfcrypttest +/lib/ld-linux-aarch64.so.1 --list /usr/bin/wolfcrypttest +test -e /sys/kernel/fcs_sysfs +``` + +The DHCP address can change after booting a replacement image. Use the serial +console, the DHCP server's leases, or a local subnet scan to find the target +rather than assuming its previous address is retained. + +The GSRD image does not install `ldd` by default. Invoking the AArch64 dynamic +loader with `--list` performs the same runtime dependency check without adding +a diagnostic package to the image. + +Run the installed test: + +```sh +/usr/bin/wolfcrypttest +``` + +The run must exit with status 0 and print: + +```text +ALTERA-FCS test passed! +``` + +The Altera subtests require successful hardware operations for RNG, SHA-256, +and AES. They cannot pass solely through wolfSSL software fallback. Device +resident ECDSA and ECDH keys and HMAC verification are also exercised. + +If only the Altera test fails, check SDM provisioning status `0x85`. Status +`0x84` indicates session exhaustion and requires a board power cycle. + +## Developer-only smoke test + +Copying `${B}/wolfcrypt/test/.libs/testwolfcrypt` directly to a running target +is useful while developing the recipe. It is not the final integration test +because it bypasses image construction, package installation, and runtime +dependency resolution. Use the packaged `/usr/bin/wolfcrypttest` flow above +for release validation. diff --git a/recipes-support/libfcs/agilex5/kas-wolfssl.yml b/recipes-support/libfcs/agilex5/kas-wolfssl.yml new file mode 100644 index 0000000..accfeae --- /dev/null +++ b/recipes-support/libfcs/agilex5/kas-wolfssl.yml @@ -0,0 +1,17 @@ +header: + version: 17 + +repos: + meta-wolfssl: + path: meta-wolfssl + layers: + .: + +local_conf_header: + wolfssl-altera-fcs: | + WOLFSSL_ALTERA_FCS = "1" + WOLFSSL_FCS_PROVIDER = "gsrd-intel-fcs-lib" + IMAGE_INSTALL:append = " wolfssl wolfcrypttest wolfcryptbenchmark" + # Use the matching wolfSSL Agilex 5 port until that change is merged. + SRC_URI:pn-wolfssl = "git://github.com/aidangarske/wolfssl.git;protocol=https;nobranch=1" + SRCREV:pn-wolfssl = "0ffd8501323463011dfd769967b847e7bcf12281" diff --git a/recipes-support/libfcs/gsrd-intel-fcs-lib_%.bbappend b/recipes-support/libfcs/gsrd-intel-fcs-lib_%.bbappend new file mode 100644 index 0000000..3296805 --- /dev/null +++ b/recipes-support/libfcs/gsrd-intel-fcs-lib_%.bbappend @@ -0,0 +1,13 @@ +# The Agilex 5 GSRD recipe installs only the versioned runtime library. Add the +# development linker name so dependent recipes can use -lFCS from their +# recipe-specific sysroots. +do_install:append() { + if [ -e ${D}${prefix}/lib/libFCS.so.3 ]; then + install -d ${D}${libdir} + if [ "${libdir}" = "${prefix}/lib" ]; then + ln -sfn libFCS.so.3 ${D}${libdir}/libFCS.so + else + ln -sfn ../lib/libFCS.so.3 ${D}${libdir}/libFCS.so + fi + fi +} diff --git a/recipes-support/libfcs/libfcs_git.bb b/recipes-support/libfcs/libfcs_git.bb new file mode 100644 index 0000000..69a11cf --- /dev/null +++ b/recipes-support/libfcs/libfcs_git.bb @@ -0,0 +1,48 @@ +SUMMARY = "Altera FPGA Crypto Services library" +DESCRIPTION = "Userspace library for the FPGA Crypto Services of Altera \ +SoCFPGA devices, exposing the Secure Device Manager crypto mailbox through \ +/sys/kernel/fcs_sysfs." +HOMEPAGE = "https://github.com/altera-fpga/libfcs" +LICENSE = "MIT-0" +LIC_FILES_CHKSUM = "file://LICENSE;md5=6f25b4c3a6d23285f956387ab54830ad" + +SRC_URI = "git://github.com/altera-fpga/libfcs.git;protocol=https;branch=main" +SRCREV = "87b4b726f4981be102fc8f09feab051fe3578334" +PV = "3.01+git" + +DEPENDS = "dtc" + +COMPATIBLE_HOST = "aarch64.*-linux" + +python () { + if d.getVar('UNPACKDIR', False): + d.setVar('S', '${UNPACKDIR}/${BP}') + else: + d.setVar('S', '${WORKDIR}/git') +} + +inherit cmake + +EXTRA_OECMAKE = "-DARCH=linux_aarch64" + +# upstream install() destinations are relative, so they nest under ${prefix} +wolfssl_fcs_fixup_install() { + if [ -d ${D}${prefix}${prefix} ]; then + cp -a ${D}${prefix}${prefix}/. ${D}${prefix}/ + rm -rf ${D}${prefix}${prefix} + fi + + # libfcs hardcodes /usr/lib. Move the library to Yocto's configured libdir, + # then supply the linker name expected by consumers such as wolfSSL. + if [ "${prefix}/lib" != "${libdir}" ] && \ + [ -e ${D}${prefix}/lib/libFCS.so.3 ]; then + install -d ${D}${libdir} + mv ${D}${prefix}/lib/libFCS.so.3 ${D}${libdir}/ + rmdir ${D}${prefix}/lib + fi + if [ -e ${D}${libdir}/libFCS.so.3 ]; then + ln -sfn libFCS.so.3 ${D}${libdir}/libFCS.so + fi +} + +do_install[postfuncs] += "wolfssl_fcs_fixup_install" diff --git a/recipes-support/libfcs/wolfssl_%.bbappend b/recipes-support/libfcs/wolfssl_%.bbappend new file mode 100644 index 0000000..8103bde --- /dev/null +++ b/recipes-support/libfcs/wolfssl_%.bbappend @@ -0,0 +1,24 @@ +# Altera Agilex 5 SDM crypto offload; needs an FCS provisioned device at +# runtime. GSRD stacks that already build the library can point this at +# their own recipe, e.g. WOLFSSL_FCS_PROVIDER = "gsrd-intel-fcs-lib". + +def wolfssl_fcs_is_target(d): + import bb + return (d.getVar('WOLFSSL_ALTERA_FCS') == '1' and + bb.utils.contains('OVERRIDES', 'class-target', True, False, d) and + (d.getVar('TARGET_ARCH') or '') == 'aarch64') + +WOLFSSL_FCS_CONFIGURE = "${@'--enable-alterafcs --enable-aesctr' if wolfssl_fcs_is_target(d) else ''}" +WOLFSSL_FCS_DEPENDS = "${@d.getVar('WOLFSSL_FCS_PROVIDER') if wolfssl_fcs_is_target(d) else ''}" + +EXTRA_OECONF += "${WOLFSSL_FCS_CONFIGURE}" +DEPENDS += "${WOLFSSL_FCS_DEPENDS}" + +python () { + import bb + if (d.getVar('WOLFSSL_ALTERA_FCS') == '1' and + bb.utils.contains('OVERRIDES', 'class-target', True, False, d) and + not d.getVar('MLPREFIX') and + (d.getVar('TARGET_ARCH') or '') != 'aarch64'): + bb.fatal('WOLFSSL_ALTERA_FCS requires an AArch64 target') +} diff --git a/recipes-wolfssl/wolfboot/README.md b/recipes-wolfssl/wolfboot/README.md index 8a489ee..6a0a35e 100644 --- a/recipes-wolfssl/wolfboot/README.md +++ b/recipes-wolfssl/wolfboot/README.md @@ -11,10 +11,16 @@ portable secure bootloader, as part of a Yocto / OpenEmbedded image. | `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`). +Companion BSP integrations replace only the platform BL33 payload when +`WOLFBOOT_ENABLE = "1"` is set in configuration: + +- `recipes-bsp/bootbin/xilinx-bootbin_%.bbappend` places `wolfboot.elf` + in the ZynqMP `BOOT.BIN`. +- `recipes-bsp/u-boot-socfpga/u-boot-socfpga_%.bbappend` places + `wolfboot.bin` in the Agilex 5 TF-A FIT while retaining U-Boot SPL, + BL31 and the board DTB. + +See `agilex5/README.md` for the tested Agilex 5 GSRD customer flow. ## Signing-key provisioning (required) @@ -77,6 +83,7 @@ Artifacts deployed to `tmp/deploy/images//`: - `BOOT.BIN` - FSBL + PMUFW + ATF + wolfBoot.elf - `wolfboot.elf` - bare-metal bootloader +- `wolfboot.bin` - flat BL33 payload - `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) @@ -164,18 +171,9 @@ two non-obvious points: 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. +## Provider selection + +The platform first-stage recipe remains the provider of +`virtual/bootloader`. This is intentional on both supported families. The +Xilinx recipe still creates `BOOT.BIN`, and the SoCFPGA recipe still creates +SPL plus `u-boot.itb`; wolfBoot replaces only their BL33 payload. diff --git a/recipes-wolfssl/wolfboot/agilex5/README.md b/recipes-wolfssl/wolfboot/agilex5/README.md new file mode 100644 index 0000000..0728088 --- /dev/null +++ b/recipes-wolfssl/wolfboot/agilex5/README.md @@ -0,0 +1,122 @@ +# Agilex 5 wolfBoot integration + +This integration targets the Altera Agilex 5 DK-A5E013BM16AEA, machine +`agilex5e_013b`, using the GSRD Yocto flow. It preserves the supported +platform boot chain: + +```text +SDM -> U-Boot SPL -> TF-A BL31/EL3 -> wolfBoot BL33/EL2 -> signed kernel FIT +``` + +U-Boot SPL continues to initialize DDR, clocks, resets, pinmux and SD6HC. +TF-A continues to own EL3, PSCI, GICv3 and the security-controller setup. +The SoCFPGA recipe replaces only the U-Boot-proper loadable in `u-boot.itb` +with `wolfboot.bin` at `0x80200000`. + +libfcs is not linked into wolfBoot. The wolfSSL, libfcs, `wolfcrypttest` and +`wolfcryptbenchmark` packages remain Linux software in the root filesystem. + +When wolfBoot is enabled, this layer regenerates the GSRD kernel FIT with an +uncompressed Linux payload. The stock GSRD deploy append creates an LZMA FIT; +wolfBoot deliberately accepts only the supported FIT compression modes, so the +image must use the uncompressed form selected by the Agilex 5 machine config. + +## Inputs + +Use the matching wolfSSL Agilex 5 port and meta-wolfSSL FCS fragment described +in `recipes-support/libfcs/agilex5/README.md`. Keep three key domains separate: + +1. SDM virtual owner key for Linux FCS requests. +2. Platform key used by the GSRD boot authentication flow, if enabled. +3. wolfBoot firmware-signing key used for the A/B Linux payloads. + +Never store private keys in this layer, the build directory or deploy output. +For development, generate a dedicated wolfBoot key pair outside the checkout +as described in the parent `README.md`, then set these absolute paths in a +private Kas fragment or `local.conf`: + +```bitbake +WOLFBOOT_SIGNING_KEY = "/secure/path/wolfboot_signing_private_key.der" +WOLFBOOT_PUBLIC_KEY = "/secure/path/wolfboot_signing_public_key.der" +``` + +Before a cold-boot FCS test, repeat the development-board virtual-owner +provisioning procedure in `recipes-support/libfcs/agilex5/README.md`. It uses +Quartus Programmer and a recoverable virtual key; it is separate from the +wolfBoot signing key and any permanent SDM/eFuse provisioning. + +## Build with Kas + +Append both meta-wolfSSL fragments to the normal GSRD build: + +```sh +cp meta-wolfssl/recipes-wolfssl/wolfboot/agilex5/kas-wolfboot.yml ./kas-wolfboot.yml +kas build kas.yml:kas/image/gsrd-console-image.yaml:kas-wolfboot.yml:wolfboot-secrets.yml gsrd-console-image +``` + +The wolfBoot fragment also enables the matching FCS provider and installs the +packaged wolfCrypt tests, so this is one customer image flow rather than two +independent layer configurations. Kas requires concatenated files to come +from one checkout; keep the copied fragment local and untracked, and keep +`wolfboot-secrets.yml` outside version control. + +The exact FCS fragment filename can differ in a downstream GSRD checkout. +Use the one documented by the libfcs Agilex guide. The wolfBoot fragment sets: + +- the Agilex 5 wolfBoot config and pinned source revision; +- Linux root device `/dev/mmcblk0p4`; +- `kernel.itb` as the signed wolfBoot payload; +- the four-partition WIC definition; +- task dependencies for wolfBoot, signing and WIC assembly. + +## Image layout + +The resulting WIC has this DOS partition table: + +| Partition | Size | Contents | +|---|---:|---| +| p1 | 128 MiB | FAT boot files, including SPL inputs and `u-boot.itb` | +| p2 | 200 MiB | raw wolfBoot A slot, initialized with version 1 | +| p3 | 200 MiB | raw wolfBoot B slot, initialized with version 1 | +| p4 | 1500 MiB | ext4 Linux root filesystem | + +Both A and B are initialized so a first-boot fallback never selects an empty +slot. Subsequent update images must increment `WOLFBOOT_IMAGE_VERSION`. + +## Preflash validation + +Before writing removable media, record the revisions and image hash, then +inspect the artifacts: + +```sh +# GSRD separates libc-specific output under tmp-glibc. +deploy=build/tmp-glibc/deploy/images/agilex5e_013b +fdtdump "$deploy/u-boot.itb" +fdisk -l "$deploy/gsrd-console-image-agilex5e_013b.rootfs.wic" +sha256sum "$deploy/gsrd-console-image-agilex5e_013b.rootfs.wic" +``` + +Require the FIT to retain its `atf` and `fdt-0` nodes, identify the BL33 +loadable as wolfBoot, and keep its load address at `0x80200000`. Require four +partitions with Linux in p4. Also inspect both raw slots against the signed +image using byte-limited comparisons at their exact WIC offsets. + +## Hardware acceptance + +Flash only after resolving the removable card by model, size and transport. +Compare the complete WIC byte span after writing it, flush buffers, safely +remove the card, then cold boot the board. + +Acceptance requires all of the following: + +1. SPL loads the unchanged TF-A firmware node and TF-A enters wolfBoot at EL2. +2. wolfBoot verifies and boots the signed A image. +3. Linux mounts `/dev/mmcblk0p4`, brings all four CPUs online and performs a + PSCI reboot successfully. +4. `/usr/bin/wolfcrypttest` exits zero and prints + `ALTERA-FCS test passed!` after virtual owner-key provisioning. +5. A disposable-card test with only slot A corrupted boots the valid B slot. + +Do not claim secure-boot validation from a software-only CI build. CI proves +configuration, compilation and no-hardware fallback behavior; the exact WIC +and physical board prove the boot and FCS contracts. diff --git a/recipes-wolfssl/wolfboot/agilex5/kas-wolfboot.yml b/recipes-wolfssl/wolfboot/agilex5/kas-wolfboot.yml new file mode 100644 index 0000000..d99d2db --- /dev/null +++ b/recipes-wolfssl/wolfboot/agilex5/kas-wolfboot.yml @@ -0,0 +1,32 @@ +header: + version: 17 + +repos: + meta-wolfssl: + path: meta-wolfssl + layers: + .: + +local_conf_header: + wolfssl-altera-fcs: | + WOLFSSL_ALTERA_FCS = "1" + WOLFSSL_FCS_PROVIDER = "gsrd-intel-fcs-lib" + IMAGE_INSTALL:append = " wolfssl wolfcrypttest wolfcryptbenchmark" + SRC_URI:pn-wolfssl = "git://github.com/aidangarske/wolfssl.git;protocol=https;nobranch=1" + SRCREV:pn-wolfssl = "0ffd8501323463011dfd769967b847e7bcf12281" + + wolfboot-agilex5: | + WOLFBOOT_ENABLE = "1" + WOLFBOOT_CONFIG = "agilex5_013b_sdcard.config" + WOLFBOOT_LINUX_BOOTARGS_ROOT = "/dev/mmcblk0p4" + WOLFBOOT_IMAGE_VERSION = "1" + WOLFBOOT_FIT_IMAGE = "kernel.itb" + KERNEL_PN = "linux-socfpga-lts" + + WKS_FILE = "agilex5-wolfboot.wks" + EXTRA_IMAGEDEPENDS:append = " wolfboot wolfboot-signed-image" + do_image_wic[depends] += "wolfboot:do_deploy wolfboot-signed-image:do_deploy" + + WOLFBOOT_REPO = "git://github.com/aidangarske/wolfBoot.git" + WOLFBOOT_BRANCH = "agilex5-tfa-port" + SRCREV_wolfboot:pn-wolfboot = "69dcac173cd9c5bc18e11ef542d87d574b1303f7" diff --git a/recipes-wolfssl/wolfboot/wolfboot-signed-image.bb b/recipes-wolfssl/wolfboot/wolfboot-signed-image.bb index 21209db..6492520 100644 --- a/recipes-wolfssl/wolfboot/wolfboot-signed-image.bb +++ b/recipes-wolfssl/wolfboot/wolfboot-signed-image.bb @@ -37,6 +37,10 @@ WOLFBOOT_IMAGE_VERSION ?= "1" # linux-xlnx on ZynqMP / Versal. WOLFBOOT_FIT_IMAGE ?= "fitImage" +# wolfboot-sign removes the input filename extension when naming its output +# (for example, kernel.itb becomes kernel_v1_signed.bin). +WOLFBOOT_SIGNED_BASENAME ?= "${@d.getVar('WOLFBOOT_FIT_IMAGE').rsplit('.', 1)[0]}" + # 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() { @@ -74,8 +78,8 @@ do_compile() { # 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 _v_signed.bin. Run from ${B} with a - # relative path so the output lands inside ${B} predictably. + # naming it _v_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} \ @@ -87,7 +91,7 @@ do_install[noexec] = "1" do_deploy() { install -d ${DEPLOYDIR} - install -m 0644 ${B}/${WOLFBOOT_FIT_IMAGE}_v${WOLFBOOT_IMAGE_VERSION}_signed.bin \ + install -m 0644 ${B}/${WOLFBOOT_SIGNED_BASENAME}_v${WOLFBOOT_IMAGE_VERSION}_signed.bin \ ${DEPLOYDIR}/image_v${WOLFBOOT_IMAGE_VERSION}_signed.bin } diff --git a/recipes-wolfssl/wolfboot/wolfboot.inc b/recipes-wolfssl/wolfboot/wolfboot.inc index b3970e2..2b90861 100644 --- a/recipes-wolfssl/wolfboot/wolfboot.inc +++ b/recipes-wolfssl/wolfboot/wolfboot.inc @@ -16,9 +16,16 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=1ebbd3e34237af26da5dc08a4e440464" # upstream evolves. Downstream users can override via local.conf: # SRCREV_wolfboot:pn-wolfboot = "" # SRCREV_wolfboot:pn-wolfboot-keytools-native = "" +WOLFBOOT_REPO ?= "git://github.com/wolfssl/wolfBoot.git" +WOLFBOOT_BRANCH ?= "master" +WOLFBOOT_PROTOCOL ?= "https" +WOLFBOOT_WOLFSSL_REPO ?= "git://github.com/wolfssl/wolfssl.git" +WOLFBOOT_WOLFSSL_BRANCH ?= "master" +WOLFBOOT_WOLFSSL_PROTOCOL ?= "https" + 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 \ + ${WOLFBOOT_REPO};protocol=${WOLFBOOT_PROTOCOL};branch=${WOLFBOOT_BRANCH};name=wolfboot;destsuffix=git \ + ${WOLFBOOT_WOLFSSL_REPO};protocol=${WOLFBOOT_WOLFSSL_PROTOCOL};branch=${WOLFBOOT_WOLFSSL_BRANCH};name=wolfssl;destsuffix=git/lib/wolfssl \ " SRCREV_wolfboot ?= "9a667f2a7527da2b8e490ae7923665321af2d3ac" SRCREV_wolfssl ?= "1d363f3adceba9d1478230ede476a37b0dcdef24" diff --git a/recipes-wolfssl/wolfboot/wolfboot_git.bb b/recipes-wolfssl/wolfboot/wolfboot_git.bb index 182be2e..e1c1972 100644 --- a/recipes-wolfssl/wolfboot/wolfboot_git.bb +++ b/recipes-wolfssl/wolfboot/wolfboot_git.bb @@ -1,9 +1,8 @@ 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)." +update and rollback support. On supported Arm platforms it runs as BL33 \ +after the platform first stage and TF-A." require wolfboot.inc @@ -33,6 +32,10 @@ WOLFBOOT_SIGNING_KEY ?= "" # want to pin a public key independent of the private key file (e.g. HSM). WOLFBOOT_PUBLIC_KEY ?= "" +# Algorithm used to turn WOLFBOOT_PUBLIC_KEY into wolfBoot's compiled-in +# keystore. Keep this aligned with wolfboot-signed-image.bb. +WOLFBOOT_KEYGEN_OPTIONS ?= "--rsa4096" + # 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). @@ -100,7 +103,7 @@ do_compile() { ${S}/.config fi - # Cross-compile wolfboot.elf. + # Cross-compile wolfboot.elf and its flat binary payload. # 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 @@ -128,12 +131,20 @@ do_compile() { 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. + # Generate src/keystore.c before the target build. Although wolfBoot's + # Makefile accepts KEYGEN_TOOL, its user-public-key rule still depends on + # keytools_check, which would cross-build host tools with TARGET_PREFIX. + # Running the native tool here makes that rule up to date and prevents a + # target binary from being built or executed on the build host. NATIVE_KEYGEN="$(command -v wolfboot-keygen)" - make wolfboot.elf \ + if [ -z "$NATIVE_KEYGEN" ] || [ ! -x "$NATIVE_KEYGEN" ]; then + bbfatal "wolfboot-keygen was not found in PATH; ensure wolfboot-keytools-native is available" + fi + cd ${S} + "$NATIVE_KEYGEN" ${WOLFBOOT_KEYGEN_OPTIONS} --force \ + -i "$PUBKEY_FOR_MAKE" + + make wolfboot.bin wolfboot.elf \ CROSS_COMPILE=${TARGET_PREFIX} \ CC="${TARGET_PREFIX}gcc $SYSROOT_FLAG" \ LD="${TARGET_PREFIX}gcc $SYSROOT_FLAG" \ @@ -145,15 +156,17 @@ do_compile() { } do_install() { - # Install wolfboot.elf into sysroot for xilinx-bootbin BIF consumption - # (BIF_PARTITION_IMAGE[wolfboot] points at ${RECIPE_SYSROOT}/boot/wolfboot.elf). + # The ELF is consumed by Xilinx bootgen. The flat binary is consumed by + # the SoCFPGA FIT generator as its BL33 loadable. install -d ${D}/boot install -m 0644 ${S}/wolfboot.elf ${D}/boot/wolfboot.elf + install -m 0644 ${S}/wolfboot.bin ${D}/boot/wolfboot.bin } do_deploy() { install -d ${DEPLOYDIR} install -m 0644 ${S}/wolfboot.elf ${DEPLOYDIR}/wolfboot.elf + install -m 0644 ${S}/wolfboot.bin ${DEPLOYDIR}/wolfboot.bin # 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 @@ -176,7 +189,7 @@ addtask deploy before do_build after do_compile # overrides and cannot parse the colon syntax. python __anonymous() { wolfssl_varSet(d, 'INSANE_SKIP', '${PN}', 'ldflags textrel buildpaths') - wolfssl_varSet(d, 'FILES', '${PN}', '/boot/wolfboot.elf') + wolfssl_varSet(d, 'FILES', '${PN}', '/boot/wolfboot.elf /boot/wolfboot.bin') } SYSROOT_DIRS += "/boot"