EFI 系统分区
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 System
或EFI (FAT-12/16/32)
。要确认这个分区是 ESP,mount 它,然后看看是否包含一个名为EFI
的目录,如果有,那这肯定就是 ESP。
如果你找到了现有的 EFI 系统分区,前往#挂载分区,否则,你将需要创建一个,前往#创建分区。
创建分区[编辑 | 编辑源代码]
下两节介绍如何创建 EFI 系统分区(ESP)。
分区大小应足以储存启动加载器和启动所需要的其他文件。
为避免与其他操作系统的互操作问题[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 分区磁盘[编辑 | 编辑源代码]
- 建议使用 GPT,因为某些固件可能不支持 UEFI/MBR 启动,因为它不受 Windows Setup 支持。
- bootctl 不支持将 systemd-boot 安装到MBR 分区的磁盘,参见 systemd issue 1125。
另请参阅 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。使用 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 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.
替代挂载点[编辑 | 编辑源代码]
如果不使用#典型挂载点中的简单方法之一,就需要将引导文件复制到 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 中的文件在以后的内核更新中保持最新。否则可能会导致系统无法启动。以下部分讨论了几种自动化的机制。
/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.
/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
.
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 可以生成一个不需要系统级守护程序就可以运行的钩子。它会生成一个后台进程,这个进程在复制文件之前会等待 vmlinuz
、initramfs-linux.img
和 initramfs-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 目录不存在一样。