summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/classes-recipe/uki.bbclass195
1 files changed, 195 insertions, 0 deletions
diff --git a/meta/classes-recipe/uki.bbclass b/meta/classes-recipe/uki.bbclass
new file mode 100644
index 0000000000..d4f25c7fd2
--- /dev/null
+++ b/meta/classes-recipe/uki.bbclass
@@ -0,0 +1,195 @@
1# Unified kernel image (UKI) class
2#
3# This bbclass merges kernel, initrd etc as a UKI standard UEFI binary,
4# to be loaded with UEFI firmware and systemd-boot on target HW.
5# TPM PCR pre-calculation is not supported since systemd-measure tooling
6# is meant to run on target, not in cross compile environment.
7#
8# See:
9# https://www.freedesktop.org/software/systemd/man/latest/ukify.html
10# https://uapi-group.org/specifications/specs/unified_kernel_image/
11#
12# The UKI contains:
13#
14# - UEFI stub
15# The linux kernel can generate a UEFI stub, however the one from systemd-boot can fetch
16# the command line from a separate section of the EFI application, avoiding the need to
17# rebuild the kernel.
18# - kernel
19# - initramfs
20# - kernel command line
21# - uname -r kernel version
22# - /etc/os-release to create a boot menu with version details
23# - optionally secure boot signature(s)
24# - other metadata (e.g. TPM PCR measurements)
25#
26# Usage instructions:
27#
28# - requires UEFI compatible firmware on target, e.g. qemuarm64-secureboot u-boot based
29# from meta-arm or qemux86 ovmf/edk2 based firmware for x86_64
30#
31# - Distro/build config:
32#
33# INIT_MANAGER = "systemd"
34# MACHINE_FEATURES:append = " efi"
35# EFI_PROVIDER = "systemd-boot"
36# INITRAMFS_IMAGE = "core-image-minimal-initramfs"
37#
38# - image recipe:
39#
40# inherit uki
41#
42# - qemuboot/runqemu changes in image recipe or build config:
43#
44# # Kernel command line must be inside the signed uki
45# QB_KERNEL_ROOT = ""
46# # kernel is in the uki image, not loaded separately
47# QB_DEFAULT_KERNEL = "none"
48#
49# - for UEFI secure boot, systemd-boot and uki (including kernel) can
50# be signed but require sbsign-tool-native (recipe available from meta-secure-core,
51# see also qemuarm64-secureboot from meta-arm). Set variable
52# UKI_SB_KEY to path of private key and UKI_SB_CERT for certificate.
53# Note that systemd-boot also need to be signed with the same key.
54#
55# - at runtime, UEFI firmware will load and boot systemd-boot which
56# creates a menu from all detected uki binaries. No need to manually
57# setup boot menu entries.
58#
59# - see efi-uki-bootdisk.wks.in how to create ESP partition which hosts systemd-boot,
60# config file(s) for systemd-boot and the UKI binaries.
61#
62
63DEPENDS += "\
64 os-release \
65 systemd-boot \
66 systemd-boot-native \
67 virtual/${TARGET_PREFIX}binutils \
68 virtual/kernel \
69"
70
71inherit image-artifact-names
72require ../conf/image-uefi.conf
73
74INITRAMFS_IMAGE ?= "core-image-minimal-initramfs"
75
76INITRD_ARCHIVE ?= "${INITRAMFS_IMAGE}-${MACHINE}.${INITRAMFS_FSTYPES}"
77
78do_image_complete[depends] += "${INITRAMFS_IMAGE}:do_image_complete"
79
80UKIFY_CMD ?= "ukify build"
81UKI_CONFIG_FILE ?= "${UNPACKDIR}/uki.conf"
82UKI_FILENAME ?= "uki.efi"
83UKI_KERNEL_FILENAME ?= "${KERNEL_IMAGETYPE}"
84UKI_CMDLINE ?= "rootwait root=LABEL=root console=${KERNEL_CONSOLE}"
85# secure boot keys and cert, needs sbsign-tools-native (meta-secure-core)
86#UKI_SB_KEY ?= ""
87#UKI_SB_CERT ?= ""
88
89IMAGE_EFI_BOOT_FILES ?= "${UKI_FILENAME};EFI/Linux/${UKI_FILENAME}"
90
91do_uki[depends] += " \
92 systemd-boot:do_deploy \
93 virtual/kernel:do_deploy \
94 "
95do_uki[depends] += "${@ '${INITRAMFS_IMAGE}:do_image_complete' if d.getVar('INITRAMFS_IMAGE') else ''}"
96
97# ensure that the build directory is empty everytime we generate a newly-created uki
98do_uki[cleandirs] = "${B}"
99# influence the build directory at the start of the builds
100do_uki[dirs] = "${B}"
101
102# we want to allow specifying files in SRC_URI, such as for signing the UKI
103python () {
104 d.delVarFlag("do_fetch","noexec")
105 d.delVarFlag("do_unpack","noexec")
106}
107
108# main task
109python do_uki() {
110 import glob
111 import bb.process
112
113 # base ukify command, can be extended if needed
114 ukify_cmd = d.getVar('UKIFY_CMD')
115
116 deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE')
117
118 # architecture
119 target_arch = d.getVar('EFI_ARCH')
120 if target_arch:
121 ukify_cmd += " --efi-arch %s" % (target_arch)
122
123 # systemd stubs
124 stub = "%s/linux%s.efi.stub" % (d.getVar('DEPLOY_DIR_IMAGE'), target_arch)
125 if not os.path.exists(stub):
126 bb.fatal(f"ERROR: cannot find {stub}.")
127 ukify_cmd += " --stub %s" % (stub)
128
129 # initrd
130 initramfs_image = "%s" % (d.getVar('INITRD_ARCHIVE'))
131 ukify_cmd += " --initrd=%s" % (os.path.join(deploy_dir_image, initramfs_image))
132
133 deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE')
134
135 # kernel
136 kernel_filename = d.getVar('UKI_KERNEL_FILENAME') or None
137 if kernel_filename:
138 kernel = "%s/%s" % (deploy_dir_image, kernel_filename)
139 if not os.path.exists(kernel):
140 bb.fatal(f"ERROR: cannot find %s" % (kernel))
141 ukify_cmd += " --linux=%s" % (kernel)
142 # not always needed, ukify can detect version from kernel binary
143 kernel_version = d.getVar('KERNEL_VERSION')
144 if kernel_version:
145 ukify_cmd += "--uname %s" % (kernel_version)
146 else:
147 bb.fatal("ERROR - UKI_KERNEL_FILENAME not set")
148
149 # command line
150 cmdline = d.getVar('UKI_CMDLINE')
151 if cmdline:
152 ukify_cmd += " --cmdline='%s'" % (cmdline)
153
154 # dtb
155 if d.getVar('KERNEL_DEVICETREE'):
156 for dtb in d.getVar('KERNEL_DEVICETREE').split():
157 dtb_path = "%s/%s" % (deploy_dir_image, dtb)
158 if not os.path.exists(dtb_path):
159 bb.fatal(f"ERROR: cannot find {dtb_path}.")
160 ukify_cmd += " --devicetree %s" % (dtb_path)
161
162 # custom config for ukify
163 if os.path.exists(d.getVar('UKI_CONFIG_FILE')):
164 ukify_cmd += " --config=%s" % (d.getVar('UKI_CONFIG_FILE'))
165
166 # systemd tools
167 ukify_cmd += " --tools=%s%s/lib/systemd/tools" % \
168 (d.getVar("RECIPE_SYSROOT_NATIVE"), d.getVar("prefix"))
169
170 # version
171 ukify_cmd += " --os-release=@%s%s/lib/os-release" % \
172 (d.getVar("RECIPE_SYSROOT"), d.getVar("prefix"))
173
174 # TODO: tpm2 measure for secure boot, depends on systemd-native and TPM tooling
175 # needed in systemd > 254 to fulfill ConditionSecurity=measured-uki
176 # Requires TPM device on build host, thus not supported at build time.
177 #ukify_cmd += " --measure"
178
179 # securebooot signing, also for kernel
180 key = d.getVar('UKI_SB_KEY')
181 if key:
182 ukify_cmd += " --sign-kernel --secureboot-private-key='%s'" % (key)
183 cert = d.getVar('UKI_SB_CERT')
184 if cert:
185 ukify_cmd += " --secureboot-certificate='%s'" % (cert)
186
187 # custom output UKI filename
188 output = " --output=%s/%s" % (d.getVar('DEPLOY_DIR_IMAGE'), d.getVar('UKI_FILENAME'))
189 ukify_cmd += " %s" % (output)
190
191 # Run the ukify command
192 bb.debug("uki: running command: %s" % (ukify_cmd))
193 bb.process.run(ukify_cmd, shell=True)
194}
195addtask uki after do_rootfs before do_deploy do_image_complete do_image_wic