この記事は BitVisor Advent Calendar 7日目の記事として書かれた. ここでは,BitVisorのEFI向けVMM Loader(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のコードも読んでみたい.