diff --git a/meta-dts-distro/classes-recipe/uki.bbclass b/meta-dts-distro/classes-recipe/uki.bbclass new file mode 100644 index 00000000..edf6b7f4 --- /dev/null +++ b/meta-dts-distro/classes-recipe/uki.bbclass @@ -0,0 +1,196 @@ +# Unified kernel image (UKI) class +# +# This bbclass merges kernel, initrd etc as a UKI standard UEFI binary, +# to be loaded with UEFI firmware and systemd-boot on target HW. +# 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 contains: +# +# - 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. +# - kernel +# - initramfs +# - kernel command line +# - uname -r kernel version +# - /etc/os-release to create a boot menu with version details +# - optionally secure boot signature(s) +# - other metadata (e.g. TPM PCR measurements) +# +# Usage instructions: +# +# - requires UEFI compatible firmware on target, e.g. qemuarm64-secureboot u-boot based +# from meta-arm or qemux86 ovmf/edk2 based firmware for x86_64 +# +# - Distro/build config: +# +# INIT_MANAGER = "systemd" +# MACHINE_FEATURES:append = " efi" +# EFI_PROVIDER = "systemd-boot" +# INITRAMFS_IMAGE = "core-image-minimal-initramfs" +# +# - image recipe: +# +# inherit uki +# +# - qemuboot/runqemu changes in image recipe or build config: +# +# # Kernel command line must be inside the signed uki +# QB_KERNEL_ROOT = "" +# # kernel is in the uki image, not loaded separately +# QB_DEFAULT_KERNEL = "none" +# +# - for UEFI secure boot, systemd-boot and uki (including kernel) can +# be signed but require sbsign-tool-native (recipe available from meta-secure-core, +# see also qemuarm64-secureboot from meta-arm). Set variable +# UKI_SB_KEY to path of private key and UKI_SB_CERT for certificate. +# Note that systemd-boot also need to be signed with the same key. +# +# - at runtime, UEFI firmware will load and boot systemd-boot which +# creates a menu from all detected uki binaries. No need to manually +# setup boot menu entries. +# +# - see efi-uki-bootdisk.wks.in how to create ESP partition which hosts systemd-boot, +# config file(s) for systemd-boot and the UKI binaries. +# + +DEPENDS += "\ + os-release \ + systemd-boot \ + systemd-boot-native \ + binutils-cross-${TARGET_ARCH} \ + virtual/kernel \ + python3-pefile-native \ +" + +inherit image-artifact-names + +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_KERNEL_FILENAME ?= "${KERNEL_IMAGETYPE}" +UKI_CMDLINE ?= "rootwait root=LABEL=root console=${KERNEL_CONSOLE}" +# secure boot keys and cert, needs sbsign-tools-native (meta-secure-core) +#UKI_SB_KEY ?= "" +#UKI_SB_CERT ?= "" + +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 every time 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 +# nooelint: oelint.task.noanonpython +python () { + d.delVarFlag("do_fetch","noexec") + d.delVarFlag("do_unpack","noexec") +} + +# main task +python do_uki() { + import glob + import bb.process + + # base ukify command, can be extended if needed + ukify_cmd = d.getVar('UKIFY_CMD') + + deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE') + + # architecture + target_arch = d.getVar('EFI_ARCH') + if target_arch: + ukify_cmd += " --efi-arch %s" % (target_arch) + + # systemd stubs + 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) + + # 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 + kernel_filename = d.getVar('UKI_KERNEL_FILENAME') or None + if kernel_filename: + kernel = "%s/%s" % (deploy_dir_image, kernel_filename) + if not os.path.exists(kernel): + bb.fatal(f"ERROR: cannot find %s" % (kernel)) + ukify_cmd += " --linux=%s" % (kernel) + # not always needed, ukify can detect version from kernel binary + kernel_version = d.getVar('KERNEL_VERSION') + if kernel_version: + ukify_cmd += "--uname %s" % (kernel_version) + else: + bb.fatal("ERROR - UKI_KERNEL_FILENAME not set") + + # command line + cmdline = d.getVar('UKI_CMDLINE') + if cmdline: + ukify_cmd += " --cmdline='%s'" % (cmdline) + + # 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) + + # custom config for ukify + if os.path.exists(d.getVar('UKI_CONFIG_FILE')): + ukify_cmd += " --config=%s" % (d.getVar('UKI_CONFIG_FILE')) + + # systemd tools + ukify_cmd += " --tools=%s%s/lib/systemd/tools" % \ + (d.getVar("RECIPE_SYSROOT_NATIVE"), d.getVar("prefix")) + + # version + ukify_cmd += " --os-release=@%s%s/lib/os-release" % \ + (d.getVar("RECIPE_SYSROOT"), 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" + + # securebooot signing, also for kernel + key = d.getVar('UKI_SB_KEY') + if key: + ukify_cmd += " --sign-kernel --secureboot-private-key='%s'" % (key) + cert = d.getVar('UKI_SB_CERT') + if cert: + ukify_cmd += " --secureboot-certificate='%s'" % (cert) + + # custom output UKI filename + output = " --output=%s/%s" % (d.getVar('DEPLOY_DIR_IMAGE'), d.getVar('UKI_FILENAME')) + ukify_cmd += " %s" % (output) + + # Run the ukify command + bb.debug(2, "uki: running command: %s" % (ukify_cmd)) + bb.process.run(ukify_cmd, shell=True) +} +addtask uki after do_rootfs before do_deploy do_image_complete do_image_wic diff --git a/meta-dts-distro/conf/distro/dts-distro.conf b/meta-dts-distro/conf/distro/dts-distro.conf index 5d6fe05a..6355d7b6 100644 --- a/meta-dts-distro/conf/distro/dts-distro.conf +++ b/meta-dts-distro/conf/distro/dts-distro.conf @@ -36,3 +36,6 @@ IMAGE_EFI_BOOT_FILES += "grub-efi-bootx64.efi;EFI/DTS/grubx64.efi" PREFERRED_VERSION_flashrom = "dasharo-v1.2.2+git%" PREFERRED_VERSION_fwupd = "2.0.1" APPEND += "quiet" + +BOOT_LABEL="dts-boot" +ROOT_LABEL="dts-root" diff --git a/meta-dts-distro/recipes-bsp/grub/grub-efi/grub.cfg b/meta-dts-distro/recipes-bsp/grub/grub-efi/grub.cfg new file mode 100644 index 00000000..bf170f91 --- /dev/null +++ b/meta-dts-distro/recipes-bsp/grub/grub-efi/grub.cfg @@ -0,0 +1,9 @@ +serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 +default=DTS +timeout=0 + +menuentry 'DTS' { + regexp -s 2:rootfs_drive (.*)(hd[0-9]*)(.*) $cmdpath + probe --part-uuid ($rootfs_drive,msdos2) --set=rootfs + chainloader /EFI/DTS/dts.efi root=PARTUUID=$rootfs rootwait console=ttyS0,115200 +} diff --git a/meta-dts-distro/recipes-bsp/grub/grub-efi_%.bbappend b/meta-dts-distro/recipes-bsp/grub/grub-efi_%.bbappend index ad2a1945..3cf426b6 100644 --- a/meta-dts-distro/recipes-bsp/grub/grub-efi_%.bbappend +++ b/meta-dts-distro/recipes-bsp/grub/grub-efi_%.bbappend @@ -1 +1,10 @@ +FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:" require ${@bb.utils.contains('DISTRO_FEATURES', 'efi-secure-boot', 'grub-efi-efi-custom.inc', '', d)} + +SRC_URI += "file://grub.cfg" + +do_deploy:append() { + install -m 644 "${WORKDIR}/grub.cfg" ${DEPLOY_DIR_IMAGE}/ +} + +GRUB_BUILDIN:append = " probe regexp chain" diff --git a/meta-dts-distro/recipes-dts/images/dts-base-image.bb b/meta-dts-distro/recipes-dts/images/dts-base-image.bb index 598cc9fd..4e037a4d 100644 --- a/meta-dts-distro/recipes-dts/images/dts-base-image.bb +++ b/meta-dts-distro/recipes-dts/images/dts-base-image.bb @@ -1 +1,28 @@ require dts-base-image.inc +inherit uki + +UKI_CMDLINE = "root=LABEL=${ROOT_LABEL} console=${KERNEL_CONSOLE} rootwait ${APPEND}" +UKI_FILENAME = "dts.efi" +IMAGE_EFI_BOOT_FILES += " \ + ${UKI_FILENAME};EFI/DTS/${UKI_FILENAME} \ + grub.cfg;EFI/BOOT/grub.cfg \ + " + +do_ipxe_uki() { + stub="${DEPLOY_DIR_IMAGE}/linux${EFI_ARCH}.efi.stub" + kernel="${DEPLOY_DIR_IMAGE}/${UKI_KERNEL_FILENAME}" + rootfs="${DEPLOY_DIR_IMAGE}/${IMAGE_LINK_NAME}.cpio.gz" + ukify build --cmdline "${UKI_CMDLINE}" --stub "${stub}" \ + --linux "${kernel}" --initrd "${rootfs}" \ + --tools "${RECIPE_SYSROOT_NATIVE}/lib/systemd/tools" \ + --os-release "${RECIPE_SYSROOT}/lib/os-release" \ + --output "${DEPLOY_DIR_IMAGE}/ipxe_${UKI_FILENAME}" +} + +do_ipxe_uki[depends] += " \ + systemd-boot:do_deploy \ + virtual/kernel:do_deploy \ + dts-base-image:do_image_complete \ +" + +addtask ipxe_uki after do_image_complete before do_build diff --git a/meta-dts-distro/wic/usb-stick-dts.wks.in b/meta-dts-distro/wic/usb-stick-dts.wks.in index 2ea47686..694a4994 100644 --- a/meta-dts-distro/wic/usb-stick-dts.wks.in +++ b/meta-dts-distro/wic/usb-stick-dts.wks.in @@ -1,4 +1,4 @@ bootloader --timeout=0 --append=" rootwait" -part /boot --source bootimg-biosplusefi --sourceparams="loader=grub-efi" --ondisk sda --label dts-boot --align 1024 --use-uuid --active --system-id 0xef -part / --source rootfs --fstype=ext4 --ondisk sda --label dts-root --align 1024 --use-uuid --fixed-size 1024 +part /boot --source bootimg-biosplusefi --sourceparams="loader=grub-efi" --ondisk sda --label ${BOOT_LABEL} --align 1024 --use-uuid --active --system-id 0xef +part / --source rootfs --fstype=ext4 --ondisk sda --label ${ROOT_LABEL} --align 1024 --use-uuid --fixed-size 1024