OSInside / kiwi

KIWI - Appliance Builder Next Generation
https://osinside.github.io/kiwi
GNU General Public License v3.0
306 stars 152 forks source link

efibootmgr is not called on the OEM expandable image #2140

Open ignatenkobrain opened 2 years ago

ignatenkobrain commented 2 years ago

Problem description

I'm trying to build expandable image for our physical servers and apart from many other issues I have, this one looks like a missing feature.

Expected behaviour

When firmware=uefi, I would expect that some part of kiwi installation would call to efibootmgr to actually install the boot entry, sadly this does not happen =(

Steps to reproduce the behaviour

Build CentOS Stream 9 image with kiwi, with firmware=uefi and try to install it on a physical hardware.

OS and Software information

ignatenkobrain commented 2 years ago

https://github.com/rhinstaller/anaconda/blob/1754afeb1e2fb7412fe97c1478fa8293d9c937f7/pyanaconda/modules/storage/bootloader/efi.py#L68-L109

this is how Anaconda handles this case.

ignatenkobrain commented 2 years ago

I think we should be able to check if efibootmgr is present in the image and from some dump dracut module we can call efibootmgr right away, it should not be a big deal.

ignatenkobrain commented 2 years ago

FTR this is how the helper script I use to run on the first boot:

#!/usr/bin/python3

import argparse
import json
import re
import shlex
import subprocess

LABEL = "CentOS Linux"

def remove(apply=False):
    r = subprocess.run(["efibootmgr"], check=True, capture_output=True, text=True)
    for l in r.stdout.splitlines():
        slot, label = l.split(None, 1)
        if label == LABEL:
            slot_id = re.match(r"^Boot([0-9a-fA-F]{4})", slot).group(1)
            cmd = ["efibootmgr", "-b", slot_id, "-B"]
            if apply:
                subprocess.run(
                    cmd,
                    check=True,
                    stdout=subprocess.DEVNULL,
                )
            else:
                print(shlex.join(cmd))

def add(apply=False):
    r = subprocess.run(
        ["lsblk", "-J", "-p", "-s"], check=True, capture_output=True, text=True
    )
    for bd in json.loads(r.stdout)["blockdevices"]:
        if "/boot/efi" in bd["mountpoints"]:
            disk = bd["children"][0]["name"]
            part_num = re.search(r"(\d+)$", bd["name"]).group(1)
            break
    else:
        raise Exception("Can't find /boot/efi mountpoint")

    cmd = [
        "efibootmgr",
        "-c",
        "-L",
        LABEL,
        "-d",
        disk,
        "-p",
        part_num,
        "-l",
        "\\EFI\\centos\\shimx64.efi",
    ]
    if apply:
        subprocess.run(
            cmd,
            check=True,
            stdout=subprocess.DEVNULL,
        )
    else:
        print(shlex.join(cmd))

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--apply", action="store_true")
    args = parser.parse_args()
    remove(args.apply)
    add(args.apply)

if __name__ == "__main__":
    main()