Create a grub script that auto detect EFI systems, and boot first one

1.9k views Asked by At

I am working on an opensource project, bluebanquise, that aims to deploy baremetal infrastructures, and now that we support RHEL/CentOS 8 distributions, I wish to add other RHEL derivative distributions (Rocky Linux, Oracle Linux, Cloud Linux, etc etc), but also in the future other non RHEL distributions (Ubuntu, OpenSuse, etc).

By default, on our infrastructure, all servers boot over PXE by default. DHCP provides an iPXE rom that chains to other ipxe files, etc. At some point in this chain, if server has to boot on disk (so not deploying or booting in diskless), and if server is in EFI, iPXE chain to an grub2 image with an embed script that attempt to detect already installed OS and boot the first one found.

Note: Maybe this is not the best solution to boot from iPXE to disk in EFI. But I found nothing else.

Script is the following: grub2-efi-autofind.cfg

Basically, it searches for grub.cfg file for RedHat or CentOS Linux, and if found uses them to boot. But now, I would like to make this script more "generic", so it can bypass the need of the distribution name, and only look for any /efi/*/grub.cfg file.

However, I fail to create a script able to do that :-(

I found an interesting script on this page, but I fail to adapt the for loop to my need. I tried to use it alone:

     for efi in (*,gpt*)/efi/*/grub.cfg ; do
        regexp --set=1:efi_device '^\((.*)\)/' "${efi}"
        echo found efi
        echo "${efi}"
        echo "${efi_device}"
        echo "${2}"
        echo "${1}"
        sleep --interruptible --verbose 10
    done

But this does not output anything except "(,gpt)/efi/*/grub.cfg"

I tried to use the plain code of this page, and it indeed detect the file, but after few iterations in the automatic menu (I need to press twice on Detect EFI bootloaders to get the correct path). And this is not automatic.

The GRUB2 documentation does not have a lot of examples related to this, and I do not find a lot of exchanges on this matter over the web.

If one of you know a better way to boot an EFI system on disk from an iPXE rom, or if you know how to replace a search.file with a static path by a way to generically detect any grub.cfg in efi partitions, I would be very glad to read about it ! :-)

Thanks a lot for reading this, even if you do not have an answer.

With my best regards

Ox

2

There are 2 answers

1
JBE On BEST ANSWER

Here is a simple GRUB script that searches for the first "disk" containing a file at /EFI/BOOT/BOOTX64.EFI and then conditionally display a menu entry to boot from it:

search --no-floppy --file --set=bootable_efi /EFI/BOOT/BOOTX64.EFI
if [ -n "${bootable_efi}" ]; then
  menuentry "EFI Bootable USB" --class unknown {
    set root="${bootable_efi}"
    chainloader /EFI/BOOT/BOOTX64.EFI
    boot
  }
fi
0
oxedions On

I finally found a way to do it, adding a lot of echo and sleep, I was able to find a pattern that works:

echo " Loading modules..."
insmod part_gpt
insmod fat
insmod chain
insmod part_msdos
insmod ext2
echo
echo "Scanning, first pass..."
for efi in (*,gpt*)/efi/*/grub.cfg (*,gpt*)/efi/*/*/grub.cfg (*,gpt*)/grub.cfg (*,gpt*)/*/grub.cfg ; do
                regexp --set=1:efi_device '^\((.*)\)/' "${efi}"
done
echo "Scanning, second pass..."
for efi in (*,gpt*)/efi/*/grub.cfg (*,gpt*)/efi/*/*/grub.cfg (*,gpt*)/grub.cfg (*,gpt*)/*/grub.cfg ; do
                regexp --set=1:efi_device '^\((.*)\)/' "${efi}"
                if [ -e "${efi}" ]; then
                    efi_found=true
                    echo " >> Found operating system! <<"
                    echo " Path: ${efi}"
                    echo " Booting in 5s..."
                    sleep --interruptible --verbose 5
                    configfile "${efi}"
                    boot
                fi
done

However, while it works, I do not understand why... Basically, the first time it does the for loop, grub finds nothing. The second time it does the loop, files are found. So workaround here is to do a first time the loop, doing nothing, then redo it another time with conditional and boot.

If one day someone understand what is going on here, I am very interested :-) For now, it works like a charm as is.