From: Michelle Lin <[email protected]> This class calls systemd ukify tool, which will combine kernel/initrd/stub components to build the UKI. To sign the UKI (i.e. SecureBoot), the keys/cert files can be specified in a configuration file or UEFI binary signing can be done via separate steps, see qemuarm64-secureboot in meta-arm. UKIs are loaded by UEFI firmware on target which can improve security by loading only correctly signed kernel, initrd and kernel command line.
Using systemd-measure to pre-calculate TPM PCR values and sign them is not supported since that requires a TPM device on the build host. Thus "ConditionSecurity=measured-uki" default from systemd 256 does not work but "ConditionSecurity=tpm2" in combination with secure boot will. These can be used to boot securely into systemd-boot, kernel, kernel command line and initrd which then securely mounts a read-only dm-verity /usr partition and creates a TPM encrypted read-write / rootfs. Tested via qemuarm64-secureboot in meta-arm with https://lists.yoctoproject.org/g/meta-arm/topic/patch_v3_02_13/108031399 and a few more changes needed, will be posted separately. Signed-off-by: Michelle Lin <[email protected]> Cc: Erik Schilling <[email protected]> Signed-off-by: Mikko Rapeli <[email protected]> --- meta/classes-recipe/uki.bbclass | 158 ++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 meta/classes-recipe/uki.bbclass diff --git a/meta/classes-recipe/uki.bbclass b/meta/classes-recipe/uki.bbclass new file mode 100644 index 0000000000..472eb476a0 --- /dev/null +++ b/meta/classes-recipe/uki.bbclass @@ -0,0 +1,158 @@ +# Unified kernel image (UKI) class +# +# This bbclass merges kernel, initrd etc as a UKI standard UEFI binary, +# to be loaded with UEFI firmware on target. SecureBoot signing is +# supported via add ons. TPM PCR pre-calculation is not supported since +# systemd-measure tooling is meant to run on target, not in cross compile +# environment. +# +# See: +# https://www.freedesktop.org/software/systemd/man/latest/ukify.html +# https://uapi-group.org/specifications/specs/unified_kernel_image/ +# +# The UKI is composed from +# - an UEFI stub +# The linux kernel can generate a UEFI stub, however the one from systemd-boot can fetch +# the command line from a separate section of the EFI application, avoiding the need to +# rebuild the kernel. +# - the kernel +# - an initramfs +# - other metadata (e.g. PCR measurements) +# +# Usage instructions: +# - requires UEFI compatible firmware on target, e.g. qemuarm64-secureboot from meta-arm +# - Distro config: +# INIT_MANAGER = "systemd" +# DISTRO_FEATURES += "systemd" +# DISTRO_FEATURES_NATIVE += "systemd" +# DISTRO_FEATURES += "efi" +# DISTRO_FEATURES += "uki" +# INITRAMFS_IMAGE ?= "core-image-minimal-initramfs" +# HOSTTOOLS += "getent ping" +# EFI_PROVIDER = "systemd-boot" +# - image recipe: +# INHERIT_UKI = "${@bb.utils.contains('DISTRO_FEATURES', 'uki', 'uki', '', d)}" +# inherit ${INHERIT_UKI} +# - qemuboot/runqemu changes in image recipe: +# # Detected by passing kernel parameter +# QB_KERNEL_ROOT = "" +# # kernel is in the image, should not be loaded separately +# QB_DEFAULT_KERNEL = "none" +# - for UEFI secure boot, systemd-boot, uki and linux kernel need +# to be signed with sbsign (recipe available from meta-secure-core, +# see also qemuarm64-secureboot from meta-arm) + +DEPENDS += "\ + systemd \ + systemd-boot \ + systemd-tools-native \ + virtual/${TARGET_PREFIX}binutils \ + virtual/kernel \ +" + +REQUIRED_DISTRO_FEATURES += "usrmerge systemd uki" + +inherit features_check image-artifact-names +require ../conf/image-uefi.conf + +INITRAMFS_IMAGE ?= "core-image-minimal-initramfs" + +INITRD_ARCHIVE ?= "${INITRAMFS_IMAGE}-${MACHINE}.${INITRAMFS_FSTYPES}" + +do_image_complete[depends] += "${INITRAMFS_IMAGE}:do_image_complete" + +UKIFY_CMD ?= "ukify build" +UKI_CONFIG_FILE ?= "${UNPACKDIR}/uki.conf" +UKI_FILENAME ?= "uki.efi" +UKI_CMDLINE ?= "rootwait root=/dev/vda2" + +IMAGE_EFI_BOOT_FILES ?= "${UKI_FILENAME};EFI/Linux/${UKI_FILENAME}" + +do_uki[depends] += " \ + systemd-boot:do_deploy \ + virtual/kernel:do_deploy \ + " +do_uki[depends] += "${@ '${INITRAMFS_IMAGE}:do_image_complete' if d.getVar('INITRAMFS_IMAGE') else ''}" + +# ensure that the build directory is empty everytime we generate a newly-created uki +do_uki[cleandirs] = "${B}" +# influence the build directory at the start of the builds +do_uki[dirs] = "${B}" + +# we want to allow specifying files in SRC_URI, such as for signing the UKI +python () { + d.delVarFlag("do_fetch","noexec") + d.delVarFlag("do_unpack","noexec") +} + +# main task +python do_uki() { + import glob + import bb.process + + # Construct the ukify command + ukify_cmd = d.getVar('UKIFY_CMD') + + deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE') + + # initrd + initramfs_image = "%s" % (d.getVar('INITRD_ARCHIVE')) + ukify_cmd += " --initrd=%s" % os.path.join(deploy_dir_image, initramfs_image) + + deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE') + + # Kernel + if d.getVar('KERNEL_IMAGETYPE'): + kernel = "%s/%s" % (deploy_dir_image, d.getVar('KERNEL_IMAGETYPE')) + kernel_version = d.getVar('KERNEL_VERSION') + if not os.path.exists(kernel): + bb.fatal(f"ERROR: cannot find {kernel}.") + + ukify_cmd += " --linux=%s --uname %s" % (kernel, kernel_version) + else: + bb.fatal("ERROR - Required argument: KERNEL") + + # Command line + cmdline = d.getVar('UKI_CMDLINE') + if cmdline: + ukify_cmd += " --cmdline='%s'" % cmdline + + # Architecture + target_arch = d.getVar('EFI_ARCH') + if target_arch: + ukify_cmd += " --efi-arch %s" % target_arch + + # systemd stubs from deploy + stub = "%s/linux%s.efi.stub" % (d.getVar('DEPLOY_DIR_IMAGE'), target_arch) + if not os.path.exists(stub): + bb.fatal(f"ERROR: cannot find {stub}.") + ukify_cmd += " --stub %s" % stub + + # Add option for dtb + if d.getVar('KERNEL_DEVICETREE'): + for dtb in d.getVar('KERNEL_DEVICETREE').split(): + dtb_path = "%s/%s" % (deploy_dir_image, dtb) + if not os.path.exists(dtb_path): + bb.fatal(f"ERROR: cannot find {dtb_path}.") + ukify_cmd += " --devicetree %s" % dtb_path + + # Add option to pass a config file the UKI + if os.path.exists(d.getVar('UKI_CONFIG_FILE')): + ukify_cmd += " --config=%s" % d.getVar('UKI_CONFIG_FILE') + + # Tools + ukify_cmd += " --tools=%s%s/lib/systemd/tools" % (d.getVar("RECIPE_SYSROOT_NATIVE"), d.getVar("prefix")) + + # TODO: tpm2 measure for secure boot, depends on systemd-native and TPM tooling + # needed in systemd > 254 to fulfill ConditionSecurity=measured-uki + # Requires TPM device on build host, thus not supported at build time. + #ukify_cmd += " --measure" + + # Custom UKI name + output = " --output=%s/%s" % (d.getVar('DEPLOY_DIR_IMAGE'), d.getVar('UKI_FILENAME')) + ukify_cmd += " %s" % output + + # Run the ukify command + bb.process.run(ukify_cmd, shell=True) +} +addtask uki after do_rootfs before do_deploy do_image_complete do_image_wic -- 2.34.1
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#204067): https://lists.openembedded.org/g/openembedded-core/message/204067 Mute This Topic: https://lists.openembedded.org/mt/108223987/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
