统一内核映像 - Arch Linux 中文维基

跳转到内容

# 统一内核映像

来自 Arch Linux 中文维基
翻译状态：

- 本文（或部分内容）译自 Unified kernel image，最近一次同步于 2025-04-20，若英文版本有所更改，则您可以帮助同步与翻译更改的内容。
- 您可以在 ArchWiki 的对应页面找到本文翻译的原始修订历史。
- 本文可能与英文原文存在出入。

统一内核映像（Unified Kernel Image，UKI）是一个可执行文件，可直接从 UEFI 固件进行启动，也可以无需或通过简单的配置由引导加载器自动生成。它将一个 UEFI boot stub 程序（例如 systemd-stub(7)）、一个 Linux 内核映像、一个 initrd 以及其它资源打包到了单个 UEFI PE 文件中。

该文件，也就是包括其下的所有元件都可以很方便地进行签名，以便与安全启动一起使用。

注意：在该文中，`esp` 指代 EFI 系统分区的挂载点。

## 准备统一内核映像

有多种方法可以生成 UKI 映像，并将其安装到正确位置下（esp/EFI/Linux 目录）。有多种工具都能完成该操作，可根据你的需要和喜好进行选择。

注意：

- 你只需要完成下面任一种选项。
- 可以将 UKI 放置到默认启动路径 `esp/EFI/BOOT/BOOTx64.EFI`（对于 32 位 IA32 UEFI 固件为 `BOOTIA32.EFI`）。使用默认启动路径无需在 NVRAM 显式创建启动项。

### mkinitcpio

除非安装了 systemd-ukify包，否则 mkinitcpio 会自行构建 UKI。在安装了 systemd-ukify包 的情况下，除非指定了 `--no-ukify` 选项，否则构建 UKI 的工作会被下放给 ukify。

#### 内核命令行参数

mkinitcpio 支持从 `/etc/cmdline.d` 文件夹下的命令行参数文件读取内核参数。Mkinitcpio 会将该文件夹下的所有文件内容集中到一个 .conf 文件下，并使用其生成内核命令行参数。命令行文件内以 `#` 开头的行都将被视为注释，并被 mkinitcpio 忽略掉。请移除掉指向微码和 initramfs 的启动项。

示例：

    /etc/cmdline.d/root.conf

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

提示：

这一章节正在考虑移除。

原因: 与内核参数#参数列表重复。 (在 Talk:统一内核映像 讨论)

- 如果你的根文件系统位于非默认的 Btrfs 子卷下，请确保在 `rootflags` 中设置了必要的挂载参数。举个例子，如果系统的子卷 ID 是 `256`，那么就要将 `rootflags=subvolid=256` 添加到内核命令行中。具体请参考 Btrfs#挂载子卷为根挂载点。
- `rootflags` 仅用于启动阶段，因此不需要将 `/etc/fstab` 中的全部挂载选项都复制过来。systemd-remount-fs.service(8) 会在启动后自动读取 `/etc/fstab`，重新挂载并应用所有挂载选项。

    /etc/cmdline.d/security.conf

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

也可以使用 `/etc/kernel/cmdline` 配置内核命令行选项，例如：

    /etc/kernel/cmdline

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

提示：

- 如果根分区由 systemd 自动挂载，可以省略 `root=` 参数。
- `bgrt_disable` 参数可以让 Linux 在加载 ACPI 表后不要显示 OEM 图标。

#### .preset 文件

下一步，参考如下编辑 `/etc/mkinitcpio.d/linux.preset` 或是你在使用的预设文件，并指向到 EFI 系统分区的挂载点：

- 将 `PRESETS=` 中每一项的 `PRESET_uki=` 参数取消注释（即移除 `#`），
- 可以选择注释掉 `PRESET_image=`，以避免存储多余的 `initramfs-*.img` 文件，
- 对于想要添加启动画面的项，可以在每行 `PRESET_options=` 中添加或取消注释掉 `--splash` 参数。

以下为一个可用 `linux.preset` 示例，用于 linux包 内核并包含 Arch 启动画面：

    /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"

提示：

- 如果你只是想从统一内核映像启动，可以将 ESP 挂载到 `/efi`，并只将必要文件放置到 ESP 分区。
- 可以通过添加 `--cmdline /etc/kernel/fallback_cmdline` 到 `fallback_options` 以对后备映像使用不同的 cmdline（例如不带 `quiet` 参数）。
- 要省略嵌入内核命令行，请在 `PRESET_options=` 中添加 `--no-cmdline` 参数。内核参数将需要通过引导加载器传入。

注意：`PRESET_uki` 选项之前被称作 `PRESET_efi_image`，于 2022 年 11 月变更（参见 archlinux/mkinitcpio/mkinitcpio#134）；旧选项已被废弃，但现在尚能正常使用。

#### pacman 钩子

更新 systemd-stub（systemd包 的一部分），微码（包括 intel-ucode包 和 amd-ucode包）及 linux包 内核会自动重新构建 UKI，但你可能会想检查下 `/etc/pacman.d/hooks/` 下如用于 NVIDIA 驱动的其它 pacman 钩子。

#### 构建 UKI

最后，确保用于 UKI 的目录存在，并重新生成 initramfs。例如，对于 linux预设：

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

另外，可以移除 `/boot` 或 `/efi` 下多余的 `initramfs-*.img`。

### kernel-install

Kernel-install 是 systemd包 提供的另一替代品，并依赖于 systemd-ukify包 构建统一内核映像。首先确保已正确配置了 kernel-install。

要生成 UKI，需要安装 systemd-ukify包，然后将 `kernel-install` 的 layout 设为 `uki`：

    /etc/kernel/install.conf

    layout=uki

注意：mkinitcpio 会负责创建 initrd，然后d #ukify 会进一步生成 UKI。

为 #ukify 进行的任何配置都必须在 `/etc/kernel/uki.conf` 中完成，才能被 kernel-install 使用，例如：

    /etc/kernel/uki.conf

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

注意：你也可以将 `/usr/lib/kernel/uki.conf` 模板复制到 `/etc/kernel/uki.conf`，然后将需要的部分取消注释。不要在该文件中设置命令行参数，否则设定的参数将被忽略。具体信息请参考 Kernel-install#Kernel command line。

也可以通过配置默认 `uki_generator` 来使用 mkinitcpio 生成 UKI：

    /etc/kernel/install.conf

    layout=uki
    uki_generator=mkinitcpio

在这种情况下可以不使用 systemd-ukify包。你也可以使用其它 `initrd_generator`，具体信息请参考 kernel-install(8)。

为了使配置生效，需要重新安装当前使用的内核软件包。

### dracut

请参考 dracut#统一内核映像和 dracut#升级内核时生成新 initramfs。

### ukify

安装 systemd-ukify包。如需使用自动签名功能，请一并安装 sbsigntools包。由于 ukify 不能自行生成 initramfs，因此如有需求，需要单独通过 dracut，mkinitcpio 或 booster 进行生成。

最小可用示例如下：

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

注意：如果使用了外置 initramfs 微码映像，必须将 `/boot/amd-ucode.img` 或 `/boot/intel-ucode.img` 放置到第一位，放在主 initramfs 映像之前，例如：`--initrd=/boot/intel-ucode.img --initrd=/boot/initramfs-linux.img`。
提示：

- 要跳过将 EFI 执行文件复制到 EFI 系统分区的步骤，可对 ukify 使用 `--output=esp/EFI/Linux/filename.efi` 命令行选项。
- 在使用 `--cmdline` 选项时，可在文件名前使用 `@` 符号来指定内核参数来源文件（以 `/etc/kernel/cmdline` 为例，使用 `--cmdline=@/path/to/cmdline`）

更多信息请参考 ukify(1)。

注意：要在内核/微码/initramfs 改变时自动更新 UKI，只需将 ukify 搭配如 kernel-install 或 mkinitcpio 使用。具体细节请参考之前的 #kernel-install 和 #mkinitcpio 章节。

### 手动构建

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.

注意：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/

## 为安全启动对 UKI 进行签名

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

安装 sbsigntools包，并在 `/etc/kernel/uki.conf` 中指定 `--secureboot-private-key` 和 `--secureboot-certificate`。

## 启动

注意：当启用安全启动时，内嵌 `.cmdline` 的统一内核映像会忽略所有传入的命令行参数（无论是通过启动项还是交互式传入）。在不启用安全启动时，通过命令行传入的选项会绕过掉内嵌的 `.cmdline`。

### Limine

Limine 不会自动检测 UKI，但可以通过编辑 `limine.conf` 手动进行加载。

示例 1：从默认 EFI 系统分区启动 UKI

如果 UKI 位于 `esp/EFI/Linux/` 下，可以在 `limine.conf` 中添加如下配置：

    limine.conf

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

示例 2：从其它分区启动 UKI

如果 UKI 文件位于不同的 FAT32 分区下，可以通过 `guid(PARTUUID)` 使用 PARTUUID 进行指定：

    limine.conf

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

关于支持的路径和配置选项等更多信息，请参考 Limine Paths 文档。

### systemd-boot

systemd-boot 会在 `esp/EFI/Linux/` 路径下搜索统一内核映像，无需额外配置。具体细节请参考 sd-boot(7) § FILES。

### rEFInd

rEFInd 会自动检测到 EFI 系统分区下的 UKI，并可以进行加载。也可以在 `refind.conf` 中手动进行指定，参考如下：

    esp/EFI/refind/refind.conf

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

注意，以该种方式启动时，是不会传入 `esp/EFI/refind_linux.conf` 中的内核参数的。如果 UKI 生成时没有使用 `.cmdline`，需要在启动项添加 `options` 一行指定内核参数。

### GRUB

GRUB 可以连锁加载 EFI UKI，具体细节请参考 GRUB#Chainload 一个统一的内核镜像。

### 直接从 UEFI 启动

efibootmgr 可以为 .efi 文件创建 UEFI 启动项：

    # efibootmgr --create --disk /dev/sdX --part partition_number --label "Arch Linux" --loader '\EFI\Linux\arch-linux.efi' --unicode

选项具体作用请参考 efibootmgr(8)。

注意：UEFI 标准使用反斜杠`\` 作为路径分隔符，但 efibootmgr 可以自动转换 UNIX 风格的 `/` 路径分隔符。

## 参阅

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

分类：​

- 启动加载程序
- 使用“模板:Remove”标记的章节

统一内核映像
添加话题