Unified kernel image

From ArchWiki

A unified kernel image is a single executable which can be booted directly from UEFI firmware, or automatically sourced by boot-loaders with little or no configuration.

Although Arch supported kernels themselves can be loaded by UEFI firmware, a unified image allows to incorporate:

The resulting executable, and therefore all these elements can then be easily signed for use with Secure Boot.

Note: In the entire article esp denotes the mountpoint of the EFI system partition.

Preparing a unified kernel image

mkinitpcio

One can test the feature by running as an example

# mkinitcpio -p linux -- --uefi esp/EFI/Linux/test-systemd.efi

This would produce a kernel image for the linux preset.

.preset file

First, modify /etc/mkinitcpio.d/linux.preset, or the preset that you are using, as follows, with the appropriate mount point of the EFI system partition :

  • If your system requires Microcode, add ALL_microcode=(/boot/*-ucode.img) to tell mkinitcpio where to find it.
  • Add a PRESET_efi_image= parameter for each item in PRESETS=, i.e. default_efi_image="esp/EFI/Linux/archlinux-linux.efi" and fallback_efi_image="esp/EFI/Linux/archlinux-linux-fallback.efi". This sets the executable filename.
  • Optionally, append --splash /usr/share/systemd/bootctl/splash-arch.bmp to each PRESET_options= line to add a splash image, i.e. default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp" and fallback_options="-S autodetect --splash /usr/share/systemd/bootctl/splash-arch.bmp".

Here is a working example linux.preset for the linux kernel and the Arch splash screen.

/etc/mkinitcpio.d/linux.preset
# mkinitcpio preset file for the 'linux' package

ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"
ALL_microcode=(/boot/*-ucode.img)

PRESETS=('default' 'fallback')

default_image="/boot/initramfs-linux.img"
default_efi_image="esp/EFI/Linux/archlinux-linux.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_image="/boot/initramfs-linux-fallback.img"
fallback_efi_image="esp/EFI/Linux/archlinux-linux-fallback.efi"
fallback_options="-S autodetect --splash /usr/share/systemd/bootctl/splash-arch.bmp"

This second example builds a default image for linux and a fallback image for linux-lts :

/etc/mkinitcpio.d/linux.preset
ALL_config="/etc/mkinitcpio.conf"
ALL_microcode=(/boot/*-ucode.img)

PRESETS=('default' 'fallback')

default_kver="/boot/vmlinuz-linux"
default_image="/boot/initramfs-linux.img"
default_efi_image="/boot/EFI/Linux/archlinux-linux.efi"
default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_kver="/boot/vmlinuz-linux-lts"
fallback_image="/boot/initramfs-linux-lts-fallback.img"
fallback_efi_image="/boot/EFI/Linux/archlinux-linux-lts-fallback.efi"
fallback_options="-S autodetect --splash /usr/share/systemd/bootctl/splash-arch.bmp"
Tip: If you are using multiple kernels, make sure your ESP has enough space, as mkinitcpio currently builds both the initramfs-*.img and the *.efi executable, thereby doubling the space used. See [1]

Kernel command line

Next, create /etc/kernel/cmdline with your kernel parameters.

# cp /proc/cmdline /etc/kernel/cmdline
Warning: initrd entries pointing at microcode and the initramfs need to be removed.

For example:

/etc/kernel/cmdline
rw quiet bgrt_disable
Tip: The bgrt_disable parameter tells Linux to not display the OEM logo after loading the ACPI tables.

Finally, regenerate the initramfs.

dracut

Place your command line parameters in e.g. /etc/dracut.conf.d/cmdline.conf.

Generate the image with:

# dracut -f -q --uefi --uefi-splash-image /usr/share/systemd/bootctl/splash-arch.bmp

Also see dracut#Generate a new initramfs on kernel upgrade.

sbctl

Install the sbctl package. Store the kernel cmdline in /etc/kernel/cmdline. Use the sbctl bundle command with the --save parameter to create a bundle and have it be regenerated by a Pacman hook at appropriate times:

# sbctl bundle --save esp/archlinux.efi

To create more EFI binaries for other kernels and initramfs images, repeat the above command with parameters --kernel-img and --initramfs, see sbctl(8) § EFI BINARY COMMANDS. The EFI binaries can be regenerated at any time with sbctl generate-bundles.

Manually

Put the kernel command line you want to use in a file, and create the bundle file using objcopy(1).

For microcode, first concatenate the microcode file and your initrd, as follows:

$ cat /boot/cpu_manufacturer-ucode.img /boot/initramfs-linux.img > /tmp/combined_initrd.img

When building the unified kernel image, passing /tmp/combined_initrd.img as the initrd. This file can be afterwards.

$ objcopy \
    --add-section .osrel="/usr/lib/os-release" --change-section-vma .osrel=0x20000 \
    --add-section .cmdline="/etc/kernel/cmdline" --change-section-vma .cmdline=0x30000 \
    --add-section .splash="/usr/share/systemd/bootctl/splash-arch.bmp" --change-section-vma .splash=0x40000 \
    --add-section .linux="vmlinuz-file" --change-section-vma .linux=0x2000000 \
    --add-section .initrd="initrd-file" --change-section-vma .initrd=0x3000000 \
    "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" "linux.efi"

See [2] for an explanation on why these exact numbers were chosen.

After creating the image, copy it to the EFI system partition:

# cp linux.efi esp/EFI/Linux/

Booting

systemd-boot

systemd-boot searches in esp/EFI/Linux/ for unified kernel images, and there is no further configuration needed. See sd-boot(7) § FILES

Directly from UEFI

efibootmgr can be used to create a UEFI boot entry for the .efi file:

# efibootmgr --create --disk /dev/sdX --part partition_number --label "label" --loader 'EFI\Linux\file.efi' --verbose

See efibootmgr(8) for an explanation of the options.

Note: If the system is not booting with Secure Boot enabled, systemd-stub will ignore the embedded kernel cmdline parameters. In this case the kernel cmdline parameters must be passed to the boot loader using the --unicode flag of efibootmgr.

See also