ユニファイドカーネルイメージ

提供: ArchWiki
ナビゲーションに移動 検索に移動

ユニファイドカーネルイメージ (UKI) は、単一の実行可能ファイルであり、UEFI ファームウェアから直接起動するか、ほとんどまたは全く設定を必要とせずにブートローダーによって自動的に取得されます。これは systemd-stub(7) のような UEFI ブートスタブプログラム、Linux カーネルイメージinitrd、および さらなるリソース を単一の UEFI PE ファイルに組み合わせたものです。

このファイルは、したがってこれらのすべての要素を 署名 して Secure Boot で使用することが容易になります。

ノート: 記事全体で espEFI システムパーティション のマウントポイントを表します。

unified カーネルイメージの準備

mkinitpcio

カーネルコマンドライン

mkinitcpio は、/etc/cmdline.d ディレクトリ内のコマンドライン ファイルからの カーネルパラメータ の読み取りをサポートします。Mkinitcpio は、このディレクトリ内の .conf 拡張子を持つすべてのファイルの内容を連結し、それらを使用してカーネルコマンドラインを生成します。コマンドラインファイル内の # 文字で始まる行はコメントとして扱われ、mkinitcpio によって無視されます。マイクロコードと initramfs を指す エントリを削除 するように注意してください。

例:

/etc/cmdline.d/root.conf
root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw
ヒント:
  • ルート ファイルシステムがデフォルト以外の Btrfs サブボリューム上にある場合は、必ず必要なマウントフラグを rootflags に設定してください。Btrfs#ルートとしてサブボリュームをマウントする を参照してください。
  • たとえば、システムのサブボリューム ID が 256 の場合 (サブボリューム ID は、btrfs subvolume list btrfs_mountpoint を使用して確認できます。または、/etc/fstab) でフラグを見ることができます、カーネルコマンドラインに rootflags=subvolid=256 を追加する必要があります。
  • rootflags は起動時にのみ使用されるため、/etc/fstab 内のすべてのフラグをコピーする必要はありません。Systemd は fstab を読み取り、再マウントし、ブート後にそこにリストされているフラグを自動的に適用します。
/etc/cmdline.d/security.conf
# enable apparmor
lsm=landlock,lockdown,yama,integrity,apparmor,bpf audit=1 audit_backlog_limit=256

あるいは、/etc/kernel/cmdline を使用してカーネルコマンドラインを構成することもできます。

例:

/etc/kernel/cmdline
root=UUID=0a3407de-014b-458b-b5c1-848e92a327a3 rw quiet bgrt_disable
ヒント:
  • root パーティションが systemd によって自動マウントされる 場合、root= パラメータは省略できます。
  • bgrt_disable パラメータは、ACPI テーブルのロード後に OEM ロゴを表示しないように Linux に指示します。

.preset ファイル

次に、/etc/mkinitcpio.d/linux.preset、または使用しているプリセットを、EFI システムパーティション の適切なマウントポイントを指定して、以下のように変更します:

  • PRESETS= の各項目について、PRESET_uki= パラメータのコメントを解除する (つまり # を削除する)、
  • 冗長な initramfs-*.img ファイルを保存しないように、PRESET_image= をコメントアウトする、
  • オプションとして、スプラッシュイメージを追加したい各 PRESET_options= 行に --splash パラメータを追加またはコメント解除します。

以下は linux カーネルと Arch スプラッシュスクリーンの linux.preset の例です。

/etc/mkinitcpio.d/linux.preset
# mkinitcpio preset file for the 'linux' package

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

PRESETS=('default' 'fallback')

#default_config="/etc/mkinitcpio.conf"
#default_image="/boot/initramfs-linux.img"
default_uki="esp/EFI/Linux/arch-linux.efi"
default_options="--splash=/usr/share/systemd/bootctl/splash-arch.bmp"

#fallback_config="/etc/mkinitcpio.conf"
#fallback_image="/boot/initramfs-linux-fallback.img"
fallback_uki="esp/EFI/Linux/arch-linux-fallback.efi"
fallback_options="-S autodetect"
ヒント:
  • 統一されたカーネルイメージからブートしたいだけであれば、ESP のマウント/efi にして、ESP パーティションに存在する必要があるものだけをマウントすることができます。
  • --cmdline /etc/kernel/fallback_cmdlinefallback_options に追加することで、フォールバックイメージに上記とは異なる cmdline を使用することができます (例えば quiet を無くす。)
  • カーネルコマンドラインの埋め込みを省略するには、--no-cmdlinePRESET_options= に追加してください。カーネルパラメーターはブートローダー経由で渡す必要があります。
ノート:
  • PRESET_uki オプションは以前は PRESET_efi_image として知られていました。2022 年 11 月変更, 古いオプションは非推奨ですが、今のところ動作しています。
  • IA32 UEFI では、--uefistub /usr/lib/systemd/boot/efi/linuxia32.efi.stubPRESET_options= に追加してください。

pacman フック

マイクロコードのアップグレード後に再構築をトリガーするには、pacman フック が必要です。

/etc/pacman.d/hooks/ucode.hook
[Trigger]
Operation=Install
Operation=Upgrade
Operation=Remove
Type=Package
# Change to appropriate microcode package
Target=amd-ucode
# Change the linux part above and in the Exec line if a different kernel is used
Target=linux

[Action]
Description=Update Microcode module in initcpio
Depends=mkinitcpio
When=PostTransaction
NeedsTargets
Exec=/bin/sh -c 'while read -r trg; do case $trg in linux) exit 0; esac; done; /usr/bin/mkinitcpio -P'
ヒント: mkinitcpio の繰り返し実行を避けるために、このフックを NVIDIA ドライバー のフックなど、カーネルパッケージを監視する他のフックとマージすることを検討してください。

セキュアブート用の UKI への署名

mkinitcpio ポストフック (mkinitcpio(8) § ABOUT POST HOOKS) を使用すると、生成された統合カーネルイメージに セキュアブート 用に署名できます。次のファイルを 作成 し、実行可能 にします。

/etc/initcpio/post/uki-sbsign
#!/usr/bin/env bash

uki="$3"
[[ -n "$uki" ]] || exit 0

keypairs=(/path/to/db.key /path/to/db.crt)

for (( i=0; i<${#keypairs[@]}; i+=2 )); do
    key="${keypairs[$i]}" cert="${keypairs[(( i + 1 ))]}"
    if ! sbverify --cert "$cert" "$uki" &>/dev/null; then
        sbsign --key "$key" --cert "$cert" --output "$uki" "$uki"
    fi
done

/path/to/db.key/path/to/db.crt をイメージの署名に使いたい鍵ペアのパスに置き換えてください。

UKI の構築

最後に、UKI のディレクトリが存在することを確認し、initramfs を再生成 します。たとえば、linux プリセットの場合は次のようになります。

# mkdir -p esp/EFI/Linux
# mkinitcpio -p linux

必要に応じて、残っている initramfs-*.img/boot または /efi から削除します。

kernel-install

systemdKernel-install スクリプトを使用して、カスタムカーネルとカーネルパッケージ (Pacman を使用してインストール) の両方について、UKI 形式のカーネルを esp に自動的にインストールできます。 Pacman フックを mkinitcpio から kernel-install に切り替える必要があります。

UKI イメージは、mkinitcpio によって直接生成することも、カーネルイメージと生成されたスタンドアロン initramfs イメージを 1 つのファイルに統合する kernel-install によって生成することもできます (kernel-installukify ツールによって) 2024 年 1 月 15 日以降、install には、systemd-ukify が必要です。以下を参照してください) mkinitcpio は、適切なイメージ (layout=uki および uki_generator=mkinitcpio の UKI イメージ) を生成するために kernel-install によって呼び出されます。それ以外の場合は、ukify のスタンドアロンの initramfs イメージ) を生成します。

  • kernel-install layoutuki に設定します。例えば:
    # echo "layout=uki" >> /etc/kernel/install.conf
  • デフォルトでは、レイアウトが uki に設定されている場合、kernel-install は独自の ukify ツールを使用して (ここで systemd-ukify をインストールする必要があります。下記を参照) initramfs とカーネルイメージ mkinitpcio を使用して、uki イメージを直接生成できます。mkinitcpio を使用して uki イメージを生成したい場合は、uki_generatormkinitpcio に設定します。例:
    # echo "uki_generator=mkinitcpio" >> /etc/kernel/install.conf
ノート: 2024 年 1 月 15 日以降、uki_generator でない限り、mkinitcpioUKI をビルドしなくなりました
mkinitcpio に直接設定されます。以前は、systemd-ukify が明示的にインストールされていない場合、暗黙的に設定されていました。}}
  • カーネルを直接インストールする Pacman フック:
    # ln -s /dev/null /etc/pacman.d/hooks/60-mkinitcpio-remove.hook
    # ln -s /dev/null /etc/pacman.d/hooks/90-mkinitcpio-install.hook
    
  • kernel-install の Pacman フックを作成します。pacman-hook-kernel-installAUR を使うことができます。
  • 使用しているカーネルパッケージを削除して再インストールします。

dracut

コマンドラインパラメータ を、例えば /etc/dracut.conf.d/cmdline.conf に配置します。

イメージを生成する。

# dracut -f -q --uefi --uefi-splash-image /usr/share/systemd/bootctl/splash-arch.bmp

こちらも参照 dracut#カーネルのアップグレード時に新しい initramfs を生成

sbctl

sbctl パッケージを インストール して下さい。カーネルコマンド ラインを /etc/kernel/cmdline に保存します。--save パラメータを指定した sbctl bundle コマンドを使用してバンドルを作成し、適切なタイミングで Pacman フックによって再生成します。

# sbctl bundle --save esp/archlinux.efi

他のカーネルと initramfs イメージ用にさらに EFI バイナリを作成するには、パラメーター --kernel-img--initramfs を指定して上記のコマンドを繰り返します。sbctl(8) § EFI BINARY COMMANDS を参照してください。EFI バイナリは、sbctl generate-bundles を使用していつでも再生成できます。

ukify

systemd-ukify パッケージを インストール します。ukify は単独で initramfs を生成できないため、必要な場合は、dracutmkinitcpio、または booster を使用して生成する必要があります。

最小限の動作例は次のようになります。

# /usr/lib/systemd/ukify build --linux=/boot/vmlinuz-linux --initrd=/boot/intel-ucode.img \
--initrd=/boot/initramfs-linux.img \
--cmdline="quiet rw"
ノート: 外部マイクロコード initramfs イメージ を使用する場合 (/boot/amd-ucode.img または /boot/intel-ucode.img) を使用する必要があります。常にメインの initramfs イメージの前に 最初に 配置します (例: /boot/initramfs-linux.img)

次に、結果のファイルを EFI システムパーティションにコピーします。

# cp filename.efi esp/EFI/Linux/
ヒント:
  • 出来上がった EFI 実行ファイルを EFI システムパーティションにコピーするのをスキップするには、--output=esp/EFI/Linux/filename.efi コマンドラインオプションを ukify に使用します。
  • --cmdline オプションを指定する場合、--cmdline=@/path/to/cmdline のように /etc/kernel/cmdline ファイル名の前に @ シンボルを追加することで、カーネルパラメータを読み込むファイル名を指定できます。

intel ucode および /efi マウント ESP を使用した通常のカーネルイメージの systemd サービスを使用した自動 UKI 構築の例:

/etc/ukify.conf
[UKI]
Linux=/boot/vmlinuz-linux
Initrd=/boot/intel-ucode.img /boot/initramfs-linux.img
Cmdline=@/etc/kernel/cmdline
OSRelease=@/etc/os-release
Splash=/usr/share/systemd/bootctl/splash-arch.bmp
ノート: initramfs ジェネレーターが mkinitcpio などの CPU マイクロコードをデフォルトですでにバンドルしている場合は、Initrd=/boot/initramfs-linux.img で initramfs イメージのみを指定します。
/etc/systemd/system/run_ukify.service
[Unit]
Description=Run systemd ukify
[Service]
Type=oneshot
ExecStart=/usr/lib/systemd/ukify build --config=/etc/ukify.conf --output esp/EFI/Linux/archlinux-linux.efi
/etc/systemd/system/run_ukify.path
[Unit]
Description=Run systemd ukify
[Path]
PathChanged=/boot/initramfs-linux.img
PathChanged=/boot/intel-ucode.img
Unit=run_ukify.service
[Install]
WantedBy=multi-user.target

次に、run_ukify.path有効化 します。

手動で

使用するカーネルコマンドラインをファイルに記述し、objcopy(1) を使用してバンドルファイルを作成します。

マイクロコード の場合は、まず次のようにマイクロコードファイルと initrd を連結します。

$ cat esp/cpu_manufacturer-ucode.img esp/initramfs-linux.img > /tmp/combined_initrd.img

統合カーネルイメージを構築するときは、/tmp/combined_initrd.img を initrd として渡します。このファイルは後で削除できます。

ノート: IA32 UEFI では、以下のコマンドの /usr/lib/systemd/boot/efi/linuxx64.efi.stub/usr/lib/systemd/boot/efi/linuxia32.efi.stub に置き換えてください。
$ align="$(objdump -p /usr/lib/systemd/boot/efi/linuxx64.efi.stub | awk '{ if ($1 == "SectionAlignment"){print $2} }')"
$ align=$((16#$align))
$ osrel_offs="$(objdump -h "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" | awk 'NF==7 {size=strtonum("0x"$3); offset=strtonum("0x"$4)} END {print size + offset}')"
$ osrel_offs=$((osrel_offs + "$align" - osrel_offs % "$align"))
$ cmdline_offs=$((osrel_offs + $(stat -Lc%s "/usr/lib/os-release")))
$ cmdline_offs=$((cmdline_offs + "$align" - cmdline_offs % "$align"))
$ splash_offs=$((cmdline_offs + $(stat -Lc%s "/etc/kernel/cmdline")))
$ splash_offs=$((splash_offs + "$align" - splash_offs % "$align"))
$ initrd_offs=$((splash_offs + $(stat -Lc%s "/usr/share/systemd/bootctl/splash-arch.bmp")))
$ initrd_offs=$((initrd_offs + "$align" - initrd_offs % "$align"))
$ linux_offs=$((initrd_offs + $(stat -Lc%s "initrd-file")))
$ linux_offs=$((linux_offs + "$align" - linux_offs % "$align"))

$ objcopy \
    --add-section .osrel="/usr/lib/os-release" --change-section-vma .osrel=$(printf 0x%x $osrel_offs) \
    --add-section .cmdline="/etc/kernel/cmdline" \
    --change-section-vma .cmdline=$(printf 0x%x $cmdline_offs) \
    --add-section .splash="/usr/share/systemd/bootctl/splash-arch.bmp" \
    --change-section-vma .splash=$(printf 0x%x $splash_offs) \
    --add-section .initrd="initrd-file" \
    --change-section-vma .initrd=$(printf 0x%x $initrd_offs) \
    --add-section .linux="vmlinuz-file" \
    --change-section-vma .linux=$(printf 0x%x $linux_offs) \
    "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" "linux.efi"

注意すべき点がいくつかあります:

  • [1] で推奨されているように、オフセットは動的に計算されるので、セクションが重なることはありません。
  • セクションは、PE スタブの SectionAlignment フィールドが示す値 (通常は 0x1000) にアラインメントされます。
  • カーネルイメージは、[2] で述べられているように、インプレース解凍で後続のセクションが上書きされるのを防ぐために、最後のセクションにある必要があります。

イメージを作成したら、それを EFI システムパーティションにコピーします。

# cp linux.efi esp/EFI/Linux/

起動方法

ノート: セキュアブート がアクティブな場合、.cmdline が埋め込まれた統合カーネルイメージは、(ブートエントリを使用するか対話的に) 渡されたすべてのコマンドラインオプションを無視します。セキュアブートがアクティブでない場合、コマンドライン経由で渡されたオプションは、埋め込まれた .cmdline をオーバーライドします。

systemd-boot

systemd-bootesp/EFI/Linux/ 内で Unified カーネルイメージを検索しますので、それ以上の設定は必要ありません。sd-boot(7) § FILES を見て下さい。

rEFInd

rEFInd は、EFI システム パーティション上の Unified カーネルイメージを自動検出し、それらをロードできます。refind.conf で手動で指定することもできます。デフォルトでは次の場所にあります。

esp/EFI/refind/refind.conf
menuentry Linux {
    loader esp/EFI/Linux/archlinux-linux.efi
}

イメージが ESP のルートにある場合、rEFInd は次のようにその名前のみを必要とします: loader archlinux-linux.efi この方法で起動すると、esp/EFI/refind_linux.conf からのカーネルパラメータは渡されません。

GRUB

rEFInd と同様に、GRUBGRUB#Unified カーネルイメージをチェインロード で説明されているように EFI UKI をチェーンロードできます。

UEFI から直接起動

efibootmgr を使って .efi ファイルに UEFI ブートエントリを作成することができます。

# efibootmgr --create --disk /dev/sdX --part partition_number --label "Arch Linux" --loader '\EFI\Linux\arch-linux.efi' --unicode

オプションの説明は efibootmgr(8) をご覧ください。

参照