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應該足夠任何人使用,比如有些工具像Limine 啟動引導器帶有用於Btrfs的Snapper集成,支持創建多個可啟動的快照。
GUID Partition Table 中 EFI 系統分區以分區類型 GUID C12A7328-F81F-11D2-BA4B-00A0C93EC93B 標識。
從以下方法中任選其一在 GPT 分區的磁碟上創建 ESP:
-
fdisk:創建分區,然後使用命令
t並指定uefi別名將分區類型更改為EFI System。 -
gdisk:創建分區類型為
EF00的分區。 -
GNU Parted:創建文件系統類型為
fat32的分區,並設置esp標誌。
創建分區之後,應當格式化為一種文件系統。前往#格式化分區。
- 某些固件可能不支持 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 中的文件在以後的內核更新中保持最新。否則可能會導致系統無法啟動。以下部分討論了幾種自動化的機制。
除了將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具備事件觸髮型任務能力。在本特定場景中,該系統利用路徑監控功能來檢測/boot/目錄下EFISTUB內核與初始化內存檔文件的更新情況,並在文件被更新時執行同步操作。選擇監視initramfs-linux-fallback.img文件是因為該文件由mkinitcpio最後生成,可確保所有文件構建完成後再啟動複製流程。需要創建的systemd路徑單元文件及服務單元文件包括:
/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/
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。
因為/etc/mkinitcpio.d/中的preset支持shell腳本,編輯presets可以複製內核和initramfs到ESP。
編輯文件 /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 鉤子能夠在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 鉤子。
第一個文件是一個監控相關文件的鉤子,如果文件在前一個事務中被修改,鉤子就會運行。
/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
如果硬碟已經預裝作業系統,那麼EFI系統分區大小可能會比#創建分區中推薦的小。例如Windows安裝程序會在一個非4Kn設備上創建一個很小的100 MiB EFI系統分區。
這種情況下最好新創建一個更大的EFI系統分區以避免存儲空間耗盡。
在Windows下,使用磁碟管理(diskmgmt.msc)或在命令行使用diskpart.exe工具管理分區。
以管理員權限運行diskmgmt.msc。
- 右鍵C盤分區,然後選擇壓縮卷
- 輸入
4096作為壓縮的空間量,並點擊壓縮。
之後在C盤後方應該會出現4 GiB的未分配空間。
引導進入Arch Linux或Arch Linux安裝介質環境為下一步創建新分區做準備。
首先確保備份好原來EFI系統分區的內容,若EFI系統分區的掛載點為esp:
# cp -a esp /esp_backup
卸載EFI系統分區:
# umount esp
運行blkid並記下舊分區的UUID和PARTUUID,下一步這些將值用於新分區上。
# blkid
/dev/sdxY: UUID="XXXX-XXXX" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI system partition" PARTUUID="YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"
# sgdisk --delete=Y /dev/sdx
在最大的未分配空間上創建新分區並指定PARTLABEL和使用舊分區的PARTUUID:
# sgdisk --align-end --largest-new=0 --typecode=0:ef00 --change-name=0:'EFI system partition' --partition-guid=0:YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY /dev/sdx
使用fdisk確認新創建的大小為4 GiB的EFI系統分區:
# fdisk -l /dev/sdx
... Device Start End Sectors Size Type /dev/sdx1 158099456 166488063 8388608 4G EFI System /dev/sdx2 206848 239615 32768 16M Microsoft reserved /dev/sdx3 239616 158099455 157859840 75.3G Microsoft basic data /dev/sdx4 166488064 167768063 1280000 625M Windows recovery environment /dev/sdx5 167768064 176156671 8388608 4G Linux swap /dev/sdx6 176156672 243265535 67108864 32G Linux root (x86-64) Partition table entries are not in disk order.
分區編號在刪除和創建分區後沒有重新排列,所以EFI系統分區編號應該和之前一樣。
將新分區格式化為FAT32,並使用舊的UUID(需要刪除UUID中的「-」橫線符):
# mkfs.fat -F 32 -i XXXXXXXX /dev/sdxY
最後掛載新分區並恢復原有內容:
# mount /dev/sdxY esp # cp -a /esp_backup/. esp/
如果你先前停止了esp.automount,再次啟動它。
如果swap正好在EFI系統分區後面,你可以犧牲swap空間用於擴大EFI系統分區。例如你的分區布局類似下面的例子:
# fdisk -l /dev/sdx
... Device Start End Sectors Size Type /dev/sdx1 2048 616447 614400 300M EFI System /dev/sdx2 616448 9005055 8388608 4G Linux swap /dev/sdx3 9005056 125827071 116822016 55.7G Linux root (x86-64)
使用fdisk刪除swap分區並增大EFI系統分區。
- 運行:
# fdisk -l /dev/sdx
- 使用
d命令刪除swap分區(在示例中swap分區的分區號是2)。 - 使用
e命令增大EFI系統分區(在示例中EFI系統分區的分區號是1)。使用給出的默認值作為新分區的大小並按下Enter確認。 - 通過{{ic|w}命令將修改落盤並退出fdisk。
分區大小修改後需要修改分區內的文件系統大小。因為fatresize(1) 存在問題並且libparted 不能修改FAT卷的大小為確切的值,唯一的辦法就是備份文件系統的文件,然後創建新的分區來利用所有分區空間。
記下文件系統的UUID:
$ lsblk -dno UUID /dev/sdxY
XXXX-XXXX
備份好原來EFI系統分區的內容,若EFI系統分區的掛載點為esp:
# cp -a esp /esp_backup
卸載EFI系統分區:
# umount esp
從分區中擦除文件系統的signature以避免受到舊文件系統的影響:
# wipefs -af /dev/sdxY
將新分區格式化為FAT32,並使用舊的UUID(需要刪除UUID中的「-」橫線符):
# mkfs.fat -F 32 -i XXXXXXXX /dev/sdxY
最後掛載新分區並恢復原有內容:
# mount /dev/sdxY esp # cp -a /esp_backup/. esp/
如果你先前停止了esp.automount,再次啟動它。
由於現在沒有了swap分區,將swap放在一個交換文件上。
將ESP放在RAID1陣列上是可能的,但這麼做也會帶來數據損壞的風險,創建ESP時也要做額外的考慮,詳情參見[8]和[9]還有UEFI booting and RAID1。
整個方案的關鍵點是使用--metadata 1.0將RAID metadata放在分區尾部,否則固件無法訪問ESP:
# mdadm --create --verbose --level=1 --metadata=1.0 --raid-devices=2 /dev/md/ESP /dev/sdaX /dev/sdbY
或者如果你的ESP不會頻繁修改,可以在進行相關更新時把主要ESP的修改複製到不同磁碟上的備用ESP上。備用ESP的引導啟動項可以用efibootmgr手動添加。參見debian wiki。需要注意這個辦法雖然避免了RAID方法的風險,但只在使用單系統時有用。
如果要給 FAT 文件系統一個卷名(即文件系統標籤),請不要將其命名為 EFI。卷名和 EFI 目錄名稱相同可能會觸發某些固件中的錯誤,導致固件表現得好像 EFI 目錄不存在一樣。