EFI ブートスタブ

提供: ArchWiki
2022年3月3日 (木) 23:19時点におけるKgx (トーク | 投稿記録)による版 (前文の翻訳更新)
ナビゲーションに移動 検索に移動

関連記事

Linux カーネルは EFISTUB ブートをサポートしており、EFI ファームウェアがカーネルを EFI 実行可能ファイルとしてロードできるようにします。このオプションは、Arch Linux カーネルではデフォルトで有効になっています。カーネルをコンパイルする場合は、カーネル構成で CONFIG_EFI_STUB=yを設定することでアクティブにできます。詳細については、EFI BootStub を参照してください。

EFISTUB の設定

EFI システムパーティションを作成したら、システムパーティションをマウントする方法を選択してください。一番簡単なのは /boot にマウントするか /bootバインドマウントすることです。pacman が直接 EFI ファームウェアによって読み込まれるカーネルを更新してくれます。その場合、#EFISTUB の起動に進んでください。

ノート: rEFInd などのファイルシステムドライバーを備えているブートマネージャを使用する場合、カーネルや initramfs を ESP 以外に保存できます。

他の ESP マウントポイント

EFI システムパーティションを /boot にマウントしない場合、ブートファイルをコピーする必要があります (以下 EFI システムパーティションの場所を esp とします):

# mkdir -p esp/EFI/arch
# cp /boot/vmlinuz-linux esp/EFI/arch/vmlinuz-linux
# cp /boot/initramfs-linux.img esp/EFI/arch/initramfs-linux.img
# cp /boot/initramfs-linux-fallback.img esp/EFI/arch/initramfs-linux-fallback.img
ノート: Intel 製の CPU を使用する場合、マイクロコードをブートエントリの場所にコピーする必要があります。

さらに、カーネルのアップデートがあったときに ESP のファイルを更新する必要があります。更新しないとシステムが起動できなくなってしまいます。以下のセクションでは自動でコピーする方法を説明しています。

systemd を使う

Systemd には特定のイベントが起こったときにタスクを実行する機能があります。/boot が更新されたときに特定のパスにおける変更を検知して EFISTUB カーネルと initramfs ファイルを同期してください:

/etc/systemd/system/efistub-update.path
[Unit]
Description=Copy EFISTUB Kernel to UEFISYS Partition

[Path]
PathChanged=/boot/initramfs-linux-fallback.img

[Install]
WantedBy=multi-user.target
WantedBy=system-update.target
ノート: 上記のユニットは initramfs-linux-fallback.img の変更を監視します。このファイルは mkinitcpio によって最後に作成されるファイルであり、ビルドが完了する前に他のファイルをコピーしてしまう競合状態を避けることができます。
/etc/systemd/system/efistub-update.service
[Unit]
Description=Copy EFISTUB Kernel to UEFISYS Partition

[Service]
Type=oneshot
ExecStart=/usr/bin/cp -f /boot/vmlinuz-linux esp/EFI/arch/vmlinuz-linux
ExecStart=/usr/bin/cp -f /boot/initramfs-linux.img esp/EFI/arch/initramfs-linux.img
ExecStart=/usr/bin/cp -f /boot/initramfs-linux-fallback.img esp/EFI/arch/initramfs-linux-fallback.img
ヒント: (自分の鍵を使って) セキュアブートする場合、(sbsigntools を使って) イメージに署名するようにサービスを設定することができます:
ExecStart=/usr/bin/sbsign --key /path/to/db.key --cert /path/to/db.crt --output esp/EFI/arch/vmlinuz-linux esp/EFI/arch/vmlinuz_linux

ユニットを作成したら efistub-update.path起動有効化してください。

incron を使う

incron を使うことでカーネルの更新時に EFISTUB カーネルを同期させるスクリプトを実行できます。

/usr/local/bin/efistub-update.sh
#!/usr/bin/env bash
/usr/bin/cp -f /boot/vmlinuz-linux esp/EFI/arch/vmlinuz-linux
/usr/bin/cp -f /boot/initramfs-linux.img esp/EFI/arch/initramfs-linux.img
/usr/bin/cp -f /boot/initramfs-linux-fallback.img esp/EFI/arch/initramfs-linux-fallback.img
ノート: 最初のパラメータの /boot/initramfs-linux-fallback.img は監視するファイルです。2番目のパラメータの IN_CLOSE_WRITE は監視する操作です。3番目のパラメータの /usr/local/bin/efistub-update.sh は実行するスクリプトです。
/etc/incron.d/efistub-update.conf
/boot/initramfs-linux-fallback.img IN_CLOSE_WRITE /usr/local/bin/efistub-update.sh

この方法を使う場合、incrond のサービスを有効にしてください:

# systemctl enable incrond.service

mkinitcpio フックを使う

Mkinitcpio はフックを生成することができ、システムレベルのデーモンを必要としません。バックグラウンドプロセスが生成され vm-linuz, initramfs-linux.img, initramfs-linux-fallback.img が生成されてからファイルをコピーします。

/etc/mkinitcpio.conf のフックのリストに efistub-update を追加してください。

/usr/lib/initcpio/install/efistub-update
#!/usr/bin/env bash
build() {
	/root/watch.sh &
}

help() {
	cat <<HELPEOF
This hook waits for mkinitcpio to finish and copies the finished ramdisk and kernel to the ESP
HELPEOF
}
/root/watch.sh
#!/usr/bin/env bash

while [[ -d "/proc/$PPID" ]]; do
	sleep 1
done

/usr/bin/cp -f /boot/vmlinuz-linux esp/EFI/arch/vmlinuz-linux
/usr/bin/cp -f /boot/initramfs-linux.img esp/EFI/arch/initramfs-linux.img
/usr/bin/cp -f /boot/initramfs-linux-fallback.img esp/EFI/arch/initramfs-linux-fallback.img

echo "Synced kernel with ESP"

mkinitcpio フックを使う (別の方法)

上記の方法と別に、コピー操作を減らす方法があります。逆転の発想で initramfs を /boot ではなく EFI パーティションに直接保存します。そして mkinitcpio フックを使ってカーネルなどのファイルを ESP にコピーします。

/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="/boot/efi/EFI/arch"

ALL_config="/etc/mkinitcpio.conf"
ALL_kver="/boot/vmlinuz-linux"

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
default_image="${ESP_DIR}/initramfs-linux.img"
default_options="-A esp-update-linux"

#fallback_config="/etc/mkinitcpio.conf"
fallback_image="${ESP_DIR}/initramfs-linux-fallback.img"
fallback_options="-S autodetect"

そして /usr/lib/initcpio/install/esp-update-linux ファイルを作成して実行可能属性を付与してください:

/usr/lib/initcpio/install/esp-update-linux
# Directory to copy the kernel, the initramfs...
ESP_DIR="/boot/efi/EFI/arch"

build() {
	cp /boot/vmlinuz-linux "${ESP_DIR}/vmlinuz-linux.efi"
	# If ucode is used uncomment this line
	#cp /boot/intel-ucode.img "${ESP_DIR}/"
}

help() {
	cat <<HELPEOF
This hook copies the kernel to the ESP partition
HELPEOF
}

テストするには以下のコマンドを実行:

# rm /boot/initramfs-linux-fallback.img
# rm /boot/initramfs-linux.img
# mkinitcpio -p linux

EFISTUB の起動

警告: Linux カーネル EFISTUB の initramfs のパスは EFI システムパーティションのルートディレクトリからの相対パスになります。例えば、initramfs が $esp/EFI/arch/initramfs-linux.img に存在する場合、UEFI で設定するのは initrd=/EFI/arch/initramfs-linux.img または initrd=\EFI\arch\initramfs-linux.img になります。以下の例では必要なファイル全てが $esp/ にあることを前提としています。

ブートマネージャを使う

UEFI ブートマネージャには UEFI ブートを簡単にすることができるオプションが存在します (特に複数のカーネルやオペレーティングシステムをインストールしている場合)。詳しくはブートローダーを見てください。

UEFI Shell を使う

通常の UEFI アプリケーションと同じように UEFI Shell から EFISTUB カーネルを起動できます。その場合、通常のパラメータとして起動する EFISTUB カーネルファイルにカーネルパラメータを渡してください:

> fs0:
> /vmlinuz-linux root=PARTUUID=3518bb68-d01e-45c9-b973-0b5d918aae96 rw initrd=/initramfs-linux.img

毎回カーネルパラメータを全て指定したくない場合、UEFI システムパーティションに archlinux.nsh などのシェルスクリプトの実行コマンドを保存することができます。以下のコマンドで起動できるようになります:

> fs0:
> archlinux

UEFI を直接使う

UEFI は GRUB などの中間的なブートローダーを排除できるように設計されています。使用しているマザーボードの UEFI 実装に問題がなければ、カーネルパラメータを UEFI ブートエントリの中に埋め込んでマザーボードから Arch を直接起動できます。efibootmgr や UEFI Shell v2 を使ってマザーボードのブートエントリを編集できます。

efibootmgr

以下のようなコマンドを実行してください:

# efibootmgr -d /dev/sdX -p Y -c -L "Arch Linux" -l /vmlinuz-linux -u "root=/dev/sdBZ rw initrd=/initramfs-linux.img"

/dev/sdXY は ESP が存在するディスクとパーティションに置き換えてください。root= パラメータを変更することで Linux のルートパーティションを指定できます (ディスクの UUID を使うこともできます)。-u 引数をダブルクオーテーションで囲っているのはカーネルパラメータを指定するためであり、ハイバネートマイクロコードを使う場合はパラメータを追加する必要があります。

以下のコマンドを実行することで作成したエントリが問題ないか確認できます:

# efibootmgr -v
警告: 特定のカーネルと efibootmgr のバージョンの組み合わせでは新しいブートエントリを作成できません。NVRAM に空き容量が存在しないことが原因です。EFI のダンプファイルを削除してみてください:
# rm /sys/firmware/efi/efivars/dump-*
もしくは、efi_no_storage_paranoia カーネルパラメータを使って起動してください。キャッシュに前のバージョンが存在する場合、efibootmgr のバージョンを 0.11.0 にダウングレードしてみてください。このバージョンでは Linux のバージョン 4.0.6 が動作します。詳しくは FS#34641 を参照。

ブートの順序を設定するには、次のコマンドを実行:

# efibootmgr -o XXXX,XXXX

XXXX は `efibootmgr` コマンドの出力で確認できるエントリの数字に置き換えてください。

ヒント: ブートエントリを作成するコマンドをシェルスクリプトに保存することで、(カーネルパラメータを変更するときなど) 編集が簡単になります。

efibootmgr については UEFI#efibootmgr を見てください。https://bbs.archlinux.org/viewtopic.php?pid=1090040#p1090040 も参照。

UEFI Shell

UEFI の実装によっては efibootmgr を使用して NVRAM を編集できないことがあります。efibootmgr でエントリを作成できない場合、UEFI Shell v2 の bcfg コマンドを使用する方法があります。

カーネルのエントリを追加するには、以下を実行:

Shell> bcfg boot add N fsV:\vmlinuz-linux "Arch Linux"

N はエントリの優先度 (0 が一番優先されます) に、V は EFI パーティションのボリューム番号に置き換えてください。ボリューム番号がわからない場合、map コマンドを使うことでファイルシステムが確認できるので、ls コマンドで中身を確認してください:

Shell> map
Shell> ls fs0:

最低限必要なカーネルオプションだけを追加する場合:

Shell> bcfg boot -opt N "root=/dev/sdX# rw initrd=\initramfs-linux.img"

N は優先度に /dev/sdX# はルートパーティションに置き換えてください。