EFI 系统分区

来自 Arch Linux 中文维基

EFI 系统分区(也称为 ESP)是一个与操作系统无关的分区,其中存储了由 UEFI 固件启动的 EFI 引导加载器、应用程序和驱动,是 UEFI 启动所必须的。

检查现有的分区[编辑 | 编辑源代码]

如果你正将 Arch Linux 安装到支持 UEFI 且已安装操作系统的计算机上,例如与 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,mount 它,然后看看是否包含一个名为 EFI 的目录,如果有,那这肯定就是 ESP。
提示:要判断是 FAT12、FAT16 还是 FAT32 文件系统,参阅 FAT#检测FAT类型英语FAT#Detecting FAT type
警告: 双启动时不要重新格式化 ESP,因为它可能包含启动其他操作系统所需的文件。

如果你找到了现有的 EFI 系统分区,前往#挂载分区,否则,你将需要创建一个,前往#创建分区

创建分区[编辑 | 编辑源代码]

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

警告: EFI 系统分区必须是磁盘主要分区表上的物理分区,不能处于 LVM 或软件 RAID 等等之下。

分区大小应足以储存启动加载器和启动所需要的其他文件。

为避免与其他操作系统的互操作问题[1][2],大小最好不小于 300 MiB。对于早期和/或古怪的 UEFI 实现,可能最少需要 512 MiB。[3]如果这些问题都不相关,分区大小最小可达 2 MiB,此时只能放得下启动加载器。

GPT 分区磁盘[编辑 | 编辑源代码]

GUID Partition Table 中 EFI 系统分区以 分区类型 GUID C12A7328-F81F-11D2-BA4B-00A0C93EC93B 标识。

从以下方法中任选其一在 GPT 分区的磁盘上创建 ESP:

  • fdisk:创建分区类型为 EFI System 的分区。
  • gdisk:创建分区类型为 EF00 的分区。
  • GNU Parted:创建文件系统类型为 fat32 的分区,并设置 esp 标志。

创建分区之后,应当格式化为一种文件系统。前往#格式化分区

MBR 分区磁盘[编辑 | 编辑源代码]

注意:

另请参阅 Partitioning#选择 GPT 还是 MBR 查看 GPT 的优点。

主引导记录中 EFI 系统分区以 分区类型 ID EF 标识。

从以下方法中任选其一在 MBR 分区的磁盘上创建 ESP:

  • fdisk: 创建一个分区类型为 EFI (FAT-12/16/32) 的主分区。
  • GNU Parted: 创建文件系统类型为 fat32 的分区,并设置 esp 标志。

创建分区之后,应当格式化为一种文件系统。前往#格式化分区

格式化分区[编辑 | 编辑源代码]

UEFI 规范要求支持 FAT12、FAT16 和 FAT32 文件系统(参见 UEFI specification version 2.9, section 13.3.1.1),但任何合规的厂商都可以支持额外的文件系统。例如,Apple Mac 的固件支持 HFS+ 文件系统。

为避免与其他操作系统的潜在问题,同时既然 UEFI 规范声称 UEFI "encompasses the use of FAT32 for a system partition, and FAT12 or FAT16 for removable media"[4],建议使用 FAT32英语FAT32。使用 dosfstools 中的 mkfs.fat(8) 工具:

# mkfs.fat -F 32 /dev/sdxY

如果你收到消息 WARNING: Not enough clusters for a 32 bit FAT!,运行 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 系统分区可能的挂载点。

典型挂载点[编辑 | 编辑源代码]

挂载 EFI 系统分区最简单的情形是:

  • 挂载 ESP 到 /efi 并使用一个能够访问储存在其他位置(通常是 /boot)的内核和 initramfs 镜像的引导加载程序。启动加载器的要求和能力详见引导加载程序
  • 挂载 ESP 到 /boot。This is the preferred method when directly booting an EFISTUB英语EFISTUB kernel from UEFI or booting it via a boot manager like systemd-boot.
  • 挂载 ESP to /efi and additionally mount an "Extended Boot Loader Partition" (XBOOTLDR) to /boot. This can be useful when a previously created ESP is too small to hold multiple boot loaders and/or kernels but the ESP cannot be easily resized (such as when installing Linux after Windows to dual boot). This method is supported by at least systemd-boot.
提示:
  • /efi 是以前流行(可能仍被其他 Linux 发行版使用)的 ESP 挂载点 /boot/efi 的一个替代品[5]
  • /efi 目录默认不可用,需要先用 mkdir(1) 创建再挂载 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/
注意: 可能还需要将微码复制到启动项的位置。

此外,还需要使 ESP 中的文件在以后的内核更新中保持最新。否则可能会导致系统无法启动。以下部分讨论了几种自动化的机制。

注意: If ESP is not mounted to /boot, make sure to not rely on the systemd automount mechanism (including that of systemd-gpt-auto-generator(8)). Always have it mounted manually prior to any system or kernel update, otherwise you may not be able to mount it after the update, locking you in the currently running kernel with no ability to update the copy of kernel on ESP.

Alternatively preload the required kernel modules on boot, e.g.:

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

Using bind mount[编辑 | 编辑源代码]

Instead of mounting the ESP itself to /boot, you can mount a directory of the ESP to /boot using a bind mount (see mount(8)). This allows pacman to update the kernel directly while keeping the ESP organized to your liking.

注意: This requires a kernel and bootloader compatible with FAT32. This is not an issue for a regular Arch install, but could be problematic for other distributions (namely those that require symlinks in /boot/). See the forum post [6].

Just like in #替代挂载点, copy all boot files to a directory on your ESP, but mount the ESP outside /boot. Then bind mount the directory:

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

After verifying success, edit your Fstab to make the changes persistent:

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

使用 systemd[编辑 | 编辑源代码]

Systemd features event triggered tasks. In this particular case, the ability to detect a change in path is used to sync the EFISTUB kernel and initramfs files when they are updated in /boot/. The file watched for changes is initramfs-linux-fallback.img since this is the last file built by mkinitcpio, to make sure all files have been built before starting the copy. The systemd path and service files to be created are:

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

Then enable and start efistub-update.path.

提示:For Secure Boot with your own keys, you can set up the service to also sign the image using 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 钩子[编辑 | 编辑源代码]

mkinitcpio 可以生成一个不需要系统级守护程序就可以运行的钩子。它会生成一个后台进程,这个进程在复制文件之前会等待 vmlinuzinitramfs-linux.imginitramfs-linux-fallback.img 的生成。

efistub-update 添加到 /etc/mkinitcpio.conf 的钩子列表中。

/etc/initcpio/install/efistub-update
#!/usr/bin/env bash
build() {
	/usr/local/bin/efistub-copy $$ &
}

help() {
	cat <<HELPEOF
This hook waits for mkinitcpio to finish and copies the finished ramdisk and kernel to the ESP
HELPEOF
}
/usr/local/bin/efistub-copy
#!/bin/sh

if [ "$1" -gt 0 ]
then
	while [ -e /proc/"$1" ]
	do
		sleep .5
	done
fi

rsync -a /boot/ esp/

echo "Synced /boot with ESP"

Using mkinitcpio preset[编辑 | 编辑源代码]

As the presets in /etc/mkinitcpio.d/ support shell scripting, the kernel and initramfs can be copied by just editing the presets.

替换上面的 mkinitcpio 钩子[编辑 | 编辑源代码]

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

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

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

ALL_config="/etc/mkinitcpio.conf"
ALL_kver="${ESP_DIR}/vmlinuz-linux"
cp -af /boot/vmlinuz-linux "${ESP_DIR}/"
[[ -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}/"

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
# mkinitcpio -p linux
另一个例子[编辑 | 编辑源代码]
/etc/mkinitcpio.d/linux.preset
ESP_DIR="esp/EFI/arch"
cp -f "/boot/vmlinuz-linux$suffix" "$ESP_DIR/"
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

使用 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

Troubleshooting[编辑 | 编辑源代码]

ESP on software RAID1[编辑 | 编辑源代码]

It is possible to make the ESP part of a RAID1 array, but doing so brings the risk of data corruption, and further considerations need to be taken when creating the ESP. See [7] and [8] for details and UEFI booting and RAID1 for an in-depth guide with a solution.

The key part is to use --metadata 1.0 in order to keep the RAID metadata at the end of the partition, otherwise the firmware will not be able to access it:

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

固件看不到 EFI 目录[编辑 | 编辑源代码]

如果要给 FAT 文件系统一个卷名(即文件系统标签),请不要将其命名为 EFI。卷名和 EFI 目录名称相同可能会触发某些固件中的错误,导致固件表现得好像 EFI 目录不存在一样。

参见[编辑 | 编辑源代码]