Skip to content. | Skip to navigation

Navigation

You are here: Home / Support / Guides / Tools / Multiboot USB / grub.cfg

Personal tools

Multiboot USB

Booting from multiple ISOs on one USB

grub.cfg

Finally, the GRUB2 configuration.

What this file contains is one thing and where it lives is another.

Where

The currently running CentOS 7.2 image, a regular install, has its grub.cfg in the ESP partition next to the .efi file:

# find /boot -name grub\*
/boot/efi/EFI/centos/grubenv
/boot/efi/EFI/centos/grubx64.efi
/boot/efi/EFI/centos/grub.cfg
/boot/grub
/boot/grub2
/boot/grub2/grubenv

Note

CentOS used a bootloader-id of CentOS although the directory on the FAT filesystem was created as centos.

This is slightly confusing as GRUB2 seems to expect the grub.cfg on the USB stick to be in .../boot/grub2.

Trial and error suggests we should be targeting .../boot/grub2/grub.cfg.

What

First Pass

The first time through we might as well get GRUB2 to create a sample file for us. Be warned, though, that the code is simplistic and rummages about in /boot for suitable entries to add. None of which, of course, are on our USB stick.

It does however, get us a decent starter for ten:

grub2-mkconfig -o /tmp/usb-sda/boot/grub2/grub.cfg

You'll see that it finds various linux and initrd images which it adds as GRUB menu options. It'll find roughly as many as you have existing GRUB menu options to boot from!

We can go and look at this and down near the bottom will be the menuentry sections we want to replace. For example:

menuentry 'CentOS Linux (3.10.0-327.10.1.el7.x86_64) 7 (Core)' --class rhel fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-327.10.1.el7.x86_64-advanced-c7de80b1-a4f6-43d8-bc3f-680b9284a0e7' {
       load_video
       set gfxpayload=keep
       insmod gzio
       insmod part_gpt
       insmod xfs
       if [ x$feature_platform_search_hint = xy ]; then
         search --no-floppy --fs-uuid --set=root  b48e0b11-72ad-4aa6-8103-2411ce84fa1b
       else
         search --no-floppy --fs-uuid --set=root b48e0b11-72ad-4aa6-8103-2411ce84fa1b
       fi
       linuxefi /vmlinuz-3.10.0-327.10.1.el7.x86_64 root=/dev/mapper/centos-root ro rd.lvm.lv=centos/root rd.lvm.lv=centos/swap crashkernel=auto rhgb quiet
       initrdefi /initramfs-3.10.0-327.10.1.el7.x86_64.img
}

We can roughly read that as:

menu entry 'Menu String' <some args> {
       <sort out the video; load_video is a function defined higher up>
       <load some modules>
       <look for a partition with some UUID and set the variable root to its value>
       linuxefi <vmlinuz file> <kernel args>
       initrdefi <initrd file>
}

Of those, there's a few interesting ones:

  • the filesystem UUID of the /boot directory

    Here, lsblk (or blkid) come to our rescue:

    # lsblk -f /dev/sda
    NAME   FSTYPE LABEL UUID                                 MOUNTPOINT
    sda
    ├─sda1 vfat         68DA-C96A                            /tmp/usb-sda/boot/efi
    └─sda2 xfs          dcdff2d3-fb08-420e-a60a-c5df939b497e /tmp/usb-sda
    

    Our USB stick's /boot folder's filesystem UUID is dcdff2d3-fb08-420e-a60a-c5df939b497e (ie. the UUID of the filesystem that has the /boot directory on it).

  • linuxefi ...

    There are a number of non-EFI linux* variants. We won't be covering them here. We will need to find the equivalent vmlinux file, though.

  • initrdefi ...

    Ditto for initrd*.

The Multiboot Trick

It's not a big trick, it's just that GRUB2 can mount ISO files as loop devices and then boot off the resultant loop mount device.

If you were typing everything by hand at the GRUB2 shell prompt, you might type:

set root=(hd0,gpt2)
loopback loop /CentOS-7-x86_64-DVD-1511.iso
linuxefi (loop)/isolinux/vmlinuz ...
initrd (loop)/isolinux/initrd.img

where (hd0,gpt2) is the second GPT partition on hd0 (how do you know it is hd0? Magic!) and the CentOS ISO is in the top-level of that disk (at the same level as the /boot directory). In addition we somehow know what the vmlinuz and initrd filenames are.

We can use some variables (which we'll need for later) and get GRUB to figure out the hard disk for us with:

set ISO="/CentOS-7-x86_64-DVD-1511.iso"
set FSUUID=dcdff2d3-fb08-420e-a60a-c5df939b497e
search --fs-uuid --set=root $FSUUID
loopback loop $ISO
linuxefi (loop)/isolinux/vmlinuz ...
initrdefi (loop)/isolinux/initrd.img

Filenames

The obvious way to discover the vmlinuz and initrd filenames is to mount the ISO and look for them:

# mkdir /tmp/iso
# mount -r CentOS-7-x86_64-DVD-1511.iso /tmp/iso
# find /tmp/iso -name vmlinuz\*
/tmp/iso/images/pxeboot/vmlinuz
/tmp/iso/isolinux/vmlinuz
# find /tmp/iso -name initrd\*
/tmp/iso/images/pxeboot/initrd.img
/tmp/iso/isolinux/initrd.img

(We'll ignore the PXE boot variants!)

Filenames are not consistent across Linux distributions or even across a distribution's variants:

  • Ubuntu 14.04 Desktop

    /casper/vmlinuz.efi and /casper/initrd.lz

  • Ubuntu 14.04 Server

    /install/vmlinuz and /install/initrd.gz

Obviously, copy the ISO files to the xfs partition:

cp *.iso /tmp/usb-sda

If you want to put the ISO files in a subdirectory, no problem. Just remember to insert the subdirectory in the pathname to the ISO files in the menuentry statements!

Kernel Arguments

There's a lot of hidden knowledge here.

Take whatever advice you can from various sources:

  • The ArchWiki covers a lot of distributions.
  • A gist on various menu entry variants

From these and other sources we might derive some flavours.

A CentOS Install

menuentry '[ISO] CentOS-7-x86_64-DVD-1511' --class rhel fedora --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-327.4.5.el7.x86_64-advanced-9e0fe5e8-33ab-4ea9-8393-fd8697662ae7' {
       set ISO="/CentOS-7-x86_64-DVD-1511.iso"
       set FSUUID="dcdff2d3-fb08-420e-a60a-c5df939b497e"
       search --fs-uuid --set=root $FSUUID
       loopback loop $ISO
       load_video
       set gfxpayload=keep
       insmod gzio
       insmod part_gpt
       insmod xfs
       linuxefi (loop)/isolinux/vmlinuz inst.stage2=hd:UUID=$FSUUID:$ISO inst.repo=hd:UUID=$FSUUID:$ISO rhgb quiet
       initrdefi (loop)/isolinux/initrd.img
}

A CentOS install requires both the inst.stage2 and inst.repo parameters.

An Ubuntu Server Live Image

menuentry "[ISO] Ubuntu 14.04 LTS (server)" {
       load_video
       set gfxpayload=keep
       set isofile="/ubuntu-14.04-server-amd64.iso"
       set FSUUID="dcdff2d3-fb08-420e-a60a-c5df939b497e"
       search --fs-uuid --set=root $FSUUID
       loopback loop $isofile
       insmod gzio
       insmod part_gpt
       insmod xfs
       linux (loop)/install/vmlinuz boot=install iso-scan/filename=$isofile liveimg noeject noprompt splash toram --
       initrd (loop)/install/initrd.gz
}

The key argument here is liveimg.

Ubuntu Desktop

Ubuntu Desktop is essentially the same as the server entry but the vmlinuz and initrd images are in the /casper directory, not the /install directory.

Skip to Next Boot Device

As exit is a valid GRUB2 command which takes us out of the current Boot device and therefore onto the next Boot device as defined in the EFI BootOrder setting.

menuentry "Boot from next device"  {
 load_video
 exit
}

Scripted grub.cfg Annoyances

As sensible people we might have written a script to rummage about in the ISO for sample menu entries to copy for each ISO.

CentOS 7 and now 8 have started using LABEL directives in their menu entries which means they don't have to mess about searching for root filesystems. Something like:

menuentry 'Install CentOS Stream 8.0.1905' --class fedora --class gnu-linux --class gnu --class os {
       linuxefi /images/pxeboot/vmlinuz inst.stage2=hd:LABEL=CentOS-Stream-BaseOS-x86_64 quiet
       initrdefi /images/pxeboot/initrd.img
}

True enough, the original ISO, if you mount it and run lsblk you'll see it:

# mount CentOS-Stream-x86_64-dvd1.iso /tmp/iso
# lsblk -f /dev/loop0
NAME  FSTYPE  LABEL                       UUID                                 MOUNTPOINT
loop0 iso9660 CentOS-Stream-BaseOS-x86_64 2019-09-20-15-46-31-00               /tmp/iso

That seems fine. However, we have a problem in multi-boot land if we were to naively copy this menu entry: our USB stick isn't labelled with the expected CentOS LABEL for this or any of the other ISO files we're stuffing on the disk.

There's a rather more fatal problem here when we boot. It'll start fine then at some point it'll try to access the kernel using the LABEL, will fail and eventually dracut will start spewing timeout errors and drop you into an emergency shell.

The upshot here is that we can't blindly copy menu entries from the ISO's grub.cfg. We can, however, modify them. In fact, we can throw in a bit of scripting support to help us. We'll know (read: can figure out) the UUID for the xfs partition we create and can twiddle the LABEL parts to refer back to the ISO via the UUID:

set USB_XFS_UUID="9ba5c614-45bc-4b76-a278-21fe709b2e61"

menuentry '[ISO] Install CentOS Stream 8.0.1905' --class fedora --class gnu-linux --class gnu --class os {
  set ISO="/CentOS-Stream-x86_64-dvd1.iso"
  loopback loop $ISO
  linuxefi (loop)/images/pxeboot/vmlinuz inst.stage2=hd:UUID=$USB_XFS_UUID:$ISO quiet
  initrdefi (loop)/images/pxeboot/initrd.img
}

Sweet!

Document Actions