diff --git a/src/cmd-buildextend-live b/src/cmd-buildextend-live index 6d271bad327a814ddde9415d9dc7662df109607c..b075c3c01ebbd5bc54c4d21d770ab14e24286ca2 100755 --- a/src/cmd-buildextend-live +++ b/src/cmd-buildextend-live @@ -19,8 +19,8 @@ import glob sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from cosalib.builds import Builds -from cosalib.cmdlib import run_verbose, sha256sum_file -from cosalib.cmdlib import import_ostree_commit, get_basearch +from cosalib.cmdlib import run_verbose, sha256sum_file, flatten_image_yaml +from cosalib.cmdlib import import_ostree_commit, get_basearch, ensure_glob from cosalib.meta import GenericBuildMeta @@ -407,7 +407,7 @@ def generate_iso(): elif basearch == "ppc64le": os.makedirs(os.path.join(tmpisoroot, 'boot/grub')) # can be EFI/fedora or EFI/redhat - grubpath = glob.glob(os.path.join(tmpisoroot, 'EFI/*/grub.cfg')) + grubpath = ensure_glob(os.path.join(tmpisoroot, 'EFI/*/grub.cfg')) shutil.move(grubpath[0], os.path.join(tmpisoroot, 'boot/grub/grub.cfg')) # safely remove things we don't need in the final ISO tree @@ -483,6 +483,60 @@ def generate_iso(): tmpimageefidir = os.path.join(tmpdir, "efi") ostree_extract_efi(repo, buildmeta_commit, tmpimageefidir) + # Find name of vendor directory + vendor_ids = [n for n in os.listdir(tmpimageefidir) if n != "BOOT"] + if len(vendor_ids) != 1: + raise Exception(f"did not find exactly one EFI vendor ID: {vendor_ids}") + + # Delete fallback and its CSV file. Its purpose is to create + # EFI boot variables, which we don't want when booting from + # removable media. + # + # A future shim release will merge fallback.efi into the main + # shim binary and enable the fallback behavior when the CSV + # exists. But for now, fail if fallback.efi is missing. + for path in ensure_glob(os.path.join(tmpimageefidir, "BOOT", "fb*.efi")): + os.unlink(path) + for path in ensure_glob(os.path.join(tmpimageefidir, vendor_ids[0], "BOOT*.CSV")): + os.unlink(path) + + # Drop vendor copies of shim; we already have it in BOOT*.EFI in + # BOOT + for path in ensure_glob(os.path.join(tmpimageefidir, vendor_ids[0], "shim*.efi")): + os.unlink(path) + + # Consolidate remaining files into BOOT. shim needs GRUB to be + # there, and the rest doesn't hurt. + for path in ensure_glob(os.path.join(tmpimageefidir, vendor_ids[0], "*")): + shutil.move(path, os.path.join(tmpimageefidir, "BOOT")) + os.rmdir(os.path.join(tmpimageefidir, vendor_ids[0])) + + # Inject a stub grub.cfg pointing to the one in the main ISO image. + # + # When booting via El Torito, this stub is not used; GRUB reads + # the ISO image directly using its own ISO support. This + # happens when booting from a CD device, or when the ISO is + # copied to a USB stick and booted on EFI firmware which prefers + # to boot a hard disk from an El Torito image if it has one. + # EDK II in QEMU behaves this way. + # + # This stub is used with EFI firmware which prefers to boot a + # hard disk from an ESP, or which cannot boot a hard disk via El + # Torito at all. In that case, GRUB thinks it booted from a + # partition of the disk (a fake ESP created by isohybrid, + # pointing to efiboot.img) and needs a grub.cfg there. + # + # IMPORTANT: For successful boot, please check exact path for + # grub.cfg in the first partition and change $prefix here. + # In most of the cases, like Fedora CoreOS, ($root)/EFI/{vendor[0]} + # will give you the right path, but there are exceptions. + with open(os.path.join(tmpimageefidir, "BOOT", "grub.cfg"), "w") as fh: + fh.write(f'''search --label "{volid}" --set root --no-floppy +set prefix=($root)/EFI/anolis +configfile $prefix/grub.cfg +boot +''') + # Install binaries from boot partition # Manually construct the tarball to ensure proper permissions and ownership efitarfile = tempfile.NamedTemporaryFile(suffix=".tar") diff --git a/src/cosalib/cmdlib.py b/src/cosalib/cmdlib.py index 7eaee371f3f3879be4cb037d6ed7162888bb4f25..0ba127055d9b615a0f1ca820d0721d9d1783e8a1 100644 --- a/src/cosalib/cmdlib.py +++ b/src/cosalib/cmdlib.py @@ -2,6 +2,7 @@ """ Houses helper code for python based coreos-assembler commands. """ +import glob import hashlib import json import os @@ -309,3 +310,55 @@ def image_info(image): return out except Exception as e: raise Exception(f"failed to inspect {image} with qemu", e) + +# Hackily run some bash code from cmdlib.sh helpers. +def cmdlib_sh(script): + subprocess.check_call(['bash', '-c', f''' + set -euo pipefail + source {THISDIR}/../cmdlib.sh + {script} + ''']) + + +def flatten_image_yaml_to_file(srcfile, outfile): + flattened = flatten_image_yaml(srcfile) + with open(outfile, 'w') as f: + yaml.dump(flattened, f) + + +def merge_lists(x, y, k): + x[k] = x.get(k, []) + assert type(x[k]) == list + y[k] = y.get(k, []) + assert type(y[k]) == list + x[k].extend(y[k]) + + +def flatten_image_yaml(srcfile, base=None): + if base is None: + base = {} + + with open(srcfile) as f: + srcyaml = yaml.safe_load(f) + + # first, special-case list values + merge_lists(base, srcyaml, 'extra-kargs') + merge_lists(base, srcyaml, 'ignition-network-kcmdline') + + # then handle all the non-list values + base = merge_dicts(base, srcyaml) + + if 'include' not in srcyaml: + return base + + fn = os.path.join(os.path.dirname(srcfile), srcyaml['include']) + del base['include'] + return flatten_image_yaml(fn, base) + + +def ensure_glob(pathname, **kwargs): + '''Call glob.glob(), and fail if there are no results.''' + ret = glob.glob(pathname, **kwargs) + if not ret: + raise Exception(f'No matches for {pathname}') + return ret diff --git a/src/create_disk.sh b/src/create_disk.sh index d5237e6d89dbf5ce6d6bea9134bfa074d62eb860..96b90344ac0f94570e0fe3729a82a7907d9dbd67 100755 --- a/src/create_disk.sh +++ b/src/create_disk.sh @@ -317,15 +317,16 @@ install_grub_cfg() { # Other arch-specific bootloader changes case "$arch" in x86_64) - install_grub_cfg - # And BIOS grub in addition. See also - # https://github.com/coreos/fedora-coreos-tracker/issues/32 - grub2-install \ - --fonts ascii \ - --target i386-pc \ - --install-modules 'test blscfg linux search configfile all_video serial gzio' \ - --boot-directory $rootfs/boot \ - $disk + install_uefi + if [ "${x86_bios_bootloader}" = 1 ]; then + # And BIOS grub in addition. See also + # https://github.com/coreos/fedora-coreos-tracker/issues/32 + grub2-install \ + --target i386-pc \ + --boot-directory $rootfs/boot \ + --modules mdraid1x \ + $disk + fi ;; aarch64) # Our aarch64 is UEFI only.