]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Introduce UnifiedKernelImageProfiles= 3090/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 1 Oct 2024 12:49:37 +0000 (14:49 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 4 Oct 2024 11:58:12 +0000 (13:58 +0200)
Same concept as PEAddons=, except these are added as additional
profiles to every UKI built by mkosi.

Accompanying systemd PR: https://github.com/systemd/systemd/pull/34608

mkosi/__init__.py
mkosi/config.py
mkosi/resources/man/mkosi.1.md
tests/test_json.py

index 28c24eaf58a43ea7b6f98f1d972e66b648539ef0..e444cbde894204632b7178be9a84550867dd5054 100644 (file)
@@ -70,6 +70,7 @@ from mkosi.config import (
     format_bytes,
     parse_boolean,
     parse_config,
+    parse_ini,
     summary,
     systemd_tool_version,
     want_selinux_relabel,
@@ -1487,6 +1488,7 @@ def run_ukify(
     cmdline: str = "",
     arguments: Sequence[PathString] = (),
     options: Sequence[PathString] = (),
+    sign: bool = True,
 ) -> None:
     ukify = context.config.find_binary("ukify", "/usr/lib/systemd/ukify")
     if not ukify:
@@ -1518,7 +1520,7 @@ def run_ukify(
         "--ro-bind", context.workspace / "cmdline", context.workspace / "cmdline",
     ]  # fmt: skip
 
-    if context.config.secure_boot:
+    if sign and context.config.secure_boot:
         assert context.config.secure_boot_key
         assert context.config.secure_boot_certificate
 
@@ -1565,6 +1567,7 @@ def build_uki(
     microcodes: list[Path],
     initrds: list[Path],
     cmdline: Sequence[str],
+    profiles: Sequence[Path],
     output: Path,
 ) -> None:
     if not (ukify := context.config.find_binary("ukify", "/usr/lib/systemd/ukify")):
@@ -1574,12 +1577,14 @@ def build_uki(
         "--os-release", f"@{context.root / 'usr/lib/os-release'}",
         "--uname", kver,
         "--linux", kimg,
+        *flatten(["--join-profile", os.fspath(profile)] for profile in profiles),
     ]  # fmt: skip
 
     options: list[PathString] = [
         "--ro-bind", context.workspace / "cmdline", context.workspace / "cmdline",
         "--ro-bind", context.root / "usr/lib/os-release", context.root / "usr/lib/os-release",
         "--ro-bind", kimg, kimg,
+        *flatten(["--ro-bind", os.fspath(profile), os.fspath(profile)] for profile in profiles),
     ]  # fmt: skip
 
     if context.config.secure_boot:
@@ -1754,6 +1759,7 @@ def install_type1(
     kimg: Path,
     token: str,
     partitions: Sequence[Partition],
+    cmdline: list[str],
 ) -> None:
     dst = context.root / "boot" / token / kver
     entry = context.root / f"boot/loader/entries/{token}-{kver}.conf"
@@ -1762,7 +1768,6 @@ def install_type1(
         entry.parent.mkdir(parents=True, exist_ok=True)
 
     kmods = build_kernel_modules_initrd(context, kver)
-    cmdline = finalize_cmdline(context, partitions, finalize_roothash(partitions))
 
     with umask(~0o600):
         if (
@@ -1854,7 +1859,13 @@ def expand_kernel_specifiers(text: str, kver: str, token: str, roothash: str, bo
 
 
 def install_uki(
-    context: Context, kver: str, kimg: Path, token: str, partitions: Sequence[Partition]
+    context: Context,
+    kver: str,
+    kimg: Path,
+    token: str,
+    partitions: Sequence[Partition],
+    profiles: Sequence[Path],
+    cmdline: list[str],
 ) -> None:
     bootloader_entry_format = context.config.unified_kernel_image_format or "&e-&k"
 
@@ -1916,7 +1927,8 @@ def install_uki(
             context.root / kimg,
             microcodes,
             initrds,
-            finalize_cmdline(context, partitions, roothash),
+            cmdline,
+            profiles,
             boot_binary,
         )
 
@@ -1973,6 +1985,46 @@ def systemd_addon_stub_binary(context: Context) -> Path:
     return stub
 
 
+def build_uki_profiles(context: Context, cmdline: Sequence[str]) -> list[Path]:
+    if not want_uki(context) or not context.config.unified_kernel_image_profiles:
+        return []
+
+    stub = systemd_addon_stub_binary(context)
+    if not stub.exists():
+        die(f"sd-stub not found at /{stub.relative_to(context.root)} in the image")
+
+    (context.workspace / "uki-profiles").mkdir()
+
+    profiles = []
+
+    for profile in context.config.unified_kernel_image_profiles:
+        output = context.workspace / "uki-profiles" / profile.with_suffix(".efi").name
+
+        # We want to append the cmdline from the ukify config file to the base kernel command line so parse
+        # it from the ukify config file and append it to our own kernel command line.
+
+        profile_cmdline = ""
+
+        for section, k, v in parse_ini(profile):
+            if section == "UKI" and k == "Cmdline":
+                profile_cmdline = v.replace("\n", " ")
+
+        with complete_step(f"Generating UKI profile '{profile.stem}'"):
+            run_ukify(
+                context,
+                stub,
+                output,
+                cmdline=f"{' '.join(cmdline)} {profile_cmdline}",
+                arguments=["--config", profile],
+                options=["--ro-bind", profile, profile],
+                sign=False,
+            )
+
+        profiles += [output]
+
+    return profiles
+
+
 def install_kernel(context: Context, partitions: Sequence[Partition]) -> None:
     # Iterates through all kernel versions included in the image and generates a combined
     # kernel+initrd+cmdline+osrelease EFI file from it and places it in the /EFI/Linux directory of
@@ -2004,12 +2056,14 @@ def install_kernel(context: Context, partitions: Sequence[Partition]) -> None:
         die("A bootable image was requested but no kernel was found")
 
     token = find_entry_token(context)
+    cmdline = finalize_cmdline(context, partitions, finalize_roothash(partitions))
+    profiles = build_uki_profiles(context, cmdline)
 
     for kver, kimg in gen_kernel_images(context):
         if want_uki(context):
-            install_uki(context, kver, kimg, token, partitions)
+            install_uki(context, kver, kimg, token, partitions, profiles, cmdline)
         if not want_uki(context) or want_grub_bios(context, partitions):
-            install_type1(context, kver, kimg, token, partitions)
+            install_type1(context, kver, kimg, token, partitions, cmdline)
 
         if context.config.bootloader == Bootloader.uki:
             break
@@ -2024,8 +2078,18 @@ def make_uki(
     )
 
     initrds = [context.workspace / "initrd"]
+    build_uki(
+        context,
+        stub,
+        kver,
+        kimg,
+        microcode,
+        initrds,
+        context.config.kernel_command_line,
+        build_uki_profiles(context, context.config.kernel_command_line),
+        output,
+    )
 
-    build_uki(context, stub, kver, kimg, microcode, initrds, context.config.kernel_command_line, output)
     extract_pe_section(context, output, ".linux", context.staging / context.config.output_split_kernel)
     extract_pe_section(context, output, ".initrd", context.staging / context.config.output_split_initrd)
 
@@ -2369,15 +2433,27 @@ def check_tools(config: Config, verb: Verb) -> None:
             check_tool(config, "depmod", reason="generate kernel module dependencies")
 
         if want_efi(config) and config.unified_kernel_images == ConfigFeature.enabled:
-            check_ukify(
-                config,
-                version="254",
-                reason="build bootable images",
-                hint=(
-                    "Use ToolsTree=default to download most required tools including ukify automatically "
-                    "or use Bootable=no to create a non-bootable image which doesn't require ukify"
-                ),
-            )
+            if config.unified_kernel_image_profiles:
+                check_ukify(
+                    config,
+                    version="257",
+                    reason="build unified kernel image profiles",
+                    hint=(
+                        "Use ToolsTree=default to download most required tools including ukify "
+                        "automatically"
+                    ),
+                )
+            else:
+                check_ukify(
+                    config,
+                    version="254",
+                    reason="build bootable images",
+                    hint=(
+                        "Use ToolsTree=default to download most required tools including ukify "
+                        "automatically or use Bootable=no to create a non-bootable image which doesn't "
+                        "require ukify"
+                    ),
+                )
 
         if config.output_format in (OutputFormat.disk, OutputFormat.esp):
             check_systemd_tool(config, "systemd-repart", version="254", reason="build disk images")
index abac5df54fcf8444ac4500bce295961083d9e7b5..a80e4d45b0a0f2995b4d2f68266a8d20a5e0e9e6 100644 (file)
@@ -1590,6 +1590,7 @@ class Config:
     shim_bootloader: ShimBootloader
     unified_kernel_images: ConfigFeature
     unified_kernel_image_format: str
+    unified_kernel_image_profiles: list[Path]
     initrds: list[Path]
     initrd_packages: list[str]
     initrd_volatile_packages: list[str]
@@ -2549,6 +2550,15 @@ SETTINGS = (
         # default=
         help="Specify the format used for the UKI filename",
     ),
+    ConfigSetting(
+        dest="unified_kernel_image_profiles",
+        long="--uki-profile",
+        metavar="PATH",
+        section="Content",
+        parse=config_make_list_parser(delimiter=",", parse=make_path_parser()),
+        recursive_paths=("mkosi.uki-profiles/",),
+        help="Configuration files to generate UKI profiles",
+    ),
     ConfigSetting(
         dest="initrds",
         long="--initrd",
@@ -4395,6 +4405,7 @@ def summary(config: Config) -> str:
                     Shim Bootloader: {config.shim_bootloader}
               Unified Kernel Images: {config.unified_kernel_images}
         Unified Kernel Image Format: {config.unified_kernel_image_format}
+      Unified Kernel Image Profiles: {line_join_list(config.unified_kernel_image_profiles)}
                             Initrds: {line_join_list(config.initrds)}
                     Initrd Packages: {line_join_list(config.initrd_packages)}
            Initrd Volatile Packages: {line_join_list(config.initrd_volatile_packages)}
index 7b181267a916ced57083005a51eadaf4848c4fed..3252f317dd6c56f84b38f16af3796bf7dbbb9305 100644 (file)
@@ -989,6 +989,14 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     | `&h`      | `roothash=` or `usrhash=` value of kernel argument |
     | `&c`      | Number of tries used for boot attempt counting     |
 
+`UnifiedKernelImageProfiles=`, `--uki-profile=`
+:   Build additional UKI profiles. Takes a comma separated list of paths
+    to `ukify` config files. This option may be used multiple times in
+    which case each config gets built into a corresponding UKI profile.
+    Config files in the `mkosi.uki-profiles/` directory are
+    automatically picked up. All configured UKI profiles are added as
+    additional UKI profiles to each UKI built by mkosi.
+
 `PeAddons=`, `--pe-addon`
 :   Build additional PE addons. Takes a comma separated list of paths to
     `ukify` config files. This option may be used multiple times in which case
index c27e1102f0d15b948ec99202ee066d69216cae04..4ce60ed4cdd2fd8cdee41c6d732bdad12ec0b855 100644 (file)
@@ -344,6 +344,9 @@ def test_config() -> None:
                 }
             ],
             "UnifiedKernelImageFormat": "myuki",
+            "UnifiedKernelImageProfiles": [
+                "/profile"
+            ],
             "UnifiedKernelImages": "auto",
             "UnitProperties": [
                 "PROPERTY=VALUE"
@@ -520,6 +523,7 @@ def test_config() -> None:
         tools_tree_release=None,
         tools_tree_repositories=["abc"],
         unified_kernel_image_format="myuki",
+        unified_kernel_image_profiles=[Path("/profile")],
         unified_kernel_images=ConfigFeature.auto,
         unit_properties=["PROPERTY=VALUE"],
         use_subvolumes=ConfigFeature.auto,