wic/bootimg-efi: Add Unified Kernel Image option

"A unified kernel image is a single EFI PE executable combining an EFI
stub loader, a kernel image, an initramfs image, and the kernel command
line.

[...]

Images of this type have the advantage that all metadata and payload
that makes up the boot entry is monopolized in a single PE file that can
be signed cryptographically as one for the purpose of EFI
SecureBoot."[1]

This commit adds a create-unified-kernel-image=true option to the
bootimg-efi plugin for creating a Unified Kernel Image[1] and installing
it into $BOOT/EFI/Linux/ with a .efi extension per the the Boot Loader
Specification[1][2]. This is useful for implementing Secure Boot.

systemd-boot is the only mainstream bootloader implementing the
specification, but GRUB should be able to boot the EFI binary, this
commit however doesn't implement the necessary changes to the GRUB
config generation logic to boot the Unified Kernel Image.

[1] https://systemd.io/BOOT_LOADER_SPECIFICATION/#type-2-efi-unified-kernel-images
[2] https://systemd.io/BOOT_LOADER_SPECIFICATION/

(From OE-Core rev: b0573f240525df561ddef6e47cb285b217d38487)

Signed-off-by: Kristian Klausen <kristian@klausen.dk>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Kristian Klausen 2021-09-28 14:44:16 +02:00 committed by Richard Purdie
parent 75b79d5c05
commit b8c0f073f6
4 changed files with 102 additions and 12 deletions

View File

@ -0,0 +1,6 @@
# short-description: This file is used in oe-selftest wic module to test efi plugin
part /boot --source bootimg-efi --sourceparams="loader=systemd-boot,create-unified-kernel-image=true,initrd=${INITRAMFS_IMAGE}-${MACHINE}.${INITRAMFS_FSTYPES}" --active --align 1024 --use-uuid
part / --source rootfs --fstype=ext4 --align 1024 --use-uuid
bootloader --timeout=0 --append="console=ttyS0,115200n8"

View File

@ -27,6 +27,7 @@ WICVARS ?= "\
ROOTFS_SIZE \
STAGING_DATADIR \
STAGING_DIR \
STAGING_DIR_HOST \
STAGING_LIBDIR \
TARGET_SYS \
"
@ -84,8 +85,8 @@ do_image_wic[deptask] += "do_image_complete"
WKS_FILE_DEPENDS_DEFAULT = '${@bb.utils.contains_any("BUILD_ARCH", [ 'x86_64', 'i686' ], "syslinux-native", "",d)}'
WKS_FILE_DEPENDS_DEFAULT += "bmap-tools-native cdrtools-native btrfs-tools-native squashfs-tools-native e2fsprogs-native"
WKS_FILE_DEPENDS_BOOTLOADERS = ""
WKS_FILE_DEPENDS_BOOTLOADERS:x86 = "syslinux grub-efi systemd-boot"
WKS_FILE_DEPENDS_BOOTLOADERS:x86-64 = "syslinux grub-efi systemd-boot"
WKS_FILE_DEPENDS_BOOTLOADERS:x86 = "syslinux grub-efi systemd-boot os-release"
WKS_FILE_DEPENDS_BOOTLOADERS:x86-64 = "syslinux grub-efi systemd-boot os-release"
WKS_FILE_DEPENDS_BOOTLOADERS:x86-x32 = "syslinux grub-efi"
WKS_FILE_DEPENDS ??= "${WKS_FILE_DEPENDS_DEFAULT} ${WKS_FILE_DEPENDS_BOOTLOADERS}"

View File

@ -1158,6 +1158,35 @@ class Wic2(WicTestCase):
out = glob(self.resultdir + "%s-*.direct" % wksname)
self.assertEqual(1, len(out))
@only_for_arch(['i586', 'i686', 'x86_64'])
def test_efi_plugin_unified_kernel_image_qemu(self):
"""Test efi plugin's Unified Kernel Image feature in qemu"""
config = 'IMAGE_FSTYPES = "wic"\n'\
'INITRAMFS_IMAGE = "core-image-minimal-initramfs"\n'\
'WKS_FILE = "test_efi_plugin.wks"\n'\
'MACHINE_FEATURES:append = " efi"\n'
self.append_config(config)
self.assertEqual(0, bitbake('core-image-minimal core-image-minimal-initramfs ovmf').status)
self.remove_config(config)
with runqemu('core-image-minimal', ssh=False,
runqemuparams='ovmf', image_fstype='wic') as qemu:
# Check that /boot has EFI bootx64.efi (required for EFI)
cmd = "ls /boot/EFI/BOOT/bootx64.efi | wc -l"
status, output = qemu.run_serial(cmd)
self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
self.assertEqual(output, '1')
# Check that /boot has EFI/Linux/linux.efi (required for Unified Kernel Images auto detection)
cmd = "ls /boot/EFI/Linux/linux.efi | wc -l"
status, output = qemu.run_serial(cmd)
self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
self.assertEqual(output, '1')
# Check that /boot doesn't have loader/entries/boot.conf (Unified Kernel Images are auto detected by the bootloader)
cmd = "ls /boot/loader/entries/boot.conf 2&>/dev/null | wc -l"
status, output = qemu.run_serial(cmd)
self.assertEqual(1, status, 'Failed to run command "%s": %s' % (cmd, output))
self.assertEqual(output, '0')
def test_fs_types(self):
"""Test filesystem types for empty and not empty partitions"""
img = 'core-image-minimal'

View File

@ -12,6 +12,7 @@
import logging
import os
import tempfile
import shutil
import re
@ -119,12 +120,13 @@ class BootimgEFIPlugin(SourcePlugin):
bootloader = creator.ks.bootloader
loader_conf = ""
loader_conf += "default boot\n"
if source_params.get('create-unified-kernel-image') != "true":
loader_conf += "default boot\n"
loader_conf += "timeout %d\n" % bootloader.timeout
initrd = source_params.get('initrd')
if initrd:
if initrd and source_params.get('create-unified-kernel-image') != "true":
# obviously we need to have a common common deploy var
bootimg_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
if not bootimg_dir:
@ -183,11 +185,12 @@ class BootimgEFIPlugin(SourcePlugin):
for rd in initrds:
boot_conf += "initrd /%s\n" % rd
logger.debug("Writing systemd-boot config "
"%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
cfg.write(boot_conf)
cfg.close()
if source_params.get('create-unified-kernel-image') != "true":
logger.debug("Writing systemd-boot config "
"%s/hdd/boot/loader/entries/boot.conf", cr_workdir)
cfg = open("%s/hdd/boot/loader/entries/boot.conf" % cr_workdir, "w")
cfg.write(boot_conf)
cfg.close()
@classmethod
@ -288,9 +291,60 @@ class BootimgEFIPlugin(SourcePlugin):
kernel = "%s-%s.bin" % \
(get_bitbake_var("KERNEL_IMAGETYPE"), get_bitbake_var("INITRAMFS_LINK_NAME"))
install_cmd = "install -m 0644 %s/%s %s/%s" % \
(staging_kernel_dir, kernel, hdddir, kernel)
exec_cmd(install_cmd)
if source_params.get('create-unified-kernel-image') == "true":
initrd = source_params.get('initrd')
if not initrd:
raise WicError("initrd= must be specified when create-unified-kernel-image=true, exiting")
deploy_dir = get_bitbake_var("DEPLOY_DIR_IMAGE")
efi_stub = glob("%s/%s" % (deploy_dir, "linux*.efi.stub"))
if len(efi_stub) == 0:
raise WicError("Unified Kernel Image EFI stub not found, exiting")
efi_stub = efi_stub[0]
with tempfile.TemporaryDirectory() as tmp_dir:
label = source_params.get('label')
label_conf = "root=%s" % creator.rootdev
if label:
label_conf = "LABEL=%s" % label
bootloader = creator.ks.bootloader
cmdline = open("%s/cmdline" % tmp_dir, "w")
cmdline.write("%s %s" % (label_conf, bootloader.append))
cmdline.close()
initrds = initrd.split(';')
initrd = open("%s/initrd" % tmp_dir, "wb")
for f in initrds:
with open("%s/%s" % (deploy_dir, f), 'rb') as in_file:
shutil.copyfileobj(in_file, initrd)
initrd.close()
# Searched by systemd-boot:
# https://systemd.io/BOOT_LOADER_SPECIFICATION/#type-2-efi-unified-kernel-images
install_cmd = "install -d %s/EFI/Linux" % hdddir
exec_cmd(install_cmd)
staging_dir_host = get_bitbake_var("STAGING_DIR_HOST")
# https://www.freedesktop.org/software/systemd/man/systemd-stub.html
objcopy_cmd = "objcopy \
--add-section .osrel=%s --change-section-vma .osrel=0x20000 \
--add-section .cmdline=%s --change-section-vma .cmdline=0x30000 \
--add-section .linux=%s --change-section-vma .linux=0x2000000 \
--add-section .initrd=%s --change-section-vma .initrd=0x3000000 \
%s %s" % \
("%s/usr/lib/os-release" % staging_dir_host,
cmdline.name,
"%s/%s" % (staging_kernel_dir, kernel),
initrd.name,
efi_stub,
"%s/EFI/Linux/linux.efi" % hdddir)
exec_cmd(objcopy_cmd)
else:
install_cmd = "install -m 0644 %s/%s %s/%s" % \
(staging_kernel_dir, kernel, hdddir, kernel)
exec_cmd(install_cmd)
if get_bitbake_var("IMAGE_EFI_BOOT_FILES"):
for src_path, dst_path in cls.install_task: