Kotaro7750's diary

低レイヤを中心とした技術ブログ、たまに日記

UEFIを使ったOS起動の流れの概要

セキュリティミニキャンプ山梨2019に参加することになり、事前課題としてUEFIアプリを書いているのですが、UEFIつかってどういうように起動しとるんだと思ったので自分でまとめてみることに。

ハードウェアの初期化

電源が入ると、CPUにリセットベクタと呼ばれるジャンプ先をセットし、そこから処理が始まります。 リセットベクタにはBIOSUEFIのROMがマッピングされており、つまりははじめにこれらのソフトウェアが実行されることになります。

BIOSUEFIはPOST(Power on self test)と呼ばれるハードウェアの動作確認を行い、初期化します。

ブートされるパーティション

ブートマネージャーはブートする対象をどうにかして探す必要があるのですが、きちんと定義してやらないと何をブートすればいいのかわかりません。UEFIでは、ディスク上の特定の領域の中身をブート対象とする、と定義することでこの問題を解決しています。

この領域のことをESP(EFI System Pertition)と言い、GPT(GUID Pertition Table)でのコードが割り振られています。

linuxにおけるパーティションはコマンドで確認できます。

$ 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はwindowsubuntuデュアルブートですので少なくとも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については今後調べていきたいと思います。