Dual-booting with EFI

Categories: Linux

Introduction

I recently installed Fedora-23 on my machine - twice, on two different partitions. That gives me one to work with, and one to experiment with - and hopefully gives me an available environment to boot into if one of my experiments goes badly wrong.

Sadly, something immediately went badly wrong :-). Fedora, EFI and dual-booting is not a good combination. However setting things up correctly isn’t too hard - see below.

Motivation

I’ve been using dual-booting for years on non-EFI systems - two (or sometimes three) Linux distributions installed concurrently on different partitions, with a shared partition for “/home”. This approach relied on each installation having its own grub binary and grub config-file. This is most easily achieved by not creating a separate boot partition, but instead leaving /boot as a normal directory in each installation’s root filesystem (yes, that does slightly increase the chance of damaging the grub setup itself).

The disk MBR then points to exactly one of these grub installations, ie that is the “master” grub install. The grub-mkconfig tool that generates the grub.cfg files does a good job of “probing” for kernels on other partitions, ie it is usually possible to boot into any install without problems.

When a new installation is done, that overwrites the MBR and becomes the “master”; running grub2-install or update-grub can manually select a “master” install. When performing OS updates that download a new kernel or similar, the grub.cfg for that installation is updated - ie does not mess with the grub.cfg for other installations.

There is a minor drawback that the “master” grub menus don’t boot into new kernels in other installations until something like “grub2-mkconfig” or “update-grub” is run in the master environment, but that’s acceptable for me.

EFI Dual-Booting

In the new EFI world, things work a little differently. There is one EFI partition to hold the grub binary and config-file - ie having a different “/boot” directory for each install no longer separates the grub config-files. On install, Fedora creates directory “/boot/efi/EFI/fedora” (ie “/EFI/fedora” on the EFI partition) and puts shim, grub, and grub.cfg there. It then creates a symlink to the grub.cfg from “/etc/grub2-efi.cfg”. Installing Fedora on a different partition repeats the process, and creates its own symlink to the same location.

Finally, this new boot possibility is stored in the EFI firmware tables.

The symlink /etc/grub2-efi.cfg is used by fedora-specific tool “grubby” when system updates require updating of grub’s config - eg when a new kernel arrives. The results are ugly - both installations are trying to use the same grub.cfg file. And unlike “grub2-mkconfig”, “grubby” doesn’t regenerate a new/clean grub.cfg file but instead parses and modifies the existing one. When that existing one was generated by the other install then things are not likely to end well.

The solution is to instead create two separate EFI boot environments, which will act more like the non-EFI approach I described above. The necessary steps are:

# become root
su -

# copy the default boot environment
cd /boot/efi/EFI
cp -a fedora myinstall1

# set up link for grubby
rm /etc/grub2-efi.cfg
ln -s /boot/efi/EFI/myinstall1/grub.cfg /etc/grub2-efi.cfg

# regenerate a clean grub config-file for the current environment
# (writes via the symlink to /boot/efi/EFI/myinstall1/grub.cfg)
grub2-mkconfig -o /etc/grub2-efi.cfg

# register the new boot-dir with EFI (and as side-effect make it the default)
efibootmgr --create --label myinstall1 --loader \\EFI\\myinstall1\\shim.efi

On reboot, the machine will now by default execute \EFI\myinstall1\shim.efi which runs grub from the same dir which uses the grub.cfg from the same dir.

There is some key-sequence that can be pressed immediately after boot (enter within 2 seconds on my machine), which will lead to a menu which then allows “alternate boot options” to be selected. The new entry (label=myinstall1) will be one of them.

Note that when booting into one environment, the grub menu will offer options to boot into kernels found on other partitions. However that information may not be up-to-date if the other install has downloaded a new kernel or similar. Personally, I think it would be nice if the “probing” option auto-detected other grub installs rather than just other kernels, and “chain-loaded” the target grub environment; that would ensure booting into the latest config for other partitions. However multibooting between multiple grub-enabled environments appears to be a low-priority use-case - multibooting into windows is probably the major one.

Broken grub os-probing in EFI

Sadly it appears that Grub generates broken grub.cfg files for multi-boot setups under EFI. When running in an EFI environment, menu-entries in the grub.cfg file need to use “linuxefi” to specify a kernel to execute, and “initrdefi” to specify an initrd/initramfs to load. This works for kernels in the host install, but the “os-probing” part that builds menu-entries for kernels on other disk partitions instead generates the traditional “linux” and “initrd” lines instead, and the menu-entry then generates error “unsupported command” when selected at boot-time.

This is being tracked via this fedora bugreport.

Other Information

When EFI is enabled, the role previously performed by the code in the MBR is instead performed by firmware. Data stored in (permanent but editable) flash-memory defines the paths to all “bootable executables”, which must be stored on an EFI-formatted partition (basically, msdos-format) which the EFI firmware can read directly rather than the ugly disk-blocklists approach used by MBR code.

On a Linux install booted via EFI, directory /sys/firmware/efi/efivars has a file per “EFI variable”. The ones named “Boot*” define the boot values.

The EFI partition does not have to be to be mounted in the booted operating-system. However if not mounted, then of course any tools that want to update the grub binary or configuration-file won’t be able to do so. The EFI partition is therefore normally mounted at /boot/efi.

The operating system kernel and associated files normally goes into /boot - which might be on the OS root partition, or might be elsewhere. In pre-EFI days, it was common to have “/boot” be a separate partition but with EFI there is less reason to do so.

For technical reasons the “EFI boot options” don’t point direct to grub but instead to a shim program. EFI supports a “secure mode” in which it only boots programs if they have been signed by a key built in to the EFI firmware. Many manufacturers enable this by default, and ship only a few inbuilt keys - such as the one belonging to Microsoft. The workaround is to have a signed shim which then boots grub.

Unfortunately, the “efibootmgr” application only works after the machine has been booted with EFI, which obviously can be tricky. The workaround is that EFI always supports a boot-option of “fallback.efi” which executes file “/EFI/BOOT/fallback.efi” and that in turn looks for files called BOOT.CSV and adds each entry to the list of boot-options. The standard grub-efi install process updates this BOOT.CSV file to add entries for the new grub-efi binary (note: file is in UTF-16 encoding). The “live install media” boots in EFI mode, so normally this work-around isn’t needed.

Useful references