Blog posts by @retrage

mirror of https://retrage.github.io/

BitVisorのEFI向けVMM Loader(1st stage)のコードを読んでみる

この記事は 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のコードも読んでみたい.

参考文献