スクエニ ITエンジニア ブログ

UEFI対応BIOSとSecureBootの訓練をしてみる話

はじめに

昨今UEFI対応BIOS+SecureBootネタに執心のネバー・フレンズ・Tです。

Linuxを相手にしたUEFI対応BIOS+SecureBoot仕様の操作の練習(試行錯誤)を安全にやりたい方向けに手元の仮想環境(kvm)で実際にUEFI対応BIOSを動かし、SecureBootを有効にしてDebianを対象にブートの設定だけ手組してみることを語ってみます。

こちらに慣れておけば、以降はお好きにUEFIの実験ができるようになり、理解が進むのでは?と思ってます。

UEFI対応BIOS+SecureBoot(Debian デフォルト起動編)の概略図とEFI Variable

いままでレガシーBIOS Bootばかり触ってきた人にありがちなのですが、巷のUEFIの解説やLinuxのUEFI Bootの解説、断片的なUEFI関係のblogやwikiを見て、あまりに断片的な内容が多いためか理解するのに心折られた方も多いかと思います。かくいう自分もその一人で、わけがわからないよ!と正直思ってました。UEFI対応BOISとSecureBootの起動についてぜんぜんわからない俺はブートを雰囲気でやっている、ということから少しは抜け出してみます。

まずは、Debianの起動からです。shim-unsignedパッケージのソースコード、及び、手元のDebian unstableの設定ファイルから、概略ですが、図に起こしてみました。

起動概略図

図中、EFI VariableがUEFI対応BOISのブートを制御する変数群となります。図に書ききれないため省略しておりますが、これら変数にはもれなくGUIDというIDが付属しており、このGUIDと変数名のペアで参照することになっています。実際にBootOrder変数を例にLinuxから見てみます。Linux環境はDebian(unstable)となります。

$ sudo apt install efivar
$ efivar -l | fgrep -i bootorder
8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder
$ efivar -L | fgrep 8be4df61
{8be4df61-93ca-11d2-aa0d-00e098032b8c} {global} efi_guid_global EFI Global Variable

8be4df61-93ca-11d2-aa0d-00e098032b8c-というのがGUIDであり、分類としては、EFI Global Variableに所属していることを示しています。早速中身を確認してみます。

$ efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder
GUID: 8be4df61-93ca-11d2-aa0d-00e098032b8c
Name: "BootOrder"
Attributes:
        Non-Volatile
        Boot Service Access
        Runtime Service Access
Value:
00000000  07 00 02 00 05 00 01 00  06 00 04 00 03 00        |..............  |

こちらには起動候補(ブートエントリ)の起動優先順位が16bit リトルエンディアンで記録されています。16bitづつ値を読み取ると、7番目、2番目、5番目、1番目、6番目、4番目、最も低い優先順位は3番目のブートエントリということを示しています。

次にこのマシンの一番優先順位が高いブートエントリの中身を見てみます。一番優先順位が高いのは7番目ですから、見るべき変数はBoot0007を見ます。GUIDはBootOrder変数についているものと同じものを利用します。

$ efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-Boot0007
GUID: 8be4df61-93ca-11d2-aa0d-00e098032b8c
Name: "Boot0007"
Attributes:
        Non-Volatile
        Boot Service Access
        Runtime Service Access
Value:
00000000  01 00 00 00 74 00 57 00  69 00 6e 00 64 00 6f 00  |....t.W.i.n.d.o.|
00000010  77 00 73 00 20 00 42 00  6f 00 6f 00 74 00 20 00  |w.s. .B.o.o.t. .|
00000020  4d 00 61 00 6e 00 61 00  67 00 65 00 72 00 00 00  |M.a.n.a.g.e.r...|
00000030  04 01 2a 00 01 00 00 00  00 08 00 00 00 00 00 00  |..*.............|
00000040  00 20 03 00 00 00 00 00  d9 5f 1f 95 7a 3a 27 4b  |. ......._..z:'K|
00000050  a2 24 95 3f d7 56 77 c4  02 02 04 04 46 00 5c 00  |.$.?.Vw.....F.\.|
00000060  45 00 46 00 49 00 5c 00  4d 00 69 00 63 00 72 00  |E.F.I.\.M.i.c.r.|
00000070  6f 00 73 00 6f 00 66 00  74 00 5c 00 42 00 6f 00  |o.s.o.f.t.\.B.o.|
00000080  6f 00 74 00 5c 00 62 00  6f 00 6f 00 74 00 6d 00  |o.t.\.b.o.o.t.m.|
00000090  67 00 66 00 77 00 2e 00  65 00 66 00 69 00 00 00  |g.f.w...e.f.i...|
000000a0  7f ff 04 00 57 49 4e 44  4f 57 53 00 01 00 00 00  |....WINDOWS.....|
...中略...

16進ダンプのASCII表示の部分からなんとなくわかるとおり、Windowsのブートエントリです。エントリにはラベルがついておりWindows Boot Managerという名前のラベルのようです。ブートローダそのものは、\EFI\Microsoft\Boot\bootmgfw.efiであることがわかります。ここからこのマシンは電源をONにすると、何もしなければ\EFI\Microsoft\Boot\bootmgfw.efiによりWindowsが起動するマシンであることがわかります。

今回の目的に関係しそうなEFI Variableを表にすると以下のとおりです。EFI Variableはブート時に活用されるBoot Variableと、OSが走り出したあとで活用されるRuntime Variableに別れますので、ここでは両方記載します。

表の出典:Debian unstableに搭載のmokutil/shim-unsignedパッケージのソースコード、及び、efivarの実行結果を用いて解析。

EFI Boot Variable EFI Runtime Variable 説明
BootOrder BootOrder ブートエントリの起動優先順位(16Bit リトルエンディアン)
Boot00000 Boot00000 ブートレコード0番目。EFIパーティション上のブートローダへのPATH(MS-DOS形式のPATH)とUEFI対応BOISのブートメニューでのブートエントリの見出しが入っている。
Boot00001 Boot00001 ブートレコード1番目。同上
…中略… …中略… ブートレコードn番目。同上
SecureBoot SecureBoot SecureBoot(SB)モードで起動しているか?を示す。=1 SecureBoot状態、=0 SecureBootではない。
MokList MokListRT shimx64.efi用。SecureBoot時のDebian用署名検証用の公開鍵が入っている。カスタムでMachine Owner Key(MOK)の追加に成功するとこちらに追記される。mokutil --list-enroll で中身をデコード可能。
MokListX MokListXRT shimx64.efi用。公開鍵のhash値が入っており、hashで指定された公開鍵は署名確認に使えないことを示すMOK blacklist (MOKX)の内容が入っている。mokutil --list-enroll -X で中身をデコード可能。
db db SecureBoot時にバイナリの署名を確認する用途の公開鍵が入っている。mokutil --db で中身をデコード可能。
dbx dbx キーのhash値が入っており、hashで指定された公開鍵は署名確認に使えないことを示すblacklistの内容が入っている。mokutil --dbx で中身をデコード可能。
PK PK SecureBoot時にバイナリの署名を確認する用途のPlatform Key(PK)と言われる公開鍵が入っている。mokutil --pk で中身をデコード可能。
KEK KEK SecureBoot時にバイナリの署名を確認する用途のKey Exchange Key(KEK)と言われる公開鍵が入っている。mokutil --kek で中身をデコード可能。
MokSBState MokSBStateRT shimx64.efi/ grubx86.efi/ kernel用。shimx64.efiにてSecureBoot(SB)がキャンセルされているので以降はSecureBootとして扱わないでほしいことを示す。=1 キャンセル済。変数が存在しないとSecureBoot変数の内容に従う。 mokutil --enable-validationmokutil --disable-validationmmx64.efi(Mok Manager)に変更を指示することで変更可能。
MokDBState MokIgnoreDB kernelが行う署名検証に上述のdbに格納されている公開鍵をあえて利用しないことを指示する。
SbatLevel_DEVEL SbatLevel_DEVEL shimx64.efiにてUEFI Secure Boot Advanced Targeting (SBAT)という署名検証時のセキュリティ対策用の変数。
MokPWStore mmx86.efiがメニューを出すまえにパスワードを尋ねるようになる。パスワード文字列はこちらの変数に記載されたhashと照合が行われる。変更はmokutils --passwdmokutils --clear-passwdで実施。--root-pw--hash-file--simple-hashを組み合わせると、linuxのrootのパスワードを利用したり、特定のhashファイルを利用したりできる。
MokSB MokSB mmx86.efi操作リクエスト用。SecureBootをキャンセルする動作に移ることを要求する。次回起動時に促されるパスワードが平文で入っている。
MokNew MokNew mmx86.efi操作リクエスト用。mokutil --list-newに対応。
MokPw MokPw mmx86.efi操作リクエスト用。mokutil --passwdmokutils --clear-passwdに対応。--root-pw--hash-file--simple-hashを組み合わせると、linuxのrootのパスワードを利用したり、特定のhashファイルを利用したりできる。
MokAuth MokAuth mmx86.efi操作リクエスト用。mokutil --import crt の際に設定するパスワードに関する付加情報を記録。mmx86.efiはcrtをimportする際にパスワードの入力を促し、こちらの付加情報とも照合する。
MokDel MokNew mmx86.efi操作リクエスト用。mokutil --list-deleteに対応。
MokDB MokDB mmx86.efi操作リクエスト用。mokutil --use-dbmokutils --ignore-dbに対応。この変数を変更することにより、mmx86.efiによりMokDBStateが変更される。
MokXNew MokXNew mmx86.efi操作リクエスト用。mokutil --list-new -X に対応。
MokXDel MokXDel mmx86.efi操作リクエスト用。mokutil --list-delete -X に対応。
MokXAuth MokXAuth mmx86.efi操作リクエスト用。mokutil --import-hash hash -Xmokutil --delete hash -X にて署名確認時に参照させたくないMOKのhashを設定する際のパスワードの付加情報。

また、表中のPK、KEK、MOKという用語については、SUSEのUEFIドキュメントがとてもわかりやすいのでおすすめしておきます。

なお、EFI Runtime Variablesの欄の変数ですが、EFI Boot Variablesをこの変数にコピーしてkernel側に見せるということをしていたりします。

kvmとUEFI対応BIOSとSecureBootでインストール練習をしてみる!

今回本記事は実際の機材を触る前に、なんとかしてUEFI対応BIOSとSecureBootの練習をしておくという記事なので、まずはUEFI対応BOISとSecureBootのモードでDebianを普通にインストールする事から始めてみます。

なお、環境としては、すでに何らかの方法で用意されたDebian(unstable)の上にkvmにてUEFI対応BIOSを載せて練習することにします。なお、今回Debianそのもののインストール途中のメニューの話はさすがにレガシーBOIS等でなれている人向けの話としたいので、割愛します。

$ lsb_release -dirc
Distributor ID: Debian
Description:    Debian GNU/Linux bookworm/sid
Release:        unstable
Codename:       sid
$ sudo apt install qemu-system-x86 qemu-utils ovmf 
$ wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-11.3.0-amd64-netinst.iso
$ qemu-img create -f qcow2 debian-sid-secureboot-uefi.qcow2 10G
$ cp /usr/share/OVMF/OVMF_VARS_4M.ms.fd ovmf_vars_4m.ms.debian-sid.fd
$ kvm -cpu host -machine q35,smm=on -global driver=cfi.pflash01,property=secure,value=on \
-rtc base=utc -smp 2 -m 2G -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.ms.fd \
-drive if=pflash,format=raw,file=$(pwd)/ovmf_vars_4m.ms.debian-sid.fd \
-drive file=$(pwd)/debian-sid-secureboot-uefi.qcow2,if=virtio,index=1,media=disk,format=qcow2 \
-device intel-hda -vga virtio -display gtk -k ja -cdrom $(pwd)/debian-11.3.0-amd64-netinst.iso

kvmを利用するにあたり、qemu-system-x86qemu-utilsを導入しておきます。UEFI対応BOISのイメージも必要となりますので、ovmfパッケージでBIOSイメージを/usr/share/OVMF/以下に導入しておきます。

qemu-imgでインストール先の仮想ディスクを作ってます。/usr/share/OVMF/OVMF_VARS_4M.ms.fdがEFI VariableのNVRAMの雛形となりますので、ローカルにコピーして保存しておきます。あとでBOIS側から設定をいじったり、ブートエントリ(EFI Boot Variable等)を、自由に書き込んで保存しておく必要があるため、ここでは1つの仮想ディスクに対して1個コピーを取っておきます。

最後のkvm -cpu ...という長いコマンドラインが直接kvmによる仮想環境の起動となります。今回はネットワークはkvm側がデフォルトで用意するネットワークを利用する前提で立ち上げています。また、コマンドラインにある-vga virtioはUEFI対応BIOSであるOVMFを使うときは必須となります。これがないと正常な描画ができません。また仮想環境のディスプレイはgtkで直接デスクトップ環境へ描画します。なお、OpenGLのアクセラレーションを期待して-display gtkgl=onを付与すると、インストール中マウスカーソルについて正常なジオメトリ計算ができないようなので今回はOFFしています。

最後のkvmのコマンドが実行されると、Titan Coreのロゴが一瞬見えて、そのあとすぐに見慣れない画面になります。

EFI Shellの画面

kvmで起動するとqemuのウィンドウが出てきます。qemuのウィンドウに表示されているコンソール画面が小さいと思った人は、qemuのウィンドウで、CTRL+ALT+ +(CTRLとALTと+キーを押す)を何回か押すか、メニューからView→Zoom Inをマウスで選択下さい。qemuのウィンドウでマウスカーソルが消えてメニューが選択できない場合は、ALT+v(ALTを押しながらvキー)を押すとメニューが開きます。そのまま、カーソルキーで選択してReturnキーを押して下さい。また、画面をクリックすると、Allow Inhibit Shortcutというポップアップが出ますが、許可を押していただいて問題ありません。マウスが消えてウィンドウフォーカスがqemuのウィンドウから外れないかもしれませんが、タイトルバーにPress Ctrl+Alt+Gと出ているとおり、CTRLとALTとGキーを同時押しするとマウスが出てきてウィンドウフォーカスが外れるようになります。

この画面ですが、EFI Shellというプログラムの画面となります。こちらのコマンドを覚えると、EFI VariableのBoot0000などのブートエントリをEFI Shellから直接操作できます。ただ、あとで立ち上がるメニュー形式の方が断然使いやすいので、ここではEFI Shellをスキップすることにします。Shell>と出ているところでEXITと打ち込んで下さい。

すると、UEFI対応BIOSのメニュー画面になります。qemuのウィンドウのサイズが戻ってしまった人は、先程の画面サイズ調整(CTRL+ALT+ +ALT+vでメニュー開いてカーソルキーでZoom Inを選択等)を実施ください。

UEFI対応BOISのトップ画面

カーソルキーで文字列のハイライトが動きますので、まずはDevice Managerを選択します。ハイライトを合わせてRetrunキーを押して下さい。

Device Managerメニュー

するとDevice Managerメニューが現れ、Secure Boot Configurationというのが現れますので、こちらにハイライトを合わせてReturnキーを押下下さい。すると今度はSecure Boot Configurationメニューになります。

Secure Boot Configurationメニュー

画像の値の通りになっていることを確認下さい。異なっていたら、ハイライトを合わせてRetrunキーで選択することで変更できます。戻りはEscキーを押すと戻れます。変更した場合はF10キーを押してからESCキーを押下下さい。Device Managerメニューに戻ってきますので、更にESCキーを押して最初のメニュー画面に戻ります。

最初のメニューに戻ってきたら、Boot Managerを選択してReturnキーを押下します。

Boot Managerメニュー

ブート可能なブートエントリ一覧が出てきましたので、UEFI QEMU DVD-ROM QM00005にハイライトを合わせてReturnキーを押すと、やっとisoイメージが実行され、いつものDebianインストーラーのメニューが出てきます。

Debianインストーラーのメニュー

今回UEFI対応BIOSからのブートですので、画面の上あたりにUEFI Installer menuと出ています。Graphical install等選んでインストールをしていきます。

国として日本を選んで、ホスト名、ドメイン、rootユーザ情報、一般ユーザ情報を入力する以外は、ソフトウェアの選択メニューまでひたすら続けるを押していくだけでインストールが完了します。このあたりは、通常のDebianインストール作業になりますので、ここでは割愛します。

ソフトウェアの選択メニューになったら、一番基本の

  • SSHサーバと、
  • 標準システムユーティリティ

のみ選択下さい。

以降手放しで最後までインストールが完了します。あとは、リブート画面になり、Debianのログイン画面が出てきます。

早速インストールで入力した一般ユーザのアカウントでログインして、無事SecureBootの状況かを確認します。

Debian GNU/Linux 11 debian-uefi tty1

debian-uefi login: yours
Passwrod: <一般ユーザのパスワード>
Linux debian-uefi 5.10.0-13-amd64 #1 SMP Debian 5.10.106-1 (2022-03-17) x86_64

...中略...
$ mokutil --sb-state
SecureBoot enabled

無事SecureBootモードで起動しました。

今度は手組でEFI BootセットアップしてSecureBootしてみる!

さて本ブログを読んでいるような方々は、先ほどのぬるいゲームのようなインストールでは物足りないと思います。UEFIとSecureBootの経験値を溜めるため早速レベル上げしていきましょう!

今度は手組したディスクを使ってUEFIとSecureBootを手で操作してブートするまでを練習してみます。もちろん、ご存知の方はUEFI BootでもDebianインストールディスクのレスキューモードで立ち上げてgrub2の再インストールメニューを選べば、すぐにブートするようになることをご存知だと思います。しかしながら、人生では予想もしないタイミングで、もっと変わった構成のディスクを相手にOS復活させてからフォルダにある黒歴史の始末をすぐにしなければならないときが来ます!きっとこれで皆様も動機は十分だと思いますので、早速練習してみましょう。

本章のお題としては、空のディスクを用意して、先程インストールしたDebianの/以下のみ全部コピーし、手組でUEFIブートの設定を行って起動させるということをしてみます。

まずは、空のディスクを用意して、準備を進めます。EFI領域以外は全部適当なサイズで用意してみます。

なお、(Host)と書いてある部分はqemuを動かすHost OS、(Guest)とあるのはqemuのウィンドウで操作していることを意味します。

(Host) $ qemu-img create -f qcow2 debian-sid-secureboot-uefi-cpied.qcow2 10G
(Host) $ sudo modprobe nbd
(Host) $ sudo qemu-nbd --connect=/dev/nbd0 (pwd)/debian-efi-copied.qcow2
(Host) $ sudo apt install gdisk
(Host) $ sudo gdisk /dev/nbd0
GPT fdisk (gdisk) version 1.0.8

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries in memory.

Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-20971486, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-20971486, default = 20971486) or {+-}size{KMGTP}: +512M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): EF00 
Changed type of partition to 'EFI system partition'

Command (? for help): n 
Partition number (2-128, default 2): 
First sector (34-20971486, default = 1050624) or {+-}size{KMGTP}: 
Last sector (1050624-20971486, default = 20971486) or {+-}size{KMGTP}: +8G
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 
Changed type of partition to 'Linux filesystem'

Command (? for help): n
Partition number (3-128, default 3): 
First sector (34-20971486, default = 17827840) or {+-}size{KMGTP}: 
Last sector (17827840-20971486, default = 20971486) or {+-}size{KMGTP}: 
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8200
Changed type of partition to 'Linux swap'

Command (? for help): p
Disk /dev/nbd0: 20971520 sectors, 10.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 72270459-AC12-4194-90CB-E0DCED3C8180
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 20971486
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI system partition
   2         1050624        17827839   8.0 GiB     8300  Linux filesystem
   3        17827840        20971486   1.5 GiB     8200  Linux swap

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): Y
OK; writing new GUID partition table (GPT) to /dev/nbd0.
The operation has completed successfully.

qemu-imgで空のディスクを10GB作り、qemu-nbdでqcow2形式のディスクイメージをkernelの/dev/nbd0として認識させたあと、gdiskでGPTパーティションを作成します。EFI Partitionはディスク先頭からの512MBを割当てるのが流儀となりますのでそうします。さらにEFI領域(0xEF00)用のパーティションであることを指定します。次に8GBをLinuxの/領域として確保し、Linux領域(0x8300)とし、残り全部をswap領域(0x8200)として指定しています。

各パーティションを早速フォーマットします。EFI領域はvfat形式で、/領域はext4形式、swapはswap形式のフォーマットを行います。

(Host) $ sudo mkfs.vfat /dev/nbd0p1
(Host) $ sudo mkfs.ext4 /dev/nbd0p2
(Host) $ sudo mkswap /dev/nbd0p3

さて、先の章で作ったブート可能なdebian-sid-secureboot-uefi.qcow2をもとに/領域を今回のディスクにコピーします。EFI領域は設定練習ということであとで手組みするのでわざと何もしません。/領域のコピーについてはcp -aTコマンドが便利で、数々のアトリビュートもコピーしてくれるのでこういう時に便利ですね!

(Host) $ mkdir src dest
(Host) $ sudo qemu-nbd --connect=/dev/nbd1 $(pwd)/debian-sid-secureboot-uefi.qcow2
(Host) $ sudo mount -t ext4 /dev/nbd1p2 src/
(Host) $ sudo mount -t ext4 /dev/nbd0p2 dest/
(Host) $ sudo cp -aT src dest

EFI Variableの値の設定も手組にしますので、先程の章の通り、新規のNVRAMイメージを手元にコピーしておきます。終わりましたら最後に仮想ディスクを外し、Debianインストーラーのisoイメージを起動し、後でレスキューモードを利用します。

(Host) $ cp /usr/share/OVMF/OVMF_VARS_4M.ms.fd ovmf_vars_4m.ms.debian-copied.fd
(Host) $ sudo umount src/
(Host) $ sudo umount dest/
(Host) $ sudo qemu-nbd --disconnect /dev/nbd0 
(Host) $ sudo qemu-nbd --disconnect /dev/nbd1
(Host) $ kvm -cpu host -machine q35,smm=on -global driver=cfi.pflash01,property=secure,value=on \
-rtc base=utc -smp 2 -m 2G -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.ms.fd \
-drive if=pflash,format=raw,file=$(pwd)/ovmf_vars_4m.ms.debian-copied.fd \
-drive file=$(pwd)/debian-sid-secureboot-uefi-cpied.qcow2,if=virtio,index=1,media=disk,format=qcow2 \
-device intel-hda -vga virtio -display gtk -k ja -cdrom $(pwd)/debian-11.3.0-amd64-netinst.iso

最初にEFI Shellが立ち上がってきて〜Debianインストーラーメニュー表示までは先の章のとおりの操作となります。Debianインストーラーメニューが出てきたら、今度はAdvanced Optionsを選択してReturnキーを押下します。

Advanced Optionsメニュー

次に... Rescue modeを選択してReturnキーを押下します。

すると、Debianインストール時の言語選択等のメニューがTUIで出てきますので、いつもどおりに選択(日本を選択)し続けます。今回ホスト名とかドメインなども作業には影響しないので適当に設定します。しばらく自動でメニューが進み、レスキューモードに移行の画面になります。

レスキューモードメニューでルートパーティション設定

ここで、先程cp -aTした/領域はkvmからは/dev/vda上のディスクのパーティション2になるので、/dev/vda2を選択してReturnキーを押下します。すると/boot/efiパーティションをマウントしますか?と来ますので、<はい>を答えます。

レスキューモードメニューで/boot/efiパーティション設定

するとレスキュー操作メニューになりますので、インストーラー環境内でシェルを実行を選びます。GRUB ブートローダの再インストールという、今回のお題をまるごとチートできそうなメニューが出てきますが、今回は趣旨と異なるので無視します。

インストーラ環境内でシェルを実行を選択中

その後に親切にも/target/dev/vda2をマウントしましたという画面が出てきますが、続けるを選択すると、レスキューモードでshellが立ち上がります。

早速手組していきましょう!まずは、EFI領域をセットアップします。どういうわけか、先ほどのメニューで/boot/efiパーティションをマウントしますか?のメニューで<はい>を答えたにもかかわらずEFI領域がマウントされていないので、EFI領域(/dev/vda1)をマウントします。

さらに、UEFI対応BOISが起動できるようにするためには、/EFIというディレクトリから始めるのが習わしのようなので、/EFIも含め必要なディレクトリを作っていきます。また、必要なefi用イメージ(shimx64.efimmx64.efigrubx64.efi)をコピーしていきます。なお、今回SecureBoot有効に対応しなければならないので、コピーするefi用イメージは全部Debian側で署名されているものが必要です。そのため、末尾に.signedとついているファイルをコピー下さい。

(Guest)# mount /dev/vda1 /target/boot/efi
(Guest)# cd /target/boot/efi
(Guest)# mkdir EFI
(Guest)# cd EFI
(Guest)# mkdir debian
(Guest)# cd debian
(Guest)# cp /target/usr/lib/shim/shimx64.efi.signed shimx64.efi
(Guest)# cp /target/usr/lib/shim/mmx64.efi.signed mmx64.efi
(Guest)# cp /target/usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed grubx64.efi

では、EFI Variableにレスキューモードのkernelから読み書きできるようにefivarfsをロードしておきます。 その後、chroot /target /bin/bashして、/領域にある各種バイナリ(/bin/usr/bin/sbin/usr/sbin等)を利用できるようにしてしまいます。

(Guest)# cd /
(Guest)# modprobe efivarfs
(Guest)# chroot /target /bin/bash
(Guest) root@debian:/# <-プロンプトが変わる。

早速ブートに必要なファイルを作成、修正していきます。設定ファイルをわかりやすいようにcatで書いてますが、今ならviも普通に動きますので、いつもどおりファイルを修正、新規作成いただければ問題ありません。

(Guest) root@debian:/# cd /boot/efi/EFI/debian
(Guest) root@debian:/boot/efi/EFI/debian# blkid /dev/vda2
/dev/vda2: UUID="2e12dc00-e59c-4d17-95ca-cf5336a4fee8" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="Liux filesystem" PARTUUID="85f775b4-4761-4b48-af6d-443c3b5a2eb7"
(Guest) root@debian:/boot/efi/EFI/debian# cat > grub.cfg
search.fs_uuid 2e12dc00-e59c-4d17-95ca-cf5336a4fee8 root hd0,gpt2
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg
<CTRL+Dを押す>

/boot/efi/EFI/debian/grub.cfgにて、grub2に、/領域(/dev/vda2)のUUIDでディスクをブート時に見つけさせ、そこをdisk 0(hd0)として、gptパーティションの2番目に/領域があることを教えています。こちらの情報をもとに/領域の/boot/grubディレクトリ以下のgrub.cfgを正式なlinuxブート用の設定としてほしいという内容となります。

ちなみにですが、grub2はこれ単体でHostが物理サーバなら、HostのUSBに接続したDISKも扱う能力があります。つまり、grub.cfgに指定するUUIDは、例えば、USB-Cにつないだ外部ディスクにある/領域を起動したい場合は、同じくblkidで外部ディスク上の/領域のUUIDを調べて(例: blkid /dev/sda2等)、grub.cfgに調べたUUIDを指定すると、grub2がUSBの先につながっている外付けディスクも自動で調べて起動させようとします。grub2の高機能っぷりには毎度助けられますね!

次にkernel側のディスク定義にコピー元のUUID情報が入っているので、修正します。blkidコマンドで各パーティションのUUIDを求めておき、必要なファイルのUUID情報を変更します。

(Guest) root@debian:/boot/efi/EFI/debian# cd /etc
(Guest) root@debian:/boot/efi/EFI/debian# blkid /dev/vda1
/dev/vda1: UUID="F746-FFE0" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="EFI system partition" PARTUUID="ea4e2394-eae2-4673-8028-c022840d0b6f"
(Guest) root@debian:/boot/efi/EFI/debian# blkid /dev/vda3
/dev/vda3: UUID="13475304-b7df-46d9-bd99-86b13eb5157b" TYPE="swap" PARTLABEL="Linux swap" PARTUUID="b8398490-1291-47d6-83ed-e98f3da0495e"
(Guest) root@debian:/etc# vi fstab #↓ UUID=の部分を上述のblkidで確認したものに各々修正。以下は修正済のUUIDで表示しています。
..中略..
# / was on /dev/vda2 during installation
UUID=2e12dc00-e59c-4d17-95ca-cf5336a4fee8 /      ext4 errors=remount-ro 0  1
# /boot/efi was on /dev/vda1 during installation
UUID=F746-FFE0   /boot/efi      vfat   umask=0077   0    1
# swap was on /dev/vda3 during installation
UUID=13475304-b7df-46d9-bd99-86b13eb5157b  none   swap sw   0   0
/dev/sr0        /media/cdrom0       udf,iso9660 user,noauto 0   0
(Guest) root@debian:/etc# vi initramfs-tools/conf.d/resume #↓UUID=をswap(/dev/vda3)のUUIDに変更する。以下は修正済のUUIDで表示しています。
RESUME=UUID=13475304-b7df-46d9-bd99-86b13eb5157b

次にkernelからEFI Variableを見えるようにして、ツールを起動します。

(Guest) root@debian:/etc# mount -t efivarfs efivarfs /sys/kernel/firmware/efivars
(Guest) root@debian:/etc# ls /boot/initrd*
/boot/initrd.img-5.10.0-10-amd64 /boot/initrd.img-5.10.0-13-amd64
(Guest) root@debian:/etc# update-initramfs -k 5.10.0-10-amd64 -u 
(Guest) root@debian:/etc# update-initramfs -k 5.10.0-13-amd64 -u 
(Guest) root@debian:/etc# update-grub2

/boot/initrdイメージにswapのUUIDが埋め込まれるため、initrdをアップデートしています。 さらに、/boot/grub/grub.cfgにも、/領域のUUIDが多数打ち込まれているので、update-grub2でまとめて更新しています。

最後にブートエントリと、ブート順番を、EFI Variableにefibootmgrコマンドで書き込みます。

(Guest) root@debian:/etc# efibootmgr -c -d /dev/vda -p 1 -L "debian" -l '\EFI\debian\shimx64.efi'

efibootmgrはboot loader(今回はshimx64.efi)が存在するディスクと、パーティション番号を指定する必要があります。今回の場合/dev/vdaのパーティション番号1となります。efibootmgr -cはデフォルトでEFI VariableのBoot番号の空きを調べてBoot変数を作り、それを起動順番の1番目にします。何らかの理由で1番目にしたくない場合は、efibootmgrだけ実行すると、現状のBoot変数の中身と、BootOrder変数の中身を表示してくれますので、efibootmgr -o 2,1,3,4,5,7,7,8,9,0のように起動したい順番に、BootN(Nは数字)のNをカンマで区切って指定下さい。

例:QEMU DVD(Boot0001)を最初に起動するようにしたい。
(Guest) root@debian:/etc# efibootmgr
BootCurrent: 0001
Timeout: 0 seconds
BootOrder: 0009,0000,0002,0001,0003,0004,0005,0006,0007,0008
Boot0000* UiApp
Boot0001* UEFI QEMU DVD-ROM QM00005
Boot0002* EFI Internal Shell
Boot0003* UEFI Misc Device
Boot0004* UEFI PXEv4 (MAC:525400123456) 
Boot0005* UEFI PXEv4 (MAC:525400123456) 2
Boot0006* UEFI PXEv6 (MAC:525400123456) 
Boot0007* UEFI HTTPv4 (MAC:525400123456) 
Boot0008* UEFI HTTPv6 (MAC:525400123456) 
Boot0009* debian 
(Guest) root@debian:/etc# efibootmgr -o 1,9,0,2,3,4,5,6,7,8

EFI Variableも設定できたので、いよいよリブートします。

(Guest) root@debian:/etc# exit
(Guest)# exit

レスキューモードのメニューが現れますので、システムの再起動を選択してReturnキーを押下します。すると、無事grub2のメニューが現れ、しばらくするとLinuxがブートし、無事ログインプロンプトが現れます。

Debian GNU/Linux 11 debian-uefi tty1

debian-uefi login: yours
Password: <ログインパスワード>

$ mokutil --sb-state
SecureBoot enabled

無事SecureBootモードで手組したEFI設定で起動できたことがわかります。意外と簡単だったかと思います。

おわりに

EFIブートを手組してSecureBootが簡単にセットアップできることを体験できたと思います。kvmがあればUEFI対応BIOSに関していろいろなことを簡単に試せます。これで修練を積めば、経験値も溜まり、故障などでブートしないディスクをブートさせたりができるようになることでしょう!

この記事を書いた人

記事一覧
SQUARE ENIXでは一緒に働く仲間を募集しています!
興味をお持ちいただけたら、ぜひ採用情報ページもご覧下さい!