UEFIを使ったOS起動の流れの概要
セキュリティミニキャンプ山梨2019に参加することになり、事前課題としてUEFIアプリを書いているのですが、UEFIつかってどういうように起動しとるんだと思ったので自分でまとめてみることに。
ハードウェアの初期化
電源が入ると、CPUにリセットベクタと呼ばれるジャンプ先をセットし、そこから処理が始まります。 リセットベクタにはBIOSやUEFIのROMがマッピングされており、つまりははじめにこれらのソフトウェアが実行されることになります。
BIOSやUEFIはPOST(Power on self test)と呼ばれるハードウェアの動作確認を行い、初期化します。
ブートされるパーティション
ブートマネージャーはブートする対象をどうにかして探す必要があるのですが、きちんと定義してやらないと何をブートすればいいのかわかりません。UEFIでは、ディスク上の特定の領域の中身をブート対象とする、と定義することでこの問題を解決しています。
この領域のことをESP(EFI System Pertition)と言い、GPT(GUID Pertition Table)でのコードが割り振られています。
$ parted /dev/sda print モデル: ATA SanDisk SD9TB8W2 (scsi) ディスク /dev/sda: 256GB セクタサイズ (論理/物理): 512B/512B パーティションテーブル: gpt ディスクフラグ: 番号 開始 終了 サイズ ファイルシステム 名前 フラグ 1 1049kB 274MB 273MB fat32 EFI system partition boot, hidden, esp 2 274MB 290MB 16.8MB Microsoft reserved partition msftres 3 290MB 128GB 127GB Basic data partition msftdata 4 128GB 255GB 127GB ext4 5 255GB 256GB 1049MB ntfs Basic data partition hidden, diag
/dev/sdaというディスクがパーティションテーブルgptであることがわかります。また、番号1の領域がESPだということもわかります。
BIOSでのブート領域について
上の説明だと何やら当たり前のように聞こえるかもしれませんが、BIOSとの違いを見ると結構革新的だということがわかるでしょう。
BIOSではGPTでなく、MBR(Master Boot Record)でのパーティションテーブルを使用していますが、ブート可能なコードはMBRの先頭数百バイトに書かれることになっています。この領域に後続するパーティションの内、ブートフラグの立っているパーティションをロードするコードが書かれます。
昔はこれで良かったのですが、ローダーが高機能になってくると、この領域に収まらなくなってきます。ではどうするのかというと、ミサイルのエンジンのように多段構成にするのです。この狭い領域にローダーのローダーを書き、そのローダーはMBRとはじめのパーティションの中間に置くことにしたのです。
あとは中間においたローダーでブートしていけば良いのですが、ここでひとつ問題が生じます。 この中間領域は隙間なので不定であり、できればあまり使いたくないのですが、とりあえずは動作したのでよく使われた手法だったようです。
このような背景からUEFIは結構革新的だと思います。
ブートエントリの確認
UEFIではブート可能な領域であるESPの中身をブートしていくのですが、この領域には複数のブートローダが存在し得ます。例えば私の学科で配られたPCはwindowsとubuntuのデュアルブートですので少なくとも2つはあるわけです。 UEFIブートマネージャーはこのエントリを見てブート対象を同定しているのだと思います。(詳しく調べてないのでこんな言い方になってます。)
Linuxにおいては、コマンドでブートエントリを確認することができます。
$ efibootmgr -v BootCurrent: 0001 Timeout: 2 seconds BootOrder: 0001,0000,0018,0019,001A,001B,001C,001D,001E,001F Boot0000* Windows Boot Manager HD(1,GPT,b829fd28-32f6-41fc-a0fe-a0f4df651785,0x800,0x82000)/File(\EFI\Microsoft\Boot\bootmgfw.efi)WINDOWS.........x...B.C.D.O.B.J.E.C.T.=.{.9.d.e.a.8.6.2.c.-.5.c.d.d.-.4.e.7.0.-.a.c.c.1.-.f.3.2.b.3.4.4.d.4.7.9.5.}...0................ Boot0001* ubuntu HD(1,GPT,b829fd28-32f6-41fc-a0fe-a0f4df651785,0x800,0x82000)/File(\EFI\ubuntu\shimx64.efi) ....
BootCurrentは現在ブートされているエントリ番号であり、実際にエントリ番号0001はubuntuとなっています。また、BootOrderには指定しなかった場合の優先順位が書かれており、このパソコンで電源を入れて何もしないとubuntuがブートされることからも確認されます。
ちなみにgrubがブートローダとして入ってるらしいですが、何故か見当たりません。もしかしたらubuntuのローダーが読み込まれたあとにgrubが起動しているのかも...
その後
選ばれたエントリにはOS固有のブート手順があるので詳しくは書きません。 ただ、linuxについては今後調べていきたいと思います。