Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions mkosi.conf.d/fedora/mkosi.conf.d/arm64-uki-signed.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1-or-later

[Match]
Architecture=arm64
Bootloader=uki-signed

[Content]
# kernel-uki-virt ships a pre-built UKI for virtual machines, needed to test
# Bootloader=uki-signed. Not available on all Fedora architectures (e.g. ppc64le),
# so kept in arch-specific configs and gated on Bootloader=uki-signed. A separate
# file is needed to match both Architecture= and Bootloader= simultaneously.
Packages=
kernel-uki-virt
13 changes: 13 additions & 0 deletions mkosi.conf.d/fedora/mkosi.conf.d/x86_64-uki-signed.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1-or-later

[Match]
Architecture=x86-64
Bootloader=uki-signed

[Content]
# kernel-uki-virt ships a pre-built UKI for virtual machines, needed to test
# Bootloader=uki-signed. Not available on all Fedora architectures (e.g. ppc64le),
# so kept in arch-specific configs and gated on Bootloader=uki-signed. A separate
# file is needed to match both Architecture= and Bootloader= simultaneously.
Packages=
kernel-uki-virt
6 changes: 2 additions & 4 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
summary,
systemd_tool_version,
want_kernel,
want_prebuilt_uki,
want_selinux_relabel,
yes_no,
)
Expand Down Expand Up @@ -2144,10 +2145,7 @@ def install_uki(
with umask(~0o700):
boot_binary.parent.mkdir(parents=True, exist_ok=True)

if (
context.config.bootloader.is_signed()
and context.config.unified_kernel_images == UnifiedKernelImage.auto
) or context.config.unified_kernel_images == UnifiedKernelImage.signed:
if want_prebuilt_uki(context.config):
for p in (context.root / "usr/lib/modules" / kver).glob("*.efi"):
log_step(f"Installing prebuilt UKI at {p} to {boot_binary}")
copyfile2(p, boot_binary)
Expand Down
17 changes: 12 additions & 5 deletions mkosi/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
SecureBootSignTool,
ShimBootloader,
systemd_tool_version,
want_prebuilt_uki,
)
from mkosi.context import Context
from mkosi.distribution import Distribution
Expand Down Expand Up @@ -680,15 +681,21 @@ def gen_kernel_images(context: Context) -> Iterator[tuple[str, Path]]:
# scripts in the kernel source tree sometimes do weird stuff. But let's make sure we're not returning
# UKIs as the UKI on Fedora is named vmlinuz-virt.efi. Also look for uncompressed images (vmlinux) as
# some architectures ship those. Prefer vmlinuz if both are present.
for kimg in kver.glob("vmlinuz*"):
if KernelType.identify(context.config, kimg) != KernelType.uki:
yield kver.name, kimg
break
if want_prebuilt_uki(context.config):
for kimg in kver.glob("vmlinuz*.efi"):
if KernelType.identify(context.config, kimg) == KernelType.uki:
yield kver.name, kimg
break
else:
for kimg in kver.glob("vmlinux*"):
for kimg in kver.glob("vmlinuz*"):
if KernelType.identify(context.config, kimg) != KernelType.uki:
yield kver.name, kimg
break
else:
for kimg in kver.glob("vmlinux*"):
if KernelType.identify(context.config, kimg) != KernelType.uki:
yield kver.name, kimg
break


def install_systemd_boot(context: Context) -> None:
Expand Down
10 changes: 10 additions & 0 deletions mkosi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3299,6 +3299,7 @@ def parse_kernel_module_filter_regexp(p: str) -> str:
dest="bootloader",
section="Content",
parse=config_make_enum_parser(Bootloader),
match=config_make_enum_matcher(Bootloader),
choices=Bootloader.choices(),
default=Bootloader.systemd_boot,
help="Specify which UEFI bootloader to use",
Expand Down Expand Up @@ -5445,6 +5446,15 @@ def want_default_initrd(config: Config) -> bool:
return Path("default") in config.initrds


def want_prebuilt_uki(config: Config) -> bool:
# Returns True when mkosi should use a distro-pre-built signed UKI rather than building one itself.
# This happens when a signed bootloader is selected (implying distro UKIs) or when
# UnifiedKernelImages=signed is set explicitly.
return (
config.bootloader.is_signed() and config.unified_kernel_images == UnifiedKernelImage.auto
) or config.unified_kernel_images == UnifiedKernelImage.signed


def finalize_historydir(args: Args, output_dir: Optional[Path] = None) -> Path:
# When an output directory is given, the build history is also stored there so that builds into
# distinct output directories don't read each other's history. Otherwise it lives in the config dir.
Expand Down
10 changes: 9 additions & 1 deletion mkosi/qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from mkosi.bootloader import KernelType
from mkosi.config import (
Args,
Bootloader,
Config,
ConfigFeature,
ConsoleMode,
Expand Down Expand Up @@ -1314,9 +1315,16 @@ def add_virtiofs_mount(
if kernel and (kerneltype != KernelType.uki or not config.architecture.supports_smbios(firmware)):
cmdline += ["-append", " ".join(config.kernel_command_line + kcl)]
elif config.architecture.supports_smbios(firmware):
# With Bootloader=uki-signed, a UKI built by the distro is used, and we cannot embed
# config.kernel_command_line in the UKI. Instead, pass those options through SMBIOS type#11. We
# know that this will work because all UEFI systems support SMBIOS and UKIs by construction use
# systemd-stub, which reads SMBIOS type#11.
stub_kcl = (
config.kernel_command_line if config.bootloader == Bootloader.uki_signed else []
) + kcl
cmdline += [
"-smbios",
f"type=11,value=io.systemd.stub.kernel-cmdline-extra={' '.join(kcl).replace(',', ',,')}",
f"type=11,value=io.systemd.stub.kernel-cmdline-extra={' '.join(stub_kcl).replace(',', ',,')}", # noqa: E501
"-smbios",
f"type=11,value=io.systemd.boot.kernel-cmdline-extra={' '.join(kcl).replace(',', ',,')}",
]
Expand Down
34 changes: 31 additions & 3 deletions tests/test_boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import pytest

from mkosi.config import Bootloader, Firmware, OutputFormat
from mkosi.config import Architecture, Bootloader, Firmware, OutputFormat
from mkosi.distribution import Distribution
from mkosi.run import find_binary, run
from mkosi.versioncomp import GenericVersion
Expand Down Expand Up @@ -59,11 +59,39 @@ def test_format(config: ImageConfig, format: OutputFormat) -> None:

@pytest.mark.parametrize("bootloader", Bootloader)
def test_bootloader(config: ImageConfig, bootloader: Bootloader) -> None:
if bootloader == Bootloader.uki_signed:
return

if config.distribution == Distribution.rhel_ubi or bootloader.is_signed():
return

firmware = Firmware.linux if bootloader == Bootloader.none else Firmware.auto

with Image(config) as image:
image.build(["--format=disk", "--bootloader", str(bootloader)])
image.vm(["--firmware", str(firmware)])
image.build(["--format=disk", f"--bootloader={bootloader}"])
image.vm([f"--firmware={firmware}"])


def test_bootloader_prebuilt_initrd(config: ImageConfig) -> None:
# TODO: want_prebuilt_uki() also fires for UnifiedKernelImage=signed with a non-signed bootloader,
# but there is no integration test for that path yet.
# uki-signed test matrix:
# x86-64 Fedora → supports_smbios(uefi)=True, kernel-uki-virt available → runs
# arm64 Fedora → supports_smbios(uefi)=True, kernel-uki-virt available → runs
# ppc64le Fedora → supports_smbios(uefi)=False → skipped
# non-Fedora → no kernel-uki-virt equivalent → skipped
if config.distribution != Distribution.fedora or not Architecture.native().supports_smbios(
Firmware.uefi
):
return

with Image(config) as image:
image.build(
[
"--format=disk",
f"--bootloader={Bootloader.uki_signed}",
"--incremental=no",
"--cache-key=&d~&r~&a~&I~prebuilt",
]
)
image.vm(["--firmware", str(Firmware.auto)])
Loading