]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Allow booting cpio images in qemu
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 19 Apr 2023 13:48:47 +0000 (15:48 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 19 Apr 2023 14:04:34 +0000 (16:04 +0200)
Let's add back some form of direct linux boot by allowing to boot
cpio images. To make this work, either the user has to pass -kernel
or a kernel has to be installed inside the cpio, which we'll copy
out and use as the -kernel argument.

mkosi/__init__.py
mkosi/backend.py

index aa5e867ac4a5d2dad5cba10a6aaae96407db6af2..11dbb173ec4ffdb5f53a1a7935eacead692a80de 100644 (file)
@@ -715,6 +715,10 @@ def install_unified_kernel(state: MkosiState, roothash: Optional[str]) -> None:
     if state.for_cache or state.config.bootable is False:
         return
 
+    for kver, kimg in gen_kernel_images(state):
+        copy_path(state.root / kimg, state.staging / state.config.output_split_kernel.name)
+        break
+
     if state.config.output_format == OutputFormat.cpio and state.config.bootable is None:
         return
 
@@ -793,10 +797,10 @@ def install_unified_kernel(state: MkosiState, roothash: Optional[str]) -> None:
 
             run(cmd)
 
-            if not state.staging.joinpath(state.config.output_split_kernel.name).exists():
-                copy_path(boot_binary, state.staging / state.config.output_split_kernel.name)
+            if not state.staging.joinpath(state.config.output_split_uki.name).exists():
+                copy_path(boot_binary, state.staging / state.config.output_split_uki.name)
 
-    if state.config.bootable is True and not state.staging.joinpath(state.config.output_split_kernel.name).exists():
+    if state.config.bootable is True and not state.staging.joinpath(state.config.output_split_uki.name).exists():
         die("A bootable image was requested but no kernel was found")
 
 
@@ -970,6 +974,12 @@ def unlink_output(config: MkosiConfig) -> None:
                     unlink_try_hard(p)
         unlink_try_hard(config.output_split_kernel)
 
+        if config.output_split_uki.parent.exists():
+            for p in config.output_split_uki.parent.iterdir():
+                if p.name.startswith(config.output_split_uki.name):
+                    unlink_try_hard(p)
+        unlink_try_hard(config.output_split_uki)
+
         if config.nspawn_settings is not None:
             unlink_try_hard(config.output_nspawn_settings)
 
@@ -1085,17 +1095,9 @@ def load_credentials(args: argparse.Namespace) -> dict[str, str]:
 
 
 def load_kernel_command_line_extra(args: argparse.Namespace) -> list[str]:
-    cmdline = []
-
-    for s in args.kernel_command_line_extra:
-        key, sep, value = s.partition("=")
-        if " " in value:
-            value = f'"{value}"'
-        cmdline += [key if not sep else f"{key}={value}"]
-
     columns, lines = shutil.get_terminal_size()
 
-    cmdline += [
+    cmdline = [
         f"systemd.tty.term.hvc0={os.getenv('TERM', 'vt220')}",
         f"systemd.tty.columns.hvc0={columns}",
         f"systemd.tty.rows.hvc0={lines}",
@@ -1105,6 +1107,15 @@ def load_kernel_command_line_extra(args: argparse.Namespace) -> list[str]:
         "console=hvc0",
     ]
 
+    if args.output_format == OutputFormat.cpio:
+        cmdline += ["rd.systemd.unit=default.target"]
+
+    for s in args.kernel_command_line_extra:
+        key, sep, value = s.partition("=")
+        if " " in value:
+            value = f'"{value}"'
+        cmdline += [key if not sep else f"{key}={value}"]
+
     return cmdline
 
 
@@ -1122,7 +1133,6 @@ def load_args(args: argparse.Namespace) -> MkosiConfig:
         OutputFormat.directory,
         OutputFormat.subvolume,
         OutputFormat.tar,
-        OutputFormat.cpio,
     ):
         die("Directory, subvolume, tar, cpio, and plain squashfs images cannot be booted in qemu.")
 
@@ -1225,10 +1235,6 @@ def load_args(args: argparse.Namespace) -> MkosiConfig:
         if args.compress_output != Compression.none:
             die(f"Sorry, can't {opname} with a compressed image.")
 
-    if args.verb == Verb.qemu:
-        if not args.output_format == OutputFormat.disk:
-            die("Sorry, can't boot non-disk images with qemu.")
-
     if args.repo_dirs and not (is_dnf_distribution(args.distribution) or args.distribution == Distribution.arch):
         die("--repo-dir is only supported on DNF based distributions and Arch")
 
@@ -2242,6 +2248,15 @@ def run_qemu(config: MkosiConfig) -> None:
             fname = config.output_compressed
 
         # Debian images fail to boot with virtio-scsi, see: https://github.com/systemd/mkosi/issues/725
+        if config.output_format == OutputFormat.cpio:
+            kernel = (config.output_dir or Path.cwd()) / config.output_split_kernel
+            if not kernel.exists() and "-kernel" not in config.cmdline:
+                die("No kernel found, please install a kernel in the cpio or provide a -kernel argument to mkosi qemu")
+            cmdline += [
+                "-kernel", kernel,
+                "-initrd", fname,
+                "-append", " ".join(config.kernel_command_line + config.kernel_command_line_extra),
+            ]
         if config.distribution == Distribution.debian:
             cmdline += [
                 "-drive",
index 0e452d78d06d85e8f64a15f68b5ce52b875e44bc..5a70728e4a02e2cb598165af96048316b8115020 100644 (file)
@@ -294,9 +294,13 @@ class MkosiConfig:
         return self.architecture == platform.machine()
 
     @property
-    def output_split_kernel(self) -> Path:
+    def output_split_uki(self) -> Path:
         return build_auxiliary_output_path(self, ".efi")
 
+    @property
+    def output_split_kernel(self) -> Path:
+        return build_auxiliary_output_path(self, ".vmlinuz")
+
     @property
     def output_nspawn_settings(self) -> Path:
         return build_auxiliary_output_path(self, ".nspawn")
@@ -331,6 +335,7 @@ class MkosiConfig:
     def output_paths(self) -> tuple[Path, ...]:
         return (
             self.output,
+            self.output_split_uki,
             self.output_split_kernel,
             self.output_nspawn_settings,
             self.output_checksum,