EFI 系统分区

来自 Arch Linux 中文维基

EFI 系统分区(也称为 ESP)是一个与操作系统无关的分区,其中存储了由 UEFI 固件启动的 UEFI 引导加载器、应用程序和驱动,是 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类型
警告: 双启动时不要重新格式化 ESP,因为它可能包含启动其他操作系统所需的文件。

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

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

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

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

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

推荐创建1 GiB大小的EFI系统分区以确保其提供足够的空间存放多个内核或统一内核镜像英语unified kernel image、引导加载器、固件升级文件以及任何其他操作系统或OEM文件。如何还有疑虑,4 GiB应该足够任何人使用。

注意: 当然也可以使用一个较小的分区,但要注意潜在的兼容问题:
  • 对于早期和/或古怪的 UEFI 实现,可能最少需要 512 MiB。[1]
  • 如果你打算把分区挂载到/boot并且只安装一个内核,那么400 MiB足够使用。
  • 如果是Arch + Windows 双系统,逻辑扇区大小为4096(高级格式化4Kn 设备)的分区应该至少300 MiB[2],其他情况应该至少100 MiB。[3]
  • 为确保分区能够被格式化为FAT32,逻辑扇区大小512字节的分区大小应该至少 36 MiB,4096字节的分区至少 260 MiB。[4]
  • 如果与这些问题都无关,分区大小最小可达 2 MiB,此时只能放得下启动加载器。

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

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

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

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

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

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

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

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

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

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

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

UEFI 规范要求支持 FAT12、FAT16 和 FAT32 文件系统(参见 UEFI specification version 2.10, 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"[specs/UEFI/2.10/13_Protocols_Media_Access.html#file-system-format],建议使用 FAT32英语FAT32。使用 dosfstools 中的 mkfs.fat(8) 工具:

# mkfs.fat -F 32 /dev/sdxY

如果你收到消息 WARNING: Not enough clusters for a 32 bit FAT!并且不能创建更大的EFI系统分区,运行 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系统分区没有挂载到/boot,确保在升级内核时,没有使用systemd自动挂载机制(包括systemd-gpt-auto-generator)。每次系统或内核升级前都要手动挂载EFI系统分区,否则升级后可能会无法挂载,导致你的内核停留在当前版本并无法更新EFI系统分区中的内核文件。

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

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

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

有三种挂载EFI系统分区的典型情况:

  • 挂载 EFI系统分区 到 /boot
    • 便于系统维护和管理,/boot微码包安装CPU微码initramfs文件和mkinitcpio安装内核initramfs镜像的默认位置。
    • FAT在挂载时设置了全局属性,这会阻止设置文件特定的权限拓展属性
    • 通常安装在/boot中的文件与EFI相关文件共享EFI系统分区,提高了EFI系统分区的大小需求
    • 双启动的情况下,系统特定的启动文件会处在被其它系统修改操作的潜在危险中
    • 无法加密/boot,因为固件需要读取EFI相关文件
  • 挂载 EFI系统分区到/efi
    • 当EFI系统分区包含其他系统的文件时最好和操作系统相关的文件分开,这确保了操作系统相关和EFI相关文件的分离。
    • 只有EFI二进制文件(引导加载程序(和可选驱动))和(或)统一内核镜像会安装在EFI系统分区,避免了安装在/boot中的文件对EFI系统分区的大小需求,节约了EFI系统分区的空间。
    • 允许保留/boot中文件的Linux特定的文件系统权限,避免了FAT的限制。
    • 允许根据需求单独挂载EFI系统分区,例如需要升级引导加载程序时。
    • 如果加密整个系统并且配置恰当,除少数需要文件没有被加密,/boot中的文件能够被加密保护:内核及其他文件储存在加密分区,统一内核镜像英语unified kernel image引导加载程序通过相应的文件系统驱动来访问这些文件。
  • 挂载 EFI系统分区到/efi, 然后再挂载一个“拓展引导加载器分区”(XBOOTLDR)分区到 /boot。在以前创建的 ESP 太小而无法容纳多个引导加载程序以及内核但 ESP 又无法轻松调整大小时(例如在 Windows 之后将 Linux 安装到 双引导(多引导) 时),这可能非常有用。至少在 systemd-boot#使用XBOOTLDR安装 时支持此方法。
注意:
  • /efi/boot/efi的替代挂载点[5]/boot/efi在过去被使用但现在不推荐。
  • /efi在安装一开始时不存在,需要先用 mkdir(1) 创建再挂载EFI系统分区到该目录。

替代挂载点[编辑 | 编辑源代码]

如果不使用#典型挂载点中的方法,就需要将引导文件复制到 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 中的文件在以后的内核更新中保持最新。否则可能会导致系统无法启动。以下部分讨论了几种自动化的机制。

使用bind挂载[编辑 | 编辑源代码]

除了将EFI系统分区挂载到/boot你也可以使用bind挂载将分区中的目录挂载到/boot(参考mount(8))。这样pacman就可以直接更新内核文件并保持EFI系统分区的规划。

注意: 这需要内核引导加载程序兼容FAT32。通常安装的Arch没有这个问题,但可能存在于其他发行版中(也就是说需要在 /boot中创建软连接,参见[6]

按照#替代挂载点节内容,复制所有引导文件到你的EFI系统分区,分区挂载点在/boot外面。然后bind挂载目录:

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

检查生效后,编辑Fstab使修改持续有效:

/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英语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 post 钩子 (在 Talk:EFI 系统分区 讨论)


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 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 post 钩子[编辑 | 编辑源代码]

mkinitcpio post 钩子能够在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

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 目录不存在一样。

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