Unified kernel image - ArchWiki

Jump to content

# Unified kernel image

3 languages

- 日本語
- Português
- 中文（简体）

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 initrd, 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.
- 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.

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

#### 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, such as the one for the NVIDIA driver.

#### 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 another alternative that 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: mkinitcpio will create the initrd and #ukify will thereafter generate 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 comment out 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 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.

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"))
    $ initrd_offs=$((splash_offs + $(stat -Lc%s "/usr/share/systemd/bootctl/splash-arch.bmp")))
    $ initrd_offs=$((initrd_offs + "$align" - initrd_offs % "$align"))
    $ linux_offs=$((initrd_offs + $(stat -Lc%s "initrd-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="initrd-file" \
        --change-section-vma .initrd=$(printf 0x%x $initrd_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/

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

    keypairs=(/path/to/db.key /path/to/db.crt)

    for (( i=0; i<${#keypairs[@]}; i+=2 )); do
        key="${keypairs[$i]}" cert="${keypairs[(( i + 1 ))]}"
        if ! sbverify --cert "$cert" "$uki" &>/dev/null; then
            sbsign --key "$key" --cert "$cert" --output "$uki" "$uki"
        fi
    done

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:

    /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 [3].

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

### 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 `guid(PARTUUID)` with the PARTUUID instead:

    limine.conf

    /Arch Linux
      protocol: efi
      path: guid(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