From 480463226816a572e8cafdcdfcead4671d11d00f Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Thu, 9 Jan 2020 15:48:30 +0100 Subject: [PATCH] stages/grub2: support for hybrid booting In the case that the image should support booting via EFI *and* legacy grub, i.e. hybrid booting, the canonical grub config is stored in /boot/grub2 just as for normal legacy booting. The config file for efi grub is a very small one that will just look for the partition containing the /boot/grub2/grub.cfg file and use then set the prefix accordingly and load that file. In the hybrid case grubenv file also will just be located in /boot/grub2 and not in the ESP therefore the symlink that was created by the package needs to be removed. --- stages/org.osbuild.grub2 | 57 ++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/stages/org.osbuild.grub2 b/stages/org.osbuild.grub2 index 2a3b57f7..61bd0efd 100755 --- a/stages/org.osbuild.grub2 +++ b/stages/org.osbuild.grub2 @@ -110,6 +110,15 @@ def write_grub_cfg(tree, path): "blscfg\n") +def write_grub_cfg_redirect(tree, path): + """Write a grub config pointing to the other cfg""" + print("hybrid boot support enabled. Writing alias grub config") + with open(os.path.join(tree, path), "w") as cfg: + cfg.write("search --no-floppy --set prefix --file /boot/grub2/grub.cfg\n" + "set prefix=($prefix)/boot/grub2\n" + "configfile $prefix/grub.cfg\n") + + def main(tree, options): root_fs_uuid = options["root_fs_uuid"] kernel_opts = options.get("kernel_opts", "") @@ -120,34 +129,60 @@ def main(tree, options): if isinstance(legacy, bool) and legacy: legacy = "i386-pc" + # Check if hybrid boot support is requested, i.e. the resulting image + # should support booting via legacy and also UEFI. In that case the + # canonical grub.cfg and the grubenv will be in /boot/grub2. The ESP + # will only contain a small config file redirecting to the one in + # /boot/grub2 and will not have a grubenv itself. + hybrid = uefi and legacy + # Create the configuration file that determines how grub.cfg is generated. os.makedirs(f"{tree}/etc/default", exist_ok=True) with open(f"{tree}/etc/default/grub", "w") as default: default.write("GRUB_TIMEOUT=0\n" "GRUB_ENABLE_BLSCFG=true\n") + os.makedirs(f"{tree}/boot/grub2", exist_ok=True) - with open(f"{tree}/boot/grub2/grubenv", "w") as env: + grubenv = f"{tree}/boot/grub2/grubenv" + + if hybrid: + # The rpm grub2-efi package will have installed a symlink from + # /boot/grub2/grubenv to the ESP. In the case of hybrid boot we + # want a single grubenv in /boot/grub2; therefore remove the link + try: + os.unlink(grubenv) + except FileNotFoundError: + pass + + with open(grubenv, "w") as env: env.write("# GRUB Environment Block\n" f"GRUB2_ROOT_FS_UUID={root_fs_uuid}\n" f"GRUB2_BOOT_FS_UUID={root_fs_uuid}\n" f"kernelopts=root=UUID={root_fs_uuid} {kernel_opts}\n") + if uefi is not None: + # UEFI support: + # The following files are needed for UEFI support: + # /boot/efi/EFI// + # - grubenv: in the case of non-hybrid boot it should have + # been written to via the link from /boot/grub2/grubenv + # created by grub2-efi-{x64, ia32}.rpm + # - grub.cfg: needs to be generated, either the canonical one + # or a shim one that redirects to the canonical one in + # /boot/grub2 in case of hybrid boot (see above) + vendor = uefi["vendor"] + grubcfg = f"boot/efi/EFI/{vendor}/grub.cfg" + if hybrid: + write_grub_cfg_redirect(tree, grubcfg) + else: + write_grub_cfg(tree, grubcfg) + if legacy: write_grub_cfg(tree, "boot/grub2/grub.cfg") copy_modules(tree, legacy) copy_font(tree) - if uefi is not None: - # UEFI support: - # The following files are needed for UEFI support: - # /boot/efi/EFI//{grubenv, grub.cfg} - # - grubenv should have been written to via the link from - # /boot/grub2/grubenv created by grub2-efi-{x64, ia32}.rpm - # - grub.cfg needs to be generated - vendor = uefi["vendor"] - write_grub_cfg(tree, f"boot/efi/EFI/{vendor}/grub.cfg") - return 0