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


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.

Kernel command line

First, create /etc/kernel/cmdline with your kernel parameters, taking care to remove entries pointing at microcode and initramfs, e.g.

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

.preset file

Next 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),
  • Add a PRESET_efi_image= parameter for each item in PRESETS=,
  • Optionally, append a --splash parameter to each PRESET_options= line for which you want to add a splash image.

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

# mkinitcpio preset file for the 'linux' package


PRESETS=('default' 'fallback')

default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_options="-S autodetect"

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


PRESETS=('default' 'fallback')

default_options="--splash /usr/share/systemd/bootctl/splash-arch.bmp"

fallback_options="-S autodetect"
  • If the ESP is mounted to /boot, make sure it has enough space, as mkinitcpio currently builds both the initramfs-*.img and the *.efi executables, thereby doubling the space used (see [1]).
  • If all you want to do is boot from the unified kernel images, you can mount the ESP to /efi and only those need to reside on the ESP partition.
  • You can append --cmdline /etc/kernel/fallback_cmdline to fallback_options to use different a different cmdline than above for the fallback image (e.g. without quiet)

Finally, regenerate the initramfs.


See dracut#Unified kernel image and dracut#Generate a new initramfs on kernel upgrade.


Install the sbctl package. Store the kernel command line 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.


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 esp/cpu_manufacturer-ucode.img esp/initramfs-linux.img > /tmp/combined_initrd.img

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

$ stub_line=$(objdump -h "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" | tail -2 | head -1)
$ stub_size=0x$(echo "$stub_line" | awk '{print $3}')
$ stub_offs=0x$(echo "$stub_line" | awk '{print $4}')
$ osrel_offs=$((stub_size + stub_offs))
$ cmdline_offs=$((osrel_offs + $(stat -c%s "/usr/lib/os-release")))
$ splash_offs=$((cmdline_offs + $(stat -c%s "/etc/kernel/cmdline")))
$ linux_offs=$((splash_offs + $(stat -c%s "/usr/share/systemd/bootctl/splash-arch.bmp")))
$ initrd_offs=$((linux_offs + $(stat -c%s "vmlinuz-file")))
$ objcopy \
    --add-section .osrel="/usr/lib/os-release" --change-section-vma .osrel=$(printf 0x%x $osrel_offs) \
    --add-section .cmdline="/etc/kernel/cmdline" \
    --change-section-vma .cmdline=$(printf 0x%x $cmdline_offs) \
    --add-section .splash="/usr/share/systemd/bootctl/splash-arch.bmp" \
    --change-section-vma .splash=$(printf 0x%x $splash_offs) \
    --add-section .linux="vmlinuz-file" \
    --change-section-vma .linux=$(printf 0x%x $linux_offs) \
    --add-section .initrd="initrd-file" \
    --change-section-vma .initrd=$(printf 0x%x $initrd_offs) \
    "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" "linux.efi"

The offsets are simply calculated so no sections overlap, as recommended in [2].

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

# cp linux.efi esp/EFI/Linux/



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' --unicode

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

Note: If options is present in a boot entry and Secure Boot is disabled, the value of options will override any .cmdline string embedded in the EFI image that is specified by efi or linux (see Unified kernel image#Preparing a unified kernel image). With Secure Boot, however, options (and any edits made to the kernel command line in the bootloader UI) will be ignored, and only the embedded .cmdline will be used.

See also