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 目錄不存在一樣。

參見[編輯 | 編輯原始碼]