From patchwork Mon Sep 2 09:41:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mikko Rapeli X-Patchwork-Id: 48564 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id CEF1BCD13CF for ; Mon, 2 Sep 2024 09:41:57 +0000 (UTC) Received: from mail-lj1-f177.google.com (mail-lj1-f177.google.com [209.85.208.177]) by mx.groups.io with SMTP id smtpd.web11.35079.1725270109782194246 for ; Mon, 02 Sep 2024 02:41:50 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@linaro.org header.s=google header.b=NdPstC4K; spf=pass (domain: linaro.org, ip: 209.85.208.177, mailfrom: mikko.rapeli@linaro.org) Received: by mail-lj1-f177.google.com with SMTP id 38308e7fff4ca-2f3f25a1713so46745281fa.2 for ; Mon, 02 Sep 2024 02:41:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1725270108; x=1725874908; darn=lists.openembedded.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=vGZkmSybxjWwHEzMQmozhYo1xhIaXV5MkofSKp7APfo=; b=NdPstC4Kpgl0sD6o0G9ve9lqrhg+eXh5erPKtn1XvLm3XMxkoDqr82K2xk+16hV3LU omwftRLO6VAmaj4ixBGGq6+OcoKM0BS6skLz+WlO4gVU0hHk3/otB7xLv65F6w/KyP48 mkDKBLjEK0+oqc3xr7mVCFDsKmdhzZMZPYWtwJ0rzxgLcPcHE2hYYB98/29ZJH4Tc4rU ARimVpSgXnzgh8bVvnJVSJjor8/Ck52sr/xa/fp3aVV/7xlkyKUyqKbnYjrZbLIcjLKv /bwomLmAHkxJUOnLKzkXGjnInqzU0C+kyJCgLUtUpTCWf1HcZ6sEJFuOHFpjpGXSvQL6 X//g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1725270108; x=1725874908; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vGZkmSybxjWwHEzMQmozhYo1xhIaXV5MkofSKp7APfo=; b=AQWtdeY9SlnOAmIyu9fUPV58kaDFgnUfoicEBssTUhKaPPQ+T0Yq6xPWogfmhpqmsb KBkwTC++xxsbwZoWTgsTcUEZ8unfKssaY5DrlMqdMvRG+g17auHudE4YSS2bEvZOfC3s 0WbiabJcfrFw591B15WccycAeHiUsKnjS3nhiBlkvWqKf1hful/+4U5rHTBI8WdlnyhZ 0OhHbayfdxfAJ3OGYomfF5WhwWxZLR5bufrpa7LRadraZmDdSmv69xFux5tH67/3z54V bCW/Dl31q/H2itkUu5062Qm5CbLNUEtCFGPgXbmsDCZz3KhGFD5kFZqOBJr2mC7IFeKL eSpA== X-Gm-Message-State: AOJu0YxqG6EFrGc4qS2JhKUh10PNPL270SPePWqYpQoPaKz3DV1Xci/x skJwo+Jcx08LDZj8YzwlUNoGBX/8O4gJMzu5IxE280hHUdqqwYJjpHTLLZXnGrDiYo8cRUVN063 Recw= X-Google-Smtp-Source: AGHT+IFS8/x60ppbL0E53zoYpZDCKUY6ReKadN+oTSrvNYd60ayz8t38ENtcKIn9UAGDikZuRjFx4Q== X-Received: by 2002:a05:6512:1043:b0:52b:bf8e:ffea with SMTP id 2adb3069b0e04-53546b93f60mr6230890e87.40.1725270107185; Mon, 02 Sep 2024 02:41:47 -0700 (PDT) Received: from localhost.localdomain (87-100-245-199.bb.dnainternet.fi. [87.100.245.199]) by smtp.gmail.com with ESMTPSA id 2adb3069b0e04-5354084176bsm1528853e87.219.2024.09.02.02.41.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Sep 2024 02:41:46 -0700 (PDT) From: Mikko Rapeli To: openembedded-core@lists.openembedded.org Cc: Michelle Lin , Erik Schilling , Mikko Rapeli Subject: [PATCH 3/3] uki.bbclass: add class for building Unified Kernel Images (UKI) Date: Mon, 2 Sep 2024 12:41:17 +0300 Message-ID: <20240902094117.31156-4-mikko.rapeli@linaro.org> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240902094117.31156-1-mikko.rapeli@linaro.org> References: <20240902094117.31156-1-mikko.rapeli@linaro.org> MIME-Version: 1.0 List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 02 Sep 2024 09:41:57 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/204067 From: Michelle Lin 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 Cc: Erik Schilling Signed-off-by: Mikko Rapeli Acked-by: Erik Schilling --- 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