A few tweaks to GRUB2 make booting from an ISO relatively easy.
I recently worked on a project to add an OEM-style rescue partition to a computer. Where most OEM installs have a custom program that just rewrites an install image over the top of the partition, in this case, everything was based on open-source software. This rescue partition would be only a few gigabytes in size—big enough to contain the install DVD ISO image and a few preseed files to help automate the install. If users ever wanted to get back to the factory-installed version of the operating system, they could select a special option from the GRUB menu that would boot off the ISO and start the install as though they had used a USB key or DVD.
If you read some past guides on how to boot an ISO from GRUB, you will find a number of pretty complicated instructions in some cases because they were writing for older versions of GRUB. With the recent versions of GRUB2, booting from a standard ISO is fortunately not that complicated. I base my example here off a recent Debian Stretch install DVD, but the same steps should work for other distributions and install ISOs with some tweaks.
The first step is to create the rescue partition. In my case, I automated my install with a preseed script, but basically I just created a partition that was big enough to hold the install DVD. I made sure to give it a label so I could tell it apart from other disks later on and made it the second partition on the disk. Because I was doing all of this via an automated means, I ended up using dd to create an image of the install DVD that was currently in use as an end-of-install script and dumped it to the root of that partition in a file called install_dvd1.iso. Of course, you could just copy over your ISO directly to the disk if you do this from a system that already has an OS on it.
The next step was to build a GRUB config that would mount the ISO loopback and boot off the kernel and initrd file within that ISO. On Debian-based systems, you can add bash scripts that output extra GRUB configuration to /etc/grub.d/ and run update-grub to build a new grub.cfg file. But, you also could just edit grub.cfg directly or otherwise use your distribution's GRUB configuration scripts to add the following menu items:
set root='hd0,msdos2' set isofile="/install_dvd1.iso" menuentry "Install OS" { loopback loop (hd0,msdos2)$isofile linux (loop)/install.amd/vmlinuz vga=788 auto=true ↪file=/media/preseed.cfg -- quiet initrd (loop)/install.amd/initrd.gz } menuentry "Install OS (Expert)" { loopback loop (hd0,msdos2)$isofile linux (loop)/install.amd/vmlinuz vga=788 -- quiet initrd (loop)/install.amd/initrd.gz }
Let's break this GRUB configuration down a bit. First, you set two variables: the root partition GRUB will use (the second partition on the first disk, which GRUB refers to as hd0,msdos2) and the ISO file:
set root='hd0,msdos2' set isofile="/install_dvd1.iso"
Next are two separate GRUB menus (each are within menuentry {} stanzas). The first points to a preseed configuration file and starts a partially automated Debian install, while the other just boots the Debian installer with no preseed file and acts like an “Expert” mode so you can choose every install option by hand.
Up to this point, the GRUB options were pretty similar to other GRUB configurations, but here is where that changes. Next in each menu, you define a loopback device GRUB will use:
loopback loop (hd0,msdos2)$isofile
Now, whenever you refer to (loop) further in the config, GRUB knows to access the loopback filesystem labeled loop, which points to (hd0,msdos2)/install_dvd1.iso. The next two lines should look pretty familiar to someone who has worked with Linux GRUB configuration before, but with a twist:
linux (loop)/install.amd/vmlinuz vga=788 auto=true ↪file=/media/preseed.cfg -- quiet initrd (loop)/install.amd/initrd.gz
In the first line, you define what kernel to boot and what options to pass it, and in the following line, you point GRUB to the initrd file you want it to use. The main difference here though is that you precede each file path with (loop) to instruct GRUB to look in that loopback filesystem for the file.
Once this configuration finds its way into grub.cfg, you should see the two new menu options in place the next time you boot. Now the first time I tried to boot the most recent Debian installer this way, I ran into a bit of a problem. It turns out that the initrd that comes on the ISO itself does not contain the installer scripts you need to install from an ISO on a hard drive. It assumes you will boot only off a DVD or USB disk. Because of that, I discovered I had to download a different Debian installer initrd and put it on the rescue disk for things to work. I was able to find an initrd that worked at mirrors.kernel.org/debian/dists/stretch/main/installer-amd64/current/images/hd-media/initrd.gz.
Of course, with that new initrd.gz file at the root of my rescue partition, I had to change my GRUB config a little bit to point to it:
set root='hd0,msdos2' set isofile="/install_dvd1.iso" menuentry "Install OS" { loopback loop (hd0,msdos2)$isofile linux (loop)/install.amd/vmlinuz vga=788 auto=true ↪file=/media/preseed.cfg -- quiet initrd /initrd.gz } menuentry "Install OS (Expert)" { loopback loop (hd0,msdos2)$isofile linux (loop)/install.amd/vmlinuz vga=788 -- quiet initrd /initrd.gz }
Note that the initrd lines now point to /initrd.gz. Since I already defined the root variable earlier in the config, it knows to look on hd0,msdos2 for the file. Once this new initrd was in place, I was in business, and the installer worked as expected. If you want to use this with a different installer, just make sure that once the kernel boots up, it has the ability to scan disks for an installer ISO to use.