BitVisorのEFI向け VMM Loader(1st stage)のコードを読んでみる
この記事は BitVisor Advent Calendar 7日目の記事として書かれた. ここでは,BitVisorのEFI向け VMMLoader(1st stage)のコードを読んでみる.
EFI Loaderのコードは
boot/uefi-loader/loadvmm.c
にある.
efi_main
をみていく.
status = systab->BootServices->
HandleProtocol (image, &LoadedImageProtocol, &tmp);
LoadedImageProtocolがサポートされているかを確認.
status = systab->BootServices->
HandleProtocol (loaded_image->DeviceHandle,
&FileSystemProtocol, &tmp);
FileSystemProtocolがサポートされているかを確認.
create_file_path (loaded_image->FilePath, L"bitvisor.elf", file_path,
sizeof file_path / sizeof file_path[0]);
ここで,bitvisor.elf
までのパスを作成する.
status = fileio->OpenVolume (fileio, &file);
ボリュームを開く.
status = file->Open (file, &file2, file_path, EFI_FILE_MODE_READ, 0);
先で作成したファイルパスのファイルを開く.
status = systab->BootServices->AllocatePages (AllocateMaxAddress,
EfiLoaderData, 0x10,
&paddr);
4KiB*0x10=64KiBだけページをAllocateする.
readsize = 0x10000;
status = file2->Read (file2, &readsize, (void *)paddr);
bitvisor.elf
のうち0x10000だけpaddrに展開する.
このあたりのマジックナンバーについては榮樂さんによるスライド
に詳しく書いてある.
それによれば,
BitVisorのVMMローダ(2nd stage)ではELFバイナリの先頭64KiB
(20ページ目) とある.この部分はその先頭64KiBを読み出していると考えられる.
entry = *(uint32_t *)(paddr + 0x18);
entry_func = (entry_func_t *)(paddr + (entry & 0xFFFF));
ELFの先頭アドレスからエントリポイントを計算し,entry_func
とする.
struct bitvisor_boot boot_ext = {
UEFI_BITVISOR_BOOT_UUID,
paddr,
readsize,
file2
};
void *boot_exts[] = {
&boot_ext,
NULL
};
boot_error = entry_func (image, systab, boot_exts);
必要な情報を構造体に抱えてentry_func
に飛び込む.
以上を簡単にまとめると, ファイルの読み出し->エントリポイントの計算->エントリポイントへのジャンプ のようになっている. ELFのパースを行わないことで,簡素な実装となっていることが見て取れる. また,
最後に
EFIアプリケーションの開発と言えばEDK2やgnu-efiがあるが,
BitVisorのEFI Loaderの場合,EDKからのヘッダを利用し,
x86_64-w64-mingw32-gcc
によりコンパイルを行う.
このため依存関係が少なくコンパイルが容易であると言える.
BitVisorのuefi-loader
に加え,品川先生のuefi-simpleに刺激を受け,
uefi-sample
というサンプルを作成した.
これにより,EDK2やgnu-efiに頼ることなくEFIアプリケーションを作成することができる.
ここでは,BitVisor のVMMローダ(1st stage)のコードを読んだ. 機会があれば,2nd stageのコードも読んでみたい.