| # 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 \ |
| virtual/cross-binutils \ |
| virtual/kernel \ |
| " |
| |
| inherit image-artifact-names |
| require ../conf/image-uefi.conf |
| |
| INITRAMFS_IMAGE ?= "core-image-minimal-initramfs" |
| |
| 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" |
| KERNEL_DEVICETREE ??= "" |
| UKI_DEVICETREE ?= "${KERNEL_DEVICETREE}" |
| # 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 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 |
| |
| # 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 |
| uki_fstype = d.getVar("INITRAMFS_FSTYPES").split()[0] |
| initramfs_image = "%s-%s.%s" % (d.getVar('INITRAMFS_IMAGE'), d.getVar('MACHINE'), uki_fstype) |
| ukify_cmd += " --initrd=%s" % (os.path.join(deploy_dir_image, initramfs_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 |
| uki_devicetree = d.getVar('UKI_DEVICETREE') |
| if uki_devicetree: |
| for dtb in uki_devicetree.split(): |
| # DTBs are without sub-directories in deploy_dir |
| dtb_name = os.path.basename(dtb) |
| dtb_path = "%s/%s" % (deploy_dir_image, dtb_name) |
| 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)) |
| out, err = bb.process.run(ukify_cmd, shell=True) |
| bb.debug(2, "%s\n%s" % (out, err)) |
| } |
| addtask uki after do_rootfs before do_deploy do_image_complete do_image_wic |