EFI 系统分区

来自 ArchWiki
(重定向自 ESP

EFI 系统分区(也称为 ESP)是一个操作系统独立的分区,充当 UEFI 固件启动的 UEFI 启动加载程序、应用程序和驱动程序的存储位置。这是 UEFI 启动的必要条件。

检查是否已存在分区

如果您在已安装操作系统的支持 UEFI 的计算机上安装 Arch Linux,例如已安装 Windows 10,则很可能您已经拥有 EFI 系统分区。

要找出磁盘分区方案和系统分区,请以 root 身份在要从中启动的磁盘上使用 fdisk

# fdisk -l /dev/sdx

该命令返回

  • 磁盘的分区表:如果分区表是 GPT,则指示 Disklabel type: gpt;如果是 MBR,则指示 Disklabel type: dos
  • 磁盘上的分区列表:在列表中查找 EFI 系统分区,它通常至少为 100 MiB 大小,类型为 EFI SystemEFI (FAT-12/16/32)。要确认这是否是 ESP,挂载它并检查它是否包含名为 EFI 的目录,如果包含,则这肯定是 ESP。
提示: 要了解它是 FAT12、FAT16 还是 FAT32 文件系统,请参阅 FAT#检测 FAT 类型
警告: 双启动时,请尽可能避免重新格式化 ESP,因为它可能包含启动其他操作系统所需的文件。如果现有的 ESP 小于建议的最小尺寸,您可能需要将其替换为更大的分区

如果您找到了可接受的现有 EFI 系统分区,只需继续#挂载分区。如果您没有找到,则需要创建它,请继续#创建分区

创建分区

以下两节介绍如何创建 EFI 系统分区 (ESP)。

警告: EFI 系统分区必须是磁盘主分区表中的物理分区,而不是在 LVM 或软件 RAID 等之下。

分区大小应为存储启动加载程序和其他启动所需的文件提供足够的空间。

建议将分区大小设置为 1 GiB,以确保它有足够的空间容纳多个内核或统一内核映像、启动加载程序、固件更新文件以及任何其他操作系统或 OEM 文件。如果仍然有疑问,4 GiB 应该足够任何人使用,例如,对于像 Limine 启动加载器与 Snapper 集成用于 Btrfs 这样的工具,它支持创建多个可启动快照。

注意: 可以使用较小的分区,但请注意潜在的兼容性问题
  • 对于早期和/或有缺陷的 UEFI 实现,可能需要至少 512 MiB 的大小。[1]
  • 如果您计划将分区挂载到 /boot 并且不安装多个内核,则 400 MiB 将足够。
  • 当与 Windows 双启动时,对于具有 4096 逻辑扇区大小的驱动器(高级格式 4Kn 驱动器),大小应至少为 300 MiB[2],否则至少为 100 MiB。[3]
  • 为确保可以将分区格式化为 FAT32,在具有 512 字节逻辑扇区大小的驱动器上,它应至少为 36 MiB,在具有 4096 逻辑扇区大小的驱动器上,它应至少为 260 MiB。[4]
  • 如果这些都不是相关问题,则分区大小可以小至 2 MiB,在这种情况下,它可能仅容纳启动加载程序。

GPT 分区磁盘

GUID 分区表 上的 EFI 系统分区由分区类型 GUID C12A7328-F81F-11D2-BA4B-00A0C93EC93B 标识。

选择以下方法之一来为 GPT 分区磁盘创建 ESP

  • fdisk:创建一个分区,并使用 t 命令更改其分区类型EFI System,使用别名 uefi
  • gdisk:创建分区类型为 EF00 的分区。
  • GNU Parted:创建一个文件系统类型为 fat32 的分区,并在其上设置 esp 标志。

创建分区后,应使用文件系统对其进行格式化。继续下面的#格式化分区部分。

MBR 分区磁盘

警告: 强烈建议使用 GPT 而不是 MBR。

另请参阅 分区#在 GPT 和 MBR 之间选择,了解 MBR 的限制以及 GPT 的一般优势。

主引导记录 分区表上的 EFI 系统分区由分区类型 ID EF 标识。

选择以下方法之一来为 MBR 分区磁盘创建 ESP

  • fdisk:创建一个主分区,并使用 t 命令更改其分区类型EFI (FAT-12/16/32)
  • GNU Parted:创建一个文件系统类型为 fat32 的主分区,并在其上设置 esp 标志。

创建分区后,应使用文件系统对其进行格式化。继续下面的#格式化分区部分。

格式化分区

UEFI 规范强制要求支持 FAT12、FAT16 和 FAT32 文件系统(请参阅 UEFI 规范版本 2.10,第 13.3.1.1 节),但任何符合规范的供应商都可以选择添加对其他文件系统的支持;例如,Apple Mac 中的固件支持 HFS+ 文件系统。

为防止与其他操作系统潜在的问题,并且由于 UEFI 规范指出 UEFI“包含对系统分区使用 FAT32,以及对可移动媒体使用 FAT12 或 FAT16”[5],建议使用 FAT32。使用 mkfs.fat(8) 实用程序,该实用程序来自 dosfstools

# mkfs.fat -F 32 /dev/sdxY

如果您收到消息 WARNING: Not enough clusters for a 32 bit FAT! 并且无法创建更大的 ESP,请使用 mkfs.fat -s2 -F32 ...-s1 减小簇大小;否则 UEFI 可能无法读取该分区。有关支持的簇大小,请参阅 mkfs.fat(8)

对于小于 32 MiB 的分区,可能无法使用 FAT32。在这种情况下,将其格式化为 FAT16 甚至 FAT12。例如,2 MiB ESP 将仅能够支持 FAT12

# mkfs.fat -F 12 /dev/sdxY

挂载分区

内核、initramfs 文件以及在大多数情况下,处理器的 微代码 需要可被 启动加载器 或 UEFI 本身访问,才能成功启动系统。因此,如果您想保持设置简单,则您的启动加载器选择会限制 EFI 系统分区的可用挂载点。

注意: 如果 ESP 未挂载到 /boot,请确保不要依赖 systemd 自动挂载机制(包括 systemd-gpt-auto-generator 的机制)在内核升级期间。始终在任何系统或内核更新之前手动挂载它,否则您可能无法在更新后挂载它,从而将您锁定在当前正在运行的内核中,而无法更新 ESP 上的内核副本。

或者,在启动时预加载所需的内核模块,例如

/etc/modules-load.d/vfat.conf
vfat
nls_cp437
nls_ascii

典型的挂载点

挂载 EFI 系统分区的三个典型场景是

  • 挂载 ESP 到 /boot
    • 这有助于系统维护和管理,因为 /boot微代码 软件包放置 CPU 微代码 initramfs 文件的默认路径,也是 mkinitcpio 放置 内核initramfs 映像的默认路径。
    • 这确保了上述文件可被大多数 启动加载器 访问,因为并非所有启动加载器都可以访问其他卷上的文件。
    • 这会阻止设置特定于文件的 权限 和/或 扩展属性,因为 FAT 在挂载时设置全局权限
    • 这增加了 ESP 的大小要求,因为通常安装在 /boot 中的文件将加入与 EFI 相关的文件。
    • 在双启动的情况下,这会将特定于操作系统的启动文件暴露于来自其他操作系统的潜在危险操作。
    • 这使得 加密 /boot 变得不可能,因为 EFI 相关的文件必须可被固件访问。
  • 挂载 ESP 到 /efi
    • 它确保了操作系统相关文件和 EFI 相关文件之间的关注点分离,其中可能包括最好不要管的其他操作系统的文件。
    • 它避免了增加 ESP 的大小要求,因为它不会将安装到 /boot 的文件放在其中:只有 EFI 二进制文件(启动加载器(和可选的驱动程序)和/或统一内核映像)将存储在 ESP 上,从而节省存储空间。
    • 它允许为驻留在 /boot 中的文件保留特定于 Linux 的文件系统权限,避免 FAT 限制。
    • 它允许根据需要单独挂载 ESP,例如 仅在升级 启动加载器 时。
    • 如果使用带有适当设置的系统加密,则它允许仅保留少数必需的未加密文件,同时 /boot 保持受保护:这对于 统一内核映像启动加载器 很有用,这些启动加载器具有能够访问存储在其他位置的内核和文件的文件系统驱动程序。
  • 挂载 ESP 到 /efi,并额外挂载“扩展启动加载器分区”(XBOOTLDR) 到 /boot。当先前创建的 ESP 太小而无法容纳多个启动加载器和/或内核,但 ESP 无法轻易调整大小时(例如,在 Windows 之后安装 Linux 以进行 双启动 时),这可能很有用。至少 systemd-boot 支持此方法。
注意
  • /efi[6][7] 对历史悠久且现在不推荐使用的 ESP 挂载点 /boot/efi 的替代。
  • 默认情况下 /efi 目录不可用,您需要先创建它,然后才能将 ESP 挂载到其中。

其他挂载点

如果您不使用#典型的挂载点之一,则需要将启动文件复制到 ESP(以下简称 esp)。

# mkdir -p esp/EFI/arch
# cp -a /boot/vmlinuz-linux esp/EFI/arch/
# cp -a /boot/initramfs-linux.img esp/EFI/arch/
# cp -a /boot/initramfs-linux-fallback.img esp/EFI/arch/
注意: 如果您使用外部微代码 initramfs 映像,它们也需要复制到启动条目的位置。

此外,您需要使 ESP 上的文件与以后的内核更新保持同步。否则可能会导致系统无法启动。以下各节讨论了用于自动化此过程的几种机制。

使用绑定挂载

您可以不将 ESP 本身挂载到 /boot,而是使用绑定挂载(请参阅 mount(8))将 ESP 的目录挂载到 /boot。这允许 pacman 直接更新内核,同时保持 ESP 有序。

注意: 这需要与 FAT32 兼容的 内核启动加载器。对于常规 Arch 安装,这不是问题,但对于其他发行版(即那些需要在 /boot/ 中使用符号链接的发行版)来说可能是个问题。请参阅论坛帖子 [8]

#其他挂载点中一样,将所有启动文件复制到 ESP 上的目录,但将 ESP 挂载到 /boot之外。然后绑定挂载该目录

# mount --bind esp/EFI/arch /boot

验证成功后,编辑您的 Fstab 以使更改持久生效

/etc/fstab
esp/EFI/arch /boot none defaults,bind 0 0

使用 systemd

Systemd 具有事件触发的任务功能。在这种特定情况下,当 EFISTUB 内核和 initramfs 文件在 /boot/ 中更新时,检测路径更改的能力用于同步它们。监视更改的文件是 initramfs-linux-fallback.img,因为这是 mkinitcpio 构建的最后一个文件,以确保在开始复制之前已构建所有文件。要创建的 systemd 路径和服务文件是

/etc/systemd/system/efistub-update.path
[Unit]
Description=Copy EFISTUB Kernel to EFI system partition

[Path]
PathChanged=/boot/initramfs-linux-fallback.img

[Install]
WantedBy=multi-user.target
WantedBy=system-update.target
/etc/systemd/system/efistub-update.service
[Unit]
Description=Copy EFISTUB Kernel to EFI system partition

[Service]
Type=oneshot
ExecStart=/usr/bin/cp -af /boot/vmlinuz-linux esp/EFI/arch/
ExecStart=/usr/bin/cp -af /boot/initramfs-linux.img esp/EFI/arch/
ExecStart=/usr/bin/cp -af /boot/initramfs-linux-fallback.img esp/EFI/arch/

然后启用启动 efistub-update.path

提示: 对于使用您自己的密钥的 安全启动,您可以设置服务以使用 sbsigntools 对映像进行签名
ExecStart=/usr/bin/sbsign --key /path/to/db.key --cert /path/to/db.crt --output esp/EFI/arch/vmlinuz-linux /boot/vmlinuz-linux

使用文件系统事件

文件系统事件 可用于在内核更新后运行脚本来同步 EFISTUB 内核。以下是使用 incron 的示例。

/usr/local/bin/efistub-update
#!/bin/sh
cp -af /boot/vmlinuz-linux esp/EFI/arch/
cp -af /boot/initramfs-linux.img esp/EFI/arch/
cp -af /boot/initramfs-linux-fallback.img esp/EFI/arch/
注意: 第一个参数 /boot/initramfs-linux-fallback.img 是要监视的文件。第二个参数 IN_CLOSE_WRITE 是要监视的操作。第三个参数 /usr/local/bin/efistub-update 是要执行的脚本。
/etc/incron.d/efistub-update.conf
/boot/initramfs-linux-fallback.img IN_CLOSE_WRITE /usr/local/bin/efistub-update

为了使用此方法,启用 incrond.service

使用 mkinitcpio 预设

由于 /etc/mkinitcpio.d/ 中的预设支持 shell 脚本,因此只需编辑预设即可复制内核和 initramfs。

替换 mkinitcpio 钩子

编辑文件 /etc/mkinitcpio.d/linux.preset

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

# Directory to install the kernel, the initramfs...
ESP_DIR="esp/EFI/arch"

#ALL_config="/etc/mkinitcpio.conf"
ALL_kver="${ESP_DIR}/vmlinuz-linux"

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
default_image="${ESP_DIR}/initramfs-linux.img"
default_options=""

#fallback_config="/etc/mkinitcpio.conf"
fallback_image="${ESP_DIR}/initramfs-linux-fallback.img"
fallback_options="-S autodetect"

要测试,只需运行

# rm /boot/initramfs-linux-fallback.img /boot/initramfs-linux.img
# mv /boot/vmlinuz-linux esp/EFI/arch/
# mkinitcpio -p linux
另一个例子
/etc/mkinitcpio.d/linux.preset
ESP_DIR="esp/EFI/arch"
#ALL_config="/etc/mkinitcpio.conf"
ALL_kver="$ESP_DIR/vmlinuz-linux$suffix"
PRESETS=('default')
default_config="/etc/mkinitcpio.conf"
default_image="$ESP_DIR/initramfs-linux$suffix.img"
/etc/mkinitcpio.d/linux-zen.preset
suffix='-zen'
source /etc/mkinitcpio.d/linux.preset

使用 mkinitcpio 后期钩子

可以使用 mkinitcpio 后期钩子 在生成 initramfs 后将内核和 initramfs 映像复制到所需的目录。

创建 以下文件并使其可执行

/etc/initcpio/post/copy-kernel-and-initramfs
#!/usr/bin/env bash

kernel="$1"
initrd="$2"
target_dir="esp/EFI/arch"
files_to_copy=()

for file in "$kernel" "$initrd"; do
	if [[ -n "$file" ]] && ! cmp -s -- "$file" "${target_dir}/${file##*/}"; then
		files_to_copy+=("$file")
	fi
done

(( ! ${#files_to_copy[@]} )) && exit 0

cp -af -- "${files_to_copy[@]}" "${target_dir}/"

使用 pacman 钩子

最后一个选项依赖于在事务结束时运行的 pacman 钩子

第一个文件是一个钩子,用于监视相关文件,如果这些文件在前一个事务中被修改,则会运行该钩子。

/etc/pacman.d/hooks/999-kernel-efi-copy.hook
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Target = usr/lib/modules/*/vmlinuz
Target = usr/lib/initcpio/*
Target = boot/*-ucode.img

[Action]
Description = Copying linux and initramfs to EFI directory...
When = PostTransaction
Exec = /usr/local/bin/kernel-efi-copy.sh

第二个文件是脚本本身。创建该文件并使其可执行

/usr/local/bin/kernel-efi-copy.sh
#!/bin/sh
#
# Copy kernel and initramfs images to EFI directory
#

ESP_DIR="esp/EFI/arch"

for file in /boot/vmlinuz*
do
        cp -af "$file" "$ESP_DIR/$(basename "$file").efi"
        [ $? -ne 0 ] && exit 1
done

for file in /boot/initramfs*
do
        cp -af "$file" "$ESP_DIR/"
        [ $? -ne 0 ] && exit 1
done

[ -e /boot/intel-ucode.img ] && cp -af /boot/intel-ucode.img "$ESP_DIR/"
[ -e /boot/amd-ucode.img ] && cp -af /boot/amd-ucode.img "$ESP_DIR/"

exit 0

技巧与诀窍

替换为更大的分区

在具有预先存在的操作系统的磁盘上,EFI 系统分区可能小于#创建分区中建议的大小。例如,在非 4Kn 驱动器上,Windows 安装程序会创建一个可怜的 100 MiB EFI 系统分区。

在这种情况下,创建一个新的、更大的 EFI 系统分区可能是个好主意,以防止其空间不足。

在 Windows 中释放空间以创建新分区

在 Windows 中,可以使用磁盘管理 (diskmgmt.msc) 以图形方式管理分区,也可以从命令行使用 diskpart.exe 实用程序管理分区。

以管理员身份运行 diskmgmt.msc

  1. 右键单击“(C:)”分区(默认 Windows 创建的分区中唯一可以联机调整大小的分区),然后选择压缩卷...
  2. 输入 4096 作为要压缩的数量,然后单击压缩

现在“(C:)”分区之后应该有 4 GiB 的未分配空间。

启动进入 Arch Linux 或 Arch Linux 安装介质,以继续创建新分区。

删除旧分区并创建新分区

首先,请务必备份 EFI 系统分区的内容。例如,esp 是其挂载点

# cp -a esp /esp_backup

卸载 EFI 系统分区

# umount esp
注意: 在已安装的系统上,您可能还需要停止 esp.mountesp.automount 单元,以防止 systemd 再次自动挂载它。

运行 blkid 并记下 UUID 和 PARTUUID 值。稍后将为新分区重用它们。

# blkid
/dev/sdxY: UUID="XXXX-XXXX" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI system partition" PARTUUID="YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"

使用 gptfdisk 中的 sgdisk 删除旧分区

# sgdisk --delete=Y /dev/sdx

在最大的未分配空间中创建一个新分区,同时重用旧的 PARTUUID 和 PARTLABEL

# sgdisk --align-end --largest-new=0 --typecode=0:ef00 --change-name=0:'EFI system partition' --partition-guid=0:YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY /dev/sdx

通过使用 fdisk 列出分区来确认已创建新的 4 GiB 大小的 EFI 系统分区

# fdisk -l /dev/sdx
...
Device         Start       End   Sectors  Size Type
/dev/sdx1  158099456 166488063   8388608    4G EFI System
/dev/sdx2     206848    239615     32768   16M Microsoft reserved
/dev/sdx3     239616 158099455 157859840 75.3G Microsoft basic data
/dev/sdx4  166488064 167768063   1280000  625M Windows recovery environment
/dev/sdx5  167768064 176156671   8388608    4G Linux swap
/dev/sdx6  176156672 243265535  67108864   32G Linux root (x86-64)

Partition table entries are not in disk order.

分区号在删除和创建分区时不会重新排序,因此磁盘上的 EFI 系统分区号可能与以前相同。

将分区格式化为 FAT32,重用旧的 UUID(同时删除其中的破折号)

# mkfs.fat -F 32 -i XXXXXXXX /dev/sdxY

最后,挂载新分区并从备份还原其内容

# mount /dev/sdxY esp
# cp -a /esp_backup/. esp/

如果您之前停止了 esp.automount,请启动它。

牺牲相邻的交换分区来扩大 ESP

如果交换分区紧随 EFI 系统分区之后,您可以牺牲它来为扩大 EFI 系统分区腾出空间。例如,布局类似于

# fdisk -l /dev/sdx
...
Device       Start       End   Sectors  Size Type
/dev/sdx1     2048    616447    614400  300M EFI System
/dev/sdx2   616448   9005055   8388608    4G Linux swap
/dev/sdx3  9005056 125827071 116822016 55.7G Linux root (x86-64)

首先,停用交换分区 并将其从 fstab 中删除。

使用 fdisk 删除交换分区并扩大 EFI 系统分区。

  1. 运行
    # fdisk -l /dev/sdx
  2. 使用 d 命令删除交换分区(在上面的示例布局中为分区号 2)。
  3. 使用 e 命令扩大 EFI 系统分区(在上面的示例布局中为分区号 1)。使用建议的默认值作为新大小,然后按 Enter
  4. 通过 w 命令将更改写入磁盘并退出。

分区调整大小后,您需要调整其中文件系统的大小。由于 fatresize(1) 不起作用 并且 libparted 无法调整某些大小的 FAT 卷,因此唯一的选择是从现有文件系统备份文件,并创建一个占用分区所有空间的新文件系统。

记下文件系统 UUID,以便为新文件系统重用它

$ lsblk -dno UUID /dev/sdxY
XXXX-XXXX

备份 EFI 系统分区的内容。例如,esp 是其挂载点

# cp -a esp /esp_backup

卸载 EFI 系统分区

# umount esp
注意: 在已安装的系统上,您可能还需要停止 esp.mountesp.automount 单元,以防止 systemd 再次自动挂载它。

擦除分区中的文件系统签名,以避免旧文件系统的任何伪影

# wipefs -af /dev/sdxY

将分区格式化为 FAT32,重用旧的 UUID(同时删除其中的破折号)

# mkfs.fat -F 32 -i XXXXXXXX /dev/sdxY

最后,挂载新分区并从备份还原其内容

# mount /dev/sdxY esp
# cp -a /esp_backup/. esp/

如果您之前停止了 esp.automount,请启动它。

现在交换分区已消失,请在 交换文件 上设置交换空间。

故障排除

软件 RAID1 上的 ESP

可以将 ESP 作为 RAID1 阵列的一部分,但这样做会带来数据损坏的风险,并且在创建 ESP 时需要考虑更多因素。有关详细信息,请参阅 [9][10] 以及 UEFI booting and RAID1,其中提供了包含解决方案的深入指南。

关键部分是使用 --metadata 1.0,以便将 RAID 元数据保留在分区末尾,否则固件将无法访问它

# mdadm --create --verbose --level=1 --metadata=1.0 --raid-devices=2 /dev/md/ESP /dev/sdaX /dev/sdbY

或者,由于 ESP 不经常更新,因此可以通过在相关更新期间将主 ESP 复制到不同磁盘上的辅助 ESP 来管理辅助 ESP。然后可以使用 efibootmgr 手动添加辅助 ESP 的启动条目。有关实施示例,请参阅 debian wiki。请注意,虽然这避免了 RAID 方法的一些风险,但它仅在使用单个操作系统时才有效。

固件无法识别 EFI 目录

如果您为 FAT 文件系统指定了卷名(即文件系统标签),请确保将其命名为 EFI 以外的其他名称。这可能会触发某些固件中的错误(由于卷名与 EFI 目录名匹配),从而导致固件表现得好像 EFI 目录不存在一样。

参见