dotfiles/guides/03_UNIFIED_KERNEL_IMAGES.md

7.5 KiB

Unified Kernel Images (UKI) booting

A Unified Kernel Image is a single executable (.efi file), which can be booted directly from UEFI firmware, or be automatically sourced by boot loaders with no extra configuration.

Note

If you're still using BIOS, you will not be able to set up UKIs, they require UEFI.

A UKI will include:

  • a UEFI stub loader like (systemd-stub)
  • the kernel command line
  • microcode
  • an initramfs image
  • a kernel image
  • a splash screen

The most common reason why you might want to use UKIs is secure boot. That's because a UKI is something that can be signed and represents an immutable executable used for booting into your system.

This is good, because with a standalone bootloader, you would be allowed you to edit the kernel parameters, or even change the kernel image by editing the configuration inside of the (unencrypted) EFI partition. This is obviously dangerous, and we don't want to allow this.

Define kernel command line

Since UKI contains the kernel command line, we will need to define it so that when the image is being built, it can pick it up.

This is a crucial step especially when you have encryption set up, as without it, the kernel wouldn't know what root partition to use.

To set this up, we will use /etc/kernel/cmdline.

This is how I setup my kernel arguments (If you're unsure what arguments you need, just check your current systemd-boot configuration, if you followed the INSTALLATION guide, you will have it in: /efi/loader/entries/arch.conf, all of the options= line contain kernel command line args):

echo "rw loglevel=3" > /etc/kernel/cmdline
echo "cryptdevice=LABEL=CRYPTFS:cryptfs:allow-discards" >> /etc/kernel/cmdline
echo "root=/dev/mapper/cryptfs rootflags=subvol=/@" >> /etc/kernel/cmdline

Tip

If you prefer, you can also create /etc/kernel/cmdline.d directory, with individual files for various parts of the command line. At the end, all of the options from all files in this directory will be combined.

You might find this useful if you set a lot of kernel parameters, so you might have for example: root.conf, apparmor.conf, ...

Important

Note that you shouldn't be specifying the cryptdevice or root kernel parameters if you're using systemd initramfs, rather than BusyBox one (which mkinitramfs generates by default).

That said, you will still need rootflags to select the btrfs subvolume though, unless the root partition is your default subvolume.

If you aren't sure which initramfs you're using, it's probably BusyBox.

Modify the linux preset for mkinitcpio to build UKIs

Now open /etc/mkinitcpio.d/linux.preset, where you'll want to:

  • Uncomment ALL_config
  • Comment default_image
  • Uncomment default_uki (unified kernel image)
  • Uncomment default_options
  • Comment fallback_image
  • Uncomment fallback_uki

Recreate /efi

First, we'll need to unmount /boot, which is currently bind-mounted to /efi/EFI/arch. This is because we'll no longer be storing the kernel, initramfs, nor the microcode in the EFI partition at all. The EFI partition will now only contain the final UKI, the rest can be left in /boot, which will now be a part of the root partition, not mounted anywhere.

umount /boot
vim /etc/fstab  # remove the bind mount entry for /boot

Now, we will clear the EFI partition and install systemd-boot again from scratch:

rm -rf /efi/*

Now, we will create a /efi/EFI/Linux directory, which will contain all of our UKIs. (You can change the location in /etc/mkinitcpio.d/linux.preset if you wish to use some other directory in the EFI partition, or you want a different name for the UKI file. Note that it is recommended that you stick with the same directory, as most boot loaders will look there when searching for UKIs.)

mkdir -p /efi/EFI/Linux

Finally, we will reinstall the kernel and microcode, re-populating /boot (now on the root partition).

This will also trigger a initramfs rebuild, which will now create the UKI image based on the linux.preset file.

pacman -S linux amd-ucode # or intel-ucode

Proceeding without a boot manager

Because the Unified Kernel Images can actually be booted into directly from the UEFI, you don't need to have a boot manager installed at all. Instead, you can simply add the UKIs as entries to the UEFI boot menu.

Note

I prefer to still use a full boot manager alongside UKIs, as they allow you to have a nice graphical boot menu, from which you can dynamically override the kernel parameters during boot, or have extra entries for different operating systems, without having to rely on the specific implementation of the boot menu in your UEFI firmware (which might take really long to open, or just generally not provide that good/clean experience).

Do note though that going without a boot manager is technically a safer approach, as it cuts out the middle-man entirely, whereas with a boot manager, your UEFI firmware will be booting the EFI image of your boot manager, only to then boot your own EFI image, being the UKI.

Regardless, I still like to use systemd-boot, instead of booting UKIs directly. If you wish to do the same, skip this section.

pacman -S efibootmgr
efibootmgr --create --disk /dev/disk/nvme0n1 --part 1 --label "Arch Linux" --loader 'EFI\Linux\arch-linux.efi' --unicode
efibootmgr -c -d /dev/disk/nvme0n1 -p 1 -L "Arch Linux Fallback" -l 'EFI\Linux\arch-linux-fallback.efi' -u
pacman -R systemd-boot

You can also specify additional kernel parameters / override the default ones in the UKI, by simply adding a string as a last positional argument to the efibootmgr command, allowing you to create entires with different kernel command lines easily.

Proceeding with a boot manager

Note

This is an alternative to the above, see the note in the previous section to understand the benefits/cons of either approach.

Most boot managers can handle loading your UKIs. The boot manager of my choice is systemd-boot, but if you wish, you should be able to use grub, or any other boot manager too. That said, this guide will only mention systemd-boot.

All that we'll need to do now is installing systemd-boot, just like during the initial OS installation:

```bash
bootctl install --esp-path=/efi

We can now reboot. Systemd-boot will pick up any UKI images in /efi/EFI/Linux automatically (this path is hard-coded), even without any entry configurations.

That said, if you do wish to do so, you can still add an explicit entry for your configuration in /efi/loader/entries/arch.conf, like so:

title Arch Linux
sort-key 0
efi /EFI/Linux/arch-linux.efi
# If you wish, you can also specify kernel options here, it will
# append/override those in the UKI image
#options rootflags=subvol=/@
#options rw loglevel=3

Although do note that if your UKI image is stored in /efi/EFI/Linux, because systemd-boot picks it up automatically, you will see the entry twice, so you'll likely want to change the target directory for the UKIs (in /etc/mkinitcpio.d/linux.preset) to something else.

I however wouldn't recommend this approach, and I instead just let systemd-boot autodetect the images, unless you need something specific.

If everything went well, you should see a new systemd based initramfs, from where you'll be prompted for the LUKS2 password.