]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add grub EFI support 1797/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 18 Aug 2023 11:58:08 +0000 (13:58 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 21 Aug 2023 10:35:36 +0000 (12:35 +0200)
Note that we only generate the necessary menu entries for the grub
configuration to chainload into our generated UKIs, we do not yet
install grub for EFI ourselves as this is a distribution specific
mess that we still need to figure out. On Fedora, because the
shim and grub2-efi packages install directly to /boot which we
redirect to /efi, this is sufficient to boot with grub on EFI by
simply installing the shim and grub2-efi packages.

For other distributions, a post install or finalize script will be
necessary that installs grub (and optionally shim) to the correct
locations in the ESP.

mkosi/__init__.py
mkosi/config.py
mkosi/resources/mkosi.md

index 0ed91837e08e9b6b135319bd67e01d112db56cf6..0c82ec4d84bf601671ddd8109148156491396756 100644 (file)
@@ -581,6 +581,22 @@ def find_grub_prefix(state: MkosiState) -> Optional[str]:
     return "grub2" if "grub2" in os.fspath(path) else "grub"
 
 
+def want_grub_efi(state: MkosiState) -> bool:
+    if state.config.bootable == ConfigFeature.disabled:
+        return False
+
+    if state.config.bootloader != Bootloader.grub:
+        return False
+
+    if not any((state.root / "efi").rglob("grub*.efi")):
+        if state.config.bootable == ConfigFeature.enabled:
+            die("A bootable EFI image with grub was requested but grub for EFI is not installed in /efi")
+
+        return False
+
+    return True
+
+
 def want_grub_bios(state: MkosiState, partitions: Sequence[Partition] = ()) -> bool:
     if state.config.bootable == ConfigFeature.disabled:
         return False
@@ -637,9 +653,42 @@ def prepare_grub_config(state: MkosiState) -> Optional[Path]:
         with umask(~0o600), config.open("w") as f:
             f.write("set timeout=0\n")
 
+    # Signed EFI grub shipped by distributions reads its configuration from /EFI/<distribution>/grub.cfg in
+    # the ESP so let's put a shim there to redirect to the actual configuration file.
+    efi = state.root / "efi/EFI" / state.config.distribution.name / "grub.cfg"
+    with umask(~0o700):
+        efi.parent.mkdir(parents=True, exist_ok=True)
+
+    # Read the actual config file from the root of the ESP.
+    efi.write_text(f"configfile /{prefix}/grub.cfg\n")
+
     return config
 
 
+def prepare_grub_efi(state: MkosiState) -> None:
+    if not want_grub_efi(state):
+        return
+
+    config = prepare_grub_config(state)
+    assert config
+
+    with config.open("a") as f:
+        f.write('if [ "${grub_platform}" == "efi" ]; then\n')
+
+        for uki in (state.root / "efi/EFI/Linux").glob("*.efi"):
+            f.write(
+                textwrap.dedent(
+                    f"""\
+                    menuentry "{uki.stem}" {{
+                        chainloader /{uki.relative_to(state.root / "efi")}
+                    }}
+                    """
+                )
+            )
+
+        f.write("fi\n")
+
+
 def prepare_grub_bios(state: MkosiState, partitions: Sequence[Partition]) -> None:
     if not want_grub_bios(state, partitions):
         return
@@ -1744,6 +1793,7 @@ def build_image(args: MkosiArgs, config: MkosiConfig) -> None:
 
         partitions = make_image(state, skip=("esp", "xbootldr"))
         install_unified_kernel(state, partitions)
+        prepare_grub_efi(state)
         prepare_grub_bios(state, partitions)
         partitions = make_image(state)
         install_grub_bios(state, partitions)
index 997734b4b1d5d74641e0967b3b8271c4abd23ff5..2cbbad51ac95ae8cf808c55df31d43c6ec5d5972 100644 (file)
@@ -119,6 +119,7 @@ class Bootloader(StrEnum):
     none         = enum.auto()
     uki          = enum.auto()
     systemd_boot = enum.auto()
+    grub         = enum.auto()
 
 
 class BiosBootloader(StrEnum):
index 3b7f71cdbc1e287924034010ec34684c1746689a..1818da0b4713a6fa6028d631ce69e89d0767b5a5 100644 (file)
@@ -783,13 +783,29 @@ they should be specified with a boolean argument: either "1", "yes", or "true" t
 
 `Bootloader=`, `--bootloader=`
 
-: Takes one of `none`, `systemd-boot` or `uki`. Defaults to
+: Takes one of `none`, `systemd-boot`, `uki` or `grub`. Defaults to
   `systemd-boot`. If set to `none`, no EFI bootloader will be installed
   into the image. If set to `systemd-boot`, systemd-boot will be
   installed and for each installed kernel, a UKI will be generated and
   stored in `EFI/Linux` in the ESP. If set to `uki`, a single UKI will
   be generated for the latest installed kernel (the one with the highest
-  version) which is installed to `EFI/BOOT/BOOTX64.EFI` in the ESP.
+  version) which is installed to `EFI/BOOT/BOOTX64.EFI` in the ESP. If
+  set to `grub`, for each installed kernel, a UKI will be generated and
+  stored in `EFI/Linux` in the ESP. For each generated UKI, a menu entry
+  is appended to the grub configuration in `grub/grub.cfg` in the ESP
+  which chainloads into the UKI. A shim grub.cfg is also written to
+  `EFI/<distribution>/grub.cfg` in the ESP which loads `grub/grub.cfg`
+  in the ESP for compatibility with signed versions of grub which load
+  the grub configuration from this location.
+
+: Note that we do not yet install grub to the ESP when `Bootloader=` is
+  set to `grub`. This has to be done manually in a postinst or finalize
+  script. The grub EFI binary should be installed to
+  `/efi/EFI/BOOT/BOOTX64.EFI` (or similar depending on the architecture)
+  and should be configured to load its configuration from
+  `EFI/<distribution>/grub.cfg` in the ESP. Signed versions of grub
+  shipped by distributions will load their configuration from this
+  location by default.
 
 `BiosBootloader=`, `--bios-bootloader=`