[Spec] support configuring both BIOS and UEFI boot on amd64 in Curtin

Status Pending Review
Author @mwhudson
Release Ubuntu 22.04
Internal ID FO0024

Abstract

Installs using subiquity and the new desktop installer should set up amd64 systems so they can be booted in both BIOS and UEFI mode.

Rationale

Although it’s getting less common as UEFI takes over the world, it is still reasonably likely – especially in a world where subiquity/curtin are used to install a desktop system – that someone will install Ubuntu in legacy boot mode and then decide to switch their system to UEFI boot. The installer should handle this by installing an amd64 system that can boot both ways. Also ubiquity works this way today and the desktop installer losing this feature would be regression.

There are some (terrible) firmwares that will boot a CDROM in legacy/BIOS/CSM mode and then refuse to boot off a hard drive that does not have an ESP.

Specification

This will require curtin changes and installer changes. The curtin changes will be the tricky bit.

Current curtin behaviour

Curtin assumes that the target system will boot the same way the installer environment does and almost all of its behaviour around bootloader installation depends on this.

It allows the specification of “grub devices” in two broad ways:

  1. In the storage config.
  • For BIOS this is done by setting grub_device on an action (you’re going to have a bad time if you set this on an action that is not a disk, i guess)
  • For UEFI a partition is indicated as somewhere grub should be installed by either:
    1. setting grub_device on a partition action
    2. mounting something (a warning is emitted if it is not a partition) at /boot/efi
    3. setting grub_device on the parent disk action of a partition with flag==boot.

The last option is only considered if there are not any devices matching the first option. It is an error if something is not mounted at /boot/efi. There are no checks for the first two options that the partition action has flag==boot, i.e. is actually an ESP.

  1. By listing device paths (i.e. /dev/sda1, not ids from the storage config) in grub.install_devices in the config.

The first option wins if both are specified. This means for a UEFI booted system that uses a storage config (vs something like block-meta simple) it is actually the only one that can be used because of the requirement that something is mounted at /boot/efi.

To install / configure the bootloader on UEFI amd64, curtin:

  1. explicitly installs any of the following packages that are not already installed in the target system:
  • efibootmgr
  • grub-efi-amd64
  • shim-signed
  1. stores the by-id paths to the grub devices in debconf as grub-efi/install_devices
  2. writes the grub config to target
  3. runs dpkg-reconfigure grub-efi-amd64
  4. runs update-grub
  5. for focal+ run /usr/lib/grub/grub-multi-install
  6. for bionic run grub-install on each grub device

To install / configure the bootloader on BIOS amd64, curtin:

  1. installs the kernel, which brings in grub-pc because the kernel recommends grub-pc | grub-efi-amd64 | grub-efi-ia32 | grub | lilo and none of those are installed
  2. stores the by-id paths to the grub devices in debconf as grub-pc/install_devices
  3. writes the grub config to target
  4. runs dpkg-reconfigure grub-pc
  5. runs update-grub
  6. runs grub-install on each grub device

I don’t see how it is making sure that grub-pc is installed currently.

Curtin does some stuff to reorder the UEFI boot entries that I don’t really understand. But that’s probably not super important for this change.

Curtin Changes

My basic plan is to allow the storage config to indicate which disks should have BIOS grub installed and which partitions should have UEFI grub installed. Then for each bootloader type (i.e. BIOS and UEFI), if there are any devices for that bootloader, run through the configuration steps for that bootloader type as above.

There will need to be slight changes to the configuration steps for each bootloader:

  • For UEFI, we should explicitly install:
    • shim-signed on architectures where we expect it to work (amd64, arm64 today)
    • grub-efi-{arch} on other architectures (currently actually none, but maybe riscv64 soonish?)
  • For BIOS, explicitly install grub-pc

(We should change the image build process to include shim-signed and grub-pc in the base image for amd64 but curtin should not depend on this)

It’s not at all clear to me that the dpkg-reconfigure does anything for either bootloader, we should probably drop that.

The current ways of indicating an ESP overlap slightly (setting grub_device on a disk action means different things when UEFI booted vs BIOS booted). I propose ditching all this and adding a super explicit ‘install_bootloader’ flag on partitions and disks in version 2 of the storage config.

I would also like to drop support for grub.install_devices I guess. We’ll need to see if MAAS uses them (it doesn’t look like it, from a quick grep).

Current installer behaviour

Subiquity only uses the first option for a UEFI system (and always indicates that an ESP should be used by setting grub_device on the partition and only sets that on partitions with flag==boot).

When adding a partition to a disk and there is no boot device configured, it adds a partition as needed by the current bootloader.

For a disk that has been partitioned already, it checks if its partitions permit it being used as a boot disk under the current bootloader before allowing it to be used as a boot disk.

Installer changes

All of this mostly only applies on amd64 systems. arm64 or ppc64el or s390x (or riscv64!?) will continue to assume a single bootloader, although they will need to adjust to using the new ‘install_bootloader’ field.

When configured a to-be-formatted disk as a boot device (including the usual case where a partition is added to an unformatted disk and there is no bootloader configured yet) the installer should add a bios spacer (if GPT) and an ESP.

There is an interesting question about allowing a disk whose partitions only allow booting under the current bootloader to be configured as a boot device. We should allow this, but warn the user about the loss of compatibility this entails.

Further Information

That section, if present, may describe why particular design decisions were made, alternate designs that were considered, and related work (e.g. how the issue is handled in other systems).

May also include links to other (related) specs, links to version control repos or external information.