mirror of
https://github.com/ItsDrike/dotfiles.git
synced 2024-12-27 21:54:34 +00:00
206 lines
7.5 KiB
Markdown
206 lines
7.5 KiB
Markdown
|
# 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](./01_INSTALLATION.md), you will have it in:
|
||
|
`/efi/loader/entries/arch.conf`, all of the `options=` line contain
|
||
|
kernel command line args):
|
||
|
|
||
|
```bash
|
||
|
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
|
||
|
```
|
||
|
|
||
|
<!-- markdownlint-disable MD028 -->
|
||
|
|
||
|
> [!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`.
|
||
|
|
||
|
<!-- markdownlint-disable MD028 -->
|
||
|
|
||
|
## 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.
|
||
|
|
||
|
```bash
|
||
|
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:
|
||
|
|
||
|
```bash
|
||
|
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.)
|
||
|
|
||
|
```bash
|
||
|
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.
|
||
|
|
||
|
```bash
|
||
|
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.
|
||
|
|
||
|
<!-- markdownlint-disable MD013 -->
|
||
|
|
||
|
```bash
|
||
|
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
|
||
|
```
|
||
|
|
||
|
<!-- markdownlint-enable MD013 -->
|
||
|
|
||
|
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
|
||
|
```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:
|
||
|
|
||
|
```text
|
||
|
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.
|