Skip to main content

Multi-boot from ISO on USB with Grub 2

Last time we've talked about how to Manually UEFI Boot Linux and Windows 10 USB from Grub, today we'd like to talk about creating a multi-boot usb stick with grub 2.

Sometimes we need to create multiple linux distribution installation media or bootable usb stick. However, it is always so annoying to re-flush the usb stick again and again.

However, grub actually enables us to boot most of the linux distributions through loop format or iso image files. The iso image format is known for its original design for CD disks or DVD disks so that the distributions are bootable through your CD-ROM drivers. If the distribution is bootable through the CD-ROM drivers and the CD or DVD media is not writable, which it effectively means that we can actually boot from the non-writable iso image file as well.

Requirements

Since we need to boot multiple ISO image files directly from USB, we do really need a large USB stick. Secondly, we need to use a UEFI compatible system to avoid issues and maximize flexibility. Finally, we need to have a working linux distribution to work on which currently I'm using Ubuntu 18.04.2 demoing this tutorial.

  1. Large USB stick
  2. UEFI compatible system
  3. A Linux distribution to work on

We need to create the USB stick's partition table as gpt format and further make two partitions. We may refer to the following partition layout as an example.

$ sudo parted /dev/sdb print free
Model: SanDisk Ultra USB 3.0 (scsi)
Disk /dev/sdb: 61.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name       Flags
        17.4kB  1049kB  1031kB  Free Space
 1      1049kB  256MB   255MB   fat32        ESP        boot, esp
 2      256MB   61.5GB  61.3GB  ext4         OS-Images

We have created an ext4 filesystem partition named OS-Images for ISO image files because fat32 has a maximum file size limit of 4GB.

We now should create separate directories to mount these two partitions and copy over the ISO image files into OS-Images partition which should look very similar to the following layout.

.
├── ESP
└── OS-Images
    ├── ArchLinux
    │   └── archlinux-2019.06.01-x86_64.iso
    ├── CentOS
    │   ├── CentOS-7-x86_64-DVD-7.5.1804.iso
    │   └── CentOS-7-x86_64-DVD-7.6.1810.iso
    ├── Fedora
    │   └── Fedora-Workstation-Live-x86_64-30-1.2.iso
    ├── Redhat
    │   ├── RHEL-7.7-20190702.0-Workstation-x86_64-dvd1.iso
    │   └── RHEL-8.0.0-20190404.2-x86_64-dvd1-GA.iso
    ├── SLE
    │   ├── SLE-15-Installer-DVD-x86_64-GM-DVD1.iso
    │   └── SLE-15-SP1-Installer-DVD-x86_64-GMC-DVD1.iso
    └── Ubuntu
        ├── ubuntu-16.04.6-desktop-amd64.iso
        ├── ubuntu-18.04.2-desktop-amd64.iso
        └── ubuntu-19.04-desktop-amd64.iso

Create grub bootloader

We need to create a bootx64.efi to make sure the USB is bootable on a UEFI platform. Use the following command to generate a working grub bootloader.

$ grub-mkimage -o bootx64.efi -p /efi/boot -O x86_64-efi \
  all_videobootbtrfs cat chain configfile echo efi_gop   \
  efi_uga efifwsetup exfat ext2 fat gfxterm              \
  gfxterm_background gfxterm_menu hfsplus iso9660        \
  linux linuxefi loadenv loopback ls lsefi normal ntfs   \
  part_gpt part_msdos search search_fs_file              \
  search_fs_uuid search_label test udf usb

We need to further move the bootx64.efi to ESP/EFI/BOOT directory, backup it in the ESP/backup directory, and create a grub configuration file named grub.cfg.

Afterwards, the ESP partition should look like as following.

ESP
├── backup
│   └── bootx64.efi
└── EFI
    └── BOOT
        ├── bootx64.efi
        └── grub.cfg

Grub Boot Parameters in Detail

In grub.cfg, we can specify multiple menu entries to boot different ISO media files. Taking ubuntu 18.04.2 as an example:

insmod part_gpt
insmod ext2
insmod gzio

insmod efi_uga
insmod efi_gop

menuentry "Boot Ubuntu 18.04.2 from ISO" {
  set root=hd0,gpt2
  set isofile="/Ubuntu/ubuntu-18.04.2-desktop-amd64.iso"
  loopback loop $isofile
  linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject debug text
  initrd (loop)/casper/initrd
}

Ubuntu

menuentry "Boot Ubuntu 18.04.2 from ISO" {
  set root=hd0,gpt2
  set isofile="/Ubuntu/ubuntu-18.04.2-desktop-amd64.iso"
  loopback loop $isofile
  linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject debug text
  initrd (loop)/casper/initrd
}

Redhat

menuentry "Boot Redhat 7.7 (20190702) from ISO" {
  set root=hd0,gpt2
  set isofile="/Redhat/RHEL-7.7-20190702.0-Workstation-x86_64-dvd1.iso"
  loopback loop $isofile
  # BUG: Does not boot with LABEL
  # linuxefi (loop)/images/pxeboot/vmlinuz inst.stage2=cdrom:LABEL=OS-Images:$isofile rd.live.check debug
  linuxefi (loop)/images/pxeboot/vmlinuz inst.stage2=cdrom:sda2:$isofile rd.live.check debug
  initrdefi (loop)/images/pxeboot/initrd.img
}

CentOS

menuentry "Boot CentOS 7.6.1810 from ISO" {
  set root=hd0,gpt2
  set isofile="/CentOS/CentOS-7-x86_64-DVD-7.6.1810.iso"
  loopback loop $isofile
  # BUG: Does not boot with LABEL
  # linuxefi (loop)/images/pxeboot/vmlinuz inst.stage2=cdrom:LABEL=OS-Images:$isofile rd.live.check debug
  linuxefi (loop)/images/pxeboot/vmlinuz inst.stage2=cdrom:sda2:$isofile rd.live.check debug
  initrdefi (loop)/images/pxeboot/initrd.img
}

SUSE Linux Enterprise

menuentry "Boot SUSE 15-SP1-GMC from ISO" {
  set root=hd0,gpt2
  set isofile="/SLE/SLE-15-SP1-Installer-DVD-x86_64-GMC-DVD1.iso"
  loopback loop $isofile

  linuxefi (loop)/boot/x86_64/loader/linux splash=silent
  initrdefi (loop)/boot/x86_64/loader/initrd
}

Fedora

menuentry "Boot Fedora Workstation 30 from ISO" {
  set root=hd0,gpt2
  set isofile="/Fedora/Fedora-Workstation-Live-x86_64-30-1.2.iso"
  loopback loop $isofile
  linuxefi (loop)/images/pxeboot/vmlinuz root=live:CDLABEL=Fedora-WS-Live-30-1-2 rd.live.image iso-scan/filename=$isofile $iso_params debug text $_extra
  initrdefi (loop)/images/pxeboot/initrd.img
}

Archlinux

menuentry "Boot Archlinux 2019-06-01 from ISO" {
  set root=hd0,gpt2
  set isofile="/ArchLinux/archlinux-2019.06.01-x86_64.iso"
  loopback loop $isofile
  set imgdevpath='/dev/disk/by-partlabel/OS-Image'
  linux (loop)/arch/boot/x86_64/vmlinuz img_dev=$imgdevpath img_loop=$isofile earlymodules=loop debug text $_extra
  initrd (loop)/arch/boot/amd_ucode.img (loop)/arch/boot/intel_ucode.img (loop)/arch/boot/x86_64/archiso.img
}

Fixing a grub boot

After installing an OS, it is possible that the bootloader is corrupted after some hacking.

We can boot to a live CD using our USB stick and then follow the commands listed below to fix the grub bootloader.

sudo mount /dev/sdXY /mnt
sudo mount /dev/sdXZ /mnt/boot/efi

sudo mount --bind /dev /mnt/dev
sudo mount --bind /dev/pts /mnt/dev/pts
sudo mount --bind /proc /mnt/proc
sudo mount --bind /sys /mnt/sys

sudo chroot /mnt

grub-install --efi-directory /boot/efi /dev/sdX
grub-install --efi-directory /boot/efi --recheck /dev/sdX

# In case `update-grub` command is not found,
# use the following command to generate the grub configuration file.
# `grub-mkconfig -o /boot/grub/grub.cfg`
update-grub

exit
sudo umount /mnt/sys
sudo umount /mnt/proc
sudo umount /mnt/dev/pts
sudo umount /mnt/dev
sudo umount /mnt

 

Comments

Comments powered by Disqus