EFI 系统分区
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 System
或EFI (FAT-12/16/32)
。要确认这个分区是 ESP,mount 它,然后看看是否包含一个名为EFI
的目录,如果有,那这肯定就是 ESP。
如果你找到了现有的 EFI 系统分区,前往#挂载分区,否则,你将需要创建一个,前往#创建分区。
创建分区[编辑 | 编辑源代码]
下两节介绍如何创建 EFI 系统分区(ESP)。
分区大小应足以储存启动加载器和启动所需要的其他文件。
推荐创建1 GiB大小的EFI系统分区以确保其提供足够的空间存放多个内核或统一内核镜像、引导加载器、固件升级文件以及任何其他操作系统或OEM文件。如何还有疑虑,4 GiB应该足够任何人使用。
GPT 分区磁盘[编辑 | 编辑源代码]
GUID Partition Table 中 EFI 系统分区以分区类型 GUID C12A7328-F81F-11D2-BA4B-00A0C93EC93B
标识。
从以下方法中任选其一在 GPT 分区的磁盘上创建 ESP:
- fdisk:创建分区,然后使用命令
t
将分区类型更改为EFI System
。 - gdisk:创建分区类型为
EF00
的分区。 - GNU Parted:创建文件系统类型为
fat32
的分区,并设置esp
标志。
创建分区之后,应当格式化为一种文件系统。前往#格式化分区。
MBR 分区磁盘[编辑 | 编辑源代码]
- 某些固件可能不支持 UEFI/MBR 启动,因为它不受 Windows Setup 支持。
- bootctl 不支持将 systemd-boot 安装到MBR 分区的磁盘,参见 systemd issue 1125。
另请参阅 Partitioning#选择 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 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。使用 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 系统分区可能的挂载点。
/boot
,确保在升级内核时,没有使用systemd自动挂载机制(包括systemd-gpt-auto-generator)。每次系统或内核升级前都要手动挂载EFI系统分区,否则升级后可能会无法挂载,导致你的内核停留在当前版本并无法更新EFI系统分区中的内核文件。
或者在启动时预加载需要的内核模块,例如:
/etc/modules-load.d/vfat.conf
vfat nls_cp437 nls_ascii
典型挂载点[编辑 | 编辑源代码]
有三种挂载EFI系统分区的典型情况:
- 挂载 EFI系统分区 到
/boot
:
- 挂载 EFI系统分区到
/efi
:- 当EFI系统分区包含其他系统的文件时最好和操作系统相关的文件分开,这确保了操作系统相关和EFI相关文件的分离。
- 只有EFI二进制文件(引导加载程序(和可选驱动))和(或)统一内核镜像会安装在EFI系统分区,避免了安装在
/boot
中的文件对EFI系统分区的大小需求,节约了EFI系统分区的空间。 - 允许保留
/boot
中文件的Linux特定的文件系统权限,避免了FAT的限制。 - 允许根据需求单独挂载EFI系统分区,例如需要升级引导加载程序时。
- 如果加密整个系统并且配置恰当,除少数需要文件没有被加密,
/boot
中的文件能够被加密保护:内核及其他文件储存在加密分区,统一内核镜像或引导加载程序通过相应的文件系统驱动来访问这些文件。
- 挂载 EFI系统分区到
/efi
, 然后再挂载一个“拓展引导加载器分区”(XBOOTLDR)分区到/boot
。在以前创建的 ESP 太小而无法容纳多个引导加载程序以及内核但 ESP 又无法轻松调整大小时(例如在 Windows 之后将 Linux 安装到双引导(多引导)时),这可能非常有用。至少在 systemd-boot#使用XBOOTLDR安装时支持此方法。
替代挂载点[编辑 | 编辑源代码]
如果不使用#典型挂载点中的方法,就需要将引导文件复制到 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系统分区的规划。
按照#替代挂载点节内容,复制所有引导文件到你的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
.
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 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 目录不存在一样。