跳至內容

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系統分區以確保其提供足夠的空間存放多個內核或統一內核鏡像、引導加載器、固件升級文件以及任何其他作業系統或OEM文件。如果還有疑慮,4 GiB應該足夠任何人使用,比如有些工具像Limine 啟動引導器帶有用於Btrfs的Snapper集成英語Limine#Snapper snapshot integration for Btrfs,支持創建多個可啟動的快照。

注意:當然也可以使用一個較小的分區,但要注意潛在的兼容問題:
  • 對於早期和/或古怪的 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:

  • fdisk:創建分區,然後使用命令t並指定uefi別名將分區類型更改為 EFI System
  • gdisk:創建分區類型為 EF00 的分區。
  • GNU Parted:創建文件系統類型為 fat32 的分區,並設置 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。使用 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中的文件能夠被加密保護:內核及其他文件儲存在加密分區,統一內核鏡像引導加載程序通過相應的文件系統驅動來訪問這些文件。
  • 掛載 EFI系統分區到/efi, 然後再掛載一個「拓展引導加載器分區」(XBOOTLDR)分區到 /boot。在以前創建的 ESP 太小而無法容納多個引導加載程序以及內核但 ESP 又無法輕鬆調整大小時(例如在 Windows 之後將 Linux 安裝到雙引導(多引導)時),這可能非常有用。至少在 systemd-boot#使用XBOOTLDR安裝時支持此方法。
注意:
  • /efi/boot/efi的替代掛載點[5][6]/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中創建軟連接,參見[7]

按照#替代掛載點節內容,複製所有引導文件到你的EFI系統分區,分區掛載點在/boot外面。然後bind掛載目錄:

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

檢查生效後,編輯Fstab使修改持續有效:

/etc/fstab
esp/EFI/arch /boot none defaults,bind 0 0

使用 systemd

[編輯 | 編輯原始碼]

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/

隨後啟用啟動efistub-update.path

提示:若需使用自有密鑰實現安全啟動,您可以配置該服務,使其額外使用 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 preset

[編輯 | 編輯原始碼]

因為/etc/mkinitcpio.d/中的preset支持shell腳本,編輯presets可以複製內核和initramfs到ESP。

替換 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

提示和技巧

[編輯 | 編輯原始碼]

替換為更大的分區

[編輯 | 編輯原始碼]

如果硬碟已經預裝作業系統,那麼EFI系統分區大小可能會比#創建分區中推薦的小。例如Windows安裝程序會在一個非4Kn設備上創建一個很小的100 MiB EFI系統分區。

這種情況下最好新創建一個更大的EFI系統分區以避免存儲空間耗盡。

在Windows裡為新分區騰出空間

[編輯 | 編輯原始碼]

在Windows下,使用磁碟管理(diskmgmt.msc)或在命令行使用diskpart.exe工具管理分區。

以管理員權限運行diskmgmt.msc

  1. 右鍵C盤分區,然後選擇壓縮卷
  1. 輸入4096作為壓縮的空間量,並點擊壓縮

之後在C盤後方應該會出現4 GiB的未分配空間。

引導進入Arch Linux或Arch Linux安裝介質環境為下一步創建新分區做準備。

刪除舊的分區並創建一個新的ESP

[編輯 | 編輯原始碼]

首先確保備份好原來EFI系統分區的內容,若EFI系統分區的掛載點為esp

# cp -a esp /esp_backup

卸載EFI系統分區:

# umount esp
注意:在本地安裝的系統下你可能還需要停止 esp.mountesp.automount 單元以防止系統再次自動掛載它。

運行blkid並記下舊分區的UUID和PARTUUID,下一步這些將值用於新分區上。

# blkid
/dev/sdxY: UUID="XXXX-XXXX" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI system partition" PARTUUID="YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"

使用gptfdisksgdisk刪除舊分區:

# 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分區來擴大ESP

[編輯 | 編輯原始碼]

如果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)

首先停用swap分區,並把它從fstab裡刪除。

使用fdisk刪除swap分區並增大EFI系統分區。

  1. 運行:
    # fdisk -l /dev/sdx
  2. 使用d命令刪除swap分區(在示例中swap分區的分區號是2)。
  3. 使用e命令增大EFI系統分區(在示例中EFI系統分區的分區號是1)。使用給出的默認值作為新分區的大小並按下Enter確認。
  4. 通過{{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
注意:在本地安裝的系統下你可能還需要停止 esp.mountesp.automount 單元以防止系統再次自動掛載它。

從分區中擦除文件系統的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放在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方法的風險,但只在使用單系統時有用。

固件看不到 EFI 目錄

[編輯 | 編輯原始碼]

如果要給 FAT 文件系統一個卷名(即文件系統標籤),請不要將其命名為 EFI。卷名和 EFI 目錄名稱相同可能會觸發某些固件中的錯誤,導致固件表現得好像 EFI 目錄不存在一樣。

參見

[編輯 | 編輯原始碼]