Unified kernel image - ArchWiki

Jump to content

# Unified kernel image

From ArchWiki

A unified kernel image (UKI) is a single executable which can be booted directly from UEFI firmware, or automatically sourced by boot loaders with little or no configuration. It is the combination of a UEFI boot stub program like systemd-stub(7), a Linux kernel image, an initramfs, and further resources in a single UEFI PE file.

This file, and therefore all these elements can then easily be 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

There are several ways to generate a UKI image and install it to the proper place (the esp/Linux directory). Currently several tools compete for doing this functionality, so choose one of the following based on your needs and your likings.

Note

You only need to perform one of the subsections.

### mkinitcpio

mkinitcpio will assemble the UKI itself unless systemd-ukify is installed. In which case, UKI creation will be offloaded to ukify unless this is explicitly disabled with the `--no-ukify` option. Note: when offloading to ukify mkinitcpio forcibly supplies it a configuration file from `{/etc,/usr/lib}/kernel/uki.conf`.

#### Kernel command line

mkinitcpio supports reading kernel parameters from command line files in the `/etc/cmdline.d` directory. Mkinitcpio will concatenate the contents of all files with a .conf extension in this directory and use them to generate the kernel command line. Any lines in the command line file that start with a `#` character are treated as comments and ignored by mkinitcpio. Take care to remove entries pointing to microcode and initramfs.

For example:

    /etc/cmdline.d/root.conf

    root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw

Tip

This article or section is being considered for removal.

Reason: Duplicates Kernel parameters#Parameter list. (Discuss in Talk:Unified kernel image#About removing btrfs kernel cmdline notices)

- If your root file system is on a non-default Btrfs subvolume, make sure to set necessary mount flags in `rootflags`. For example, if your system subvolume ID is `256`, you should add `rootflags=subvolid=256` to the kernel command line. See Btrfs#Mounting subvolume as root.
- It is not necessary to copy all flags in `/etc/fstab` since `rootflags` is only used during boot. systemd-remount-fs.service(8) will read `/etc/fstab`, remount and apply flags listed there automatically after boot.

    /etc/cmdline.d/security.conf

    # enable apparmor
    lsm=landlock,lockdown,yama,integrity,apparmor,bpf audit=1 audit_backlog_limit=256

Alternatively, `/etc/kernel/cmdline` can be used to configure the kernel command line.

For example:

    /etc/kernel/cmdline

    root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet bgrt_disable

Tip

- The `root=` parameter may be omitted if the root partition is automounted by systemd.
- 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:

- Un-comment (i.e. remove `#`) the `PRESET_uki=` parameter for each item in `PRESETS=`,
- Optionally, comment out `PRESET_image=` to avoid storing a redundant `initramfs-*.img` file,
- Optionally, add or un-comment the `--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.

    /etc/mkinitcpio.d/linux.preset

    # mkinitcpio preset file for the 'linux' package

    #ALL_config="/etc/mkinitcpio.conf"
    ALL_kver="/boot/vmlinuz-linux"

    PRESETS=('default' 'fallback')

    #default_config="/etc/mkinitcpio.conf"
    #default_image="/boot/initramfs-linux.img"
    default_uki="esp/EFI/Linux/arch-linux.efi"
    default_options="--splash=/usr/share/systemd/bootctl/splash-arch.bmp"

    #fallback_config="/etc/mkinitcpio.conf"
    #fallback_image="/boot/initramfs-linux-fallback.img"
    fallback_uki="esp/EFI/Linux/arch-linux-fallback.efi"
    fallback_options="-S autodetect"

Warning Placing the kernel (`ALL_kver`) in an unencrypted file system (e.g. in `/boot/` if the EFI system partition is mounted at `/boot`), exposes it to potential manipulation from bootable drives or other operating systems when dual-booting.
Tip

- 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 a different cmdline than above for the fallback image (e.g. without `quiet`).
- To omit embedding the kernel command line, add `--no-cmdline` to `PRESET_options=`. Kernel parameters will need to be passed via the boot loader.

Note `PRESET_uki` options were previously known as `PRESET_efi_image`, changed November 2022 (see archlinux/mkinitcpio/mkinitcpio#134), with older option deprecated but working for now.

#### pacman hook

Updates to systemd-stub (part of systemd), microcode (both intel-ucode and amd-ucode), and linux kernel will automatically trigger a UKI rebuild. But you may want to review other pacman hooks in the `/etc/pacman.d/hooks/` directory.

#### Building the UKIs

Finally, make sure that the directory for the UKIs exists and regenerate the initramfs. For example, for the linux preset:

    # mkdir -p esp/EFI/Linux
    # mkinitcpio -p linux

Optionally, remove any leftover `initramfs-*.img` from `/boot` or `/efi`.

### kernel-install

Kernel-install is part of systemd and requires systemd-ukify to build unified kernel images. Make sure kernel-install is properly set up.

To generate UKIs, install systemd-ukify and set the `kernel-install` layout to `uki`:

    /etc/kernel/install.conf

    layout=uki

Note By default, mkinitcpio will generate the initramfs and #ukify will thereafter include it inside the UKI.

Any configuration for #ukify must be done in `/etc/kernel/uki.conf` in order to be used by kernel-install, e.g.

    /etc/kernel/uki.conf

    [UKI]
    Splash=/usr/share/systemd/bootctl/splash-arch.bmp

Tip You can also copy the template from `/usr/lib/kernel/uki.conf` to `/etc/kernel/uki.conf` and uncomment the section titles and useful lines. Do not set the kernel command line in this file, it will be ignored. Use Kernel-install#Kernel command line.

Alternatively, for mkinitcpio to generate the UKI, set it as the default `uki_generator`:

    /etc/kernel/install.conf

    layout=uki
    uki_generator=mkinitcpio

In that case, systemd-ukify is not necessary. You can also set a different `initrd_generator`, see kernel-install(8).

Reinstall the kernel packages that you use in order for the change to take effect.

### dracut

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

### ukify

Install the systemd-ukify package. Since ukify cannot generate an initramfs on its own, if required, it must be generated using, e.g., dracut, mkinitcpio or booster.

A minimal working example can look something like this:

    # ukify build --linux=/boot/vmlinuz-linux \
                  --initrd=/boot/initramfs-linux.img \
                  --cmdline="quiet rw"

Note If external microcode initramfs images are used, `/boot/amd-ucode.img` or `/boot/intel-ucode.img` must always be placed first, before the main initramfs image. E.g. `--initrd=/boot/intel-ucode.img --initrd=/boot/initramfs-linux.img`.
Tip

- To skip having to copy over the resulting EFI executable to the EFI System Partition, use the `--output=esp/EFI/Linux/filename.efi` command line option to ukify.
- When specifying the `--cmdline` option, one can specify a file name to read the kernel parameters from (e.g. `/etc/kernel/cmdline` by adding the `@` symbol before the file name, like `--cmdline=@/path/to/cmdline`.

For further information, see ukify(1).

Note To have the UKI update automatically when kernel, microcode, or initramfs change, just use ukify with, e.g., kernel-install or mkinitcpio. See the preceding sections #kernel-install and #mkinitcpio for details.

### 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 initramfs, as follows:

    $ cat esp/cpu_manufacturer-ucode.img esp/initramfs-linux.img > /tmp/combined_initramfs.img

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

Note For IA32 UEFI, replace `/usr/lib/systemd/boot/efi/linuxx64.efi.stub` with `/usr/lib/systemd/boot/efi/linuxia32.efi.stub` in the following commands.

    $ align="$(objdump -p /usr/lib/systemd/boot/efi/linuxx64.efi.stub | awk '{ if ($1 == "SectionAlignment"){print $2} }')"
    $ align=$((16#$align))
    $ osrel_offs="$(objdump -h "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" | awk 'NF==7 {size=strtonum("0x"$3); offset=strtonum("0x"$4)} END {print size + offset}')"
    $ osrel_offs=$((osrel_offs + "$align" - osrel_offs % "$align"))
    $ cmdline_offs=$((osrel_offs + $(stat -Lc%s "/usr/lib/os-release")))
    $ cmdline_offs=$((cmdline_offs + "$align" - cmdline_offs % "$align"))
    $ splash_offs=$((cmdline_offs + $(stat -Lc%s "/etc/kernel/cmdline")))
    $ splash_offs=$((splash_offs + "$align" - splash_offs % "$align"))
    $ initramfs_offs=$((splash_offs + $(stat -Lc%s "/usr/share/systemd/bootctl/splash-arch.bmp")))
    $ initramfs_offs=$((initramfs_offs + "$align" - initramfs_offs % "$align"))
    $ linux_offs=$((initramfs_offs + $(stat -Lc%s "initramfs-file")))
    $ linux_offs=$((linux_offs + "$align" - linux_offs % "$align"))

    $ 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 .initrd="initramfs-file" \
        --change-section-vma .initrd=$(printf 0x%x $initramfs_offs) \
        --add-section .linux="vmlinuz-file" \
        --change-section-vma .linux=$(printf 0x%x $linux_offs) \
        "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" "linux.efi"

A few things to note:

- The offsets are dynamically calculated so no sections overlap, as recommended in [1].
- The sections are aligned to what the `SectionAlignment` field of the PE stub indicates (usually 0x1000).
- The kernel image must be in the last section, to prevent in-place decompression from overwriting the sections that follow, as stated in [2].

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

    # cp linux.efi esp/EFI/Linux/

To create a UKI with xen, see [3].

## Signing the UKIs for Secure Boot

### sbctl

sbctl provides a kernel-install script, a mkinitcpio post-hook, and pacman hooks to sign updated binaries.

### mkinitcpio

By using a mkinitcpio post hook, the generated unified kernel images can be signed for Secure Boot. Create the following file and make it executable:

    /etc/initcpio/post/uki-sbsign

    #!/usr/bin/env bash

    uki="$3"
    [[ -n "$uki" ]] || exit 0

    key=/path/to/db.key
    cert=/path/to/db.crt

    if ! sbverify --cert "$cert" "$uki" &>/dev/null; then
        sbsign "$uki" --key "$key" --cert "$cert" --output "$uki"
    fi

Replace `/path/to/db.key` and `/path/to/db.crt` with the paths to the key pair you want to use for signing the image.

### ukify

To automatically sign UKIs using systemd-sbsign(1), set the following in your configuration file (you can copy and edit the template from `/usr/lib/kernel/uki.conf`):

    /etc/kernel/uki.conf

    [UKI]
    SecureBootSigningTool=systemd-sbsign
    SignKernel=true
    SecureBootPrivateKey=/etc/kernel/secure-boot-private-key.pem
    SecureBootCertificate=/etc/kernel/secure-boot-certificate.pem

Note Signing with systemd-sbsign(1) currently needs `SignKernel=true` to be set. See [4].

## Booting

Note

- When Secure Boot is active, unified kernel images with an embedded `.cmdline` ignore all command line options passed to them (either using a boot entry or interactively). When Secure Boot is not active, the options passed via the command line override the embedded `.cmdline`.
- It is possible to place the UKI in the fallback boot path `esp/EFI/BOOT/BOOTx64.EFI` (or `BOOTIA32.EFI` for 32-bit IA32 UEFI). Using the fallback boot path avoids need to explicitly create a UEFI boot entry in the NVRAM.

### Limine

Limine does not automatically detect unified kernel images (UKIs). However, `limine.conf` can be manually configured to load them.

Example 1: Booting a UKI from the default EFI system partition

If a UKI file is stored in `esp/EFI/Linux/`, add the following configuration to `limine.conf`:

    limine.conf

    /Arch Linux
      protocol: efi
      path: boot():/EFI/Linux/arch-linux.efi

Example 2: Booting a UKI from another partition

If a UKI file is located on a different FAT32 partition, use `uuid(PARTUUID)` with the PARTUUID instead:

    limine.conf

    /Arch Linux
      protocol: efi
      path: uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx):/EFI/Linux/arch-linux.efi

For more details about supported paths and configuration options, see the Limine Paths documentation.

### 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

### rEFInd

rEFInd will autodetect unified kernel images on your EFI system partition, and is capable of loading them. They can also be manually specified in `refind.conf`, by default located at:

    esp/EFI/refind/refind.conf

    menuentry "Arch Linux" {
        icon \EFI\refind\icons\os_arch.png
        ostype Linux
        loader \EFI\Linux\arch-linux.efi
    }

Keep in mind that no kernel parameters from `esp/EFI/refind_linux.conf` will be passed when booting this way. If the UKI was generated without a `.cmdline` section, specify the kernel parameters in the menu entry with an `options` line.

### GRUB

GRUB can chainload UKIs as described in GRUB#Chainloading a unified kernel image.

### 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 "Arch Linux" --loader '\EFI\Linux\arch-linux.efi' --unicode

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

Note UEFI specification uses backward slash `\` as path separator but efibootmgr can automatically convert UNIX-style `/` path separators.

## See also

- All Systems Go! talk on UKIs
- Unified kernel image specification
- mkinitcpio v31 and UEFI stubs

Category:

- Boot loaders

Hidden category:

- Sections flagged with Template:Remove

Unified kernel image
Add topic