LoginSignup
4
1

More than 5 years have passed since last update.

UEFI shellの引数をBitVisor側へ渡してみようの巻

Posted at

過去にMultiboot specificationに基づいてブートする際にBitVisorへオプションを渡せるようにしたという話がありました.

起動時にオプションが渡せると何かと便利なことがあります.
今回はUEFI shellからBitVisorを起動する際にオプション(と言う名の文字列)を渡せるようにしてみたいと思います.

おさらい: UEFI shellからの起動

本題の前に,USBメモリブートしてUEFI shellからBitVisorを起動する方法を簡単に書きます.

まずUSBメモリをFAT32でフォーマットしたのち, /BOOT/EFI ディレクトリを作成します.
その中にbitvisor.elfloadvmm.elf (BitVisorのソースのboot/uefi-loaderの中にあります)を置きます.
また,UEFI Shell (自分の場合はこのShell.efi/BOOT/EFI/BOOTX64.efiとリネームして置きます.

あとはUSBブートをするとuefi shellが自動で立ち上がるはずです.UEFI shell上でfs0:\BOOT\EFI\loadvmm.elfみたいにすればBitVisorが起動できます.
(ちなみに,loadvmm.elfBOOTX64.efiとリネームして置けばUSBブートした際自動でBitVisorが起動します.)

今回はここで起動する際に,fs0:\BOOT\EFI\loadvmm.elf <option>みたいな感じでBitVisor側へオプションを渡すことを考えます.

UEFI shellのパラメータの取得方法

BitVisorのuefi loaderはboot/uefi-loader/loadvmm.cにあります.
efi_mainがUEFIアプリケーションのエントリポイントです.

LOADED IMAGE PROTOCOLを利用すると,ロードしたイメージの情報が取得できます.
このとき,UEFI shellからアプリケーションを起動した場合,LOAD_OPTIONSに引数の情報が入っているようです.
BitVisorのローダーの中でにすでにLOADED IMAGE PROTOCOLは使われているので,loaded_image->LoadOptionsで引数にアクセスできます.

ちなみに,UEFI shell specificationの方でも何かオプションに関する項目があるみたいですが,試してません.

BitVisor側へのパラメータ送信方法

loadvmm.efiからBitVisor側のエントリポイントに飛んだ際,どこに行くかというと,core/entry.sのentry及びufi64_enryを経由してcore/uefi.cのuefi_init()にいきます.
このときページテーブルを切り替えるので,BitVisor側からUEFIローダー側のメモリ領域を単純に参照することはできません.そこでちょっと工夫がいるわけですが,実のところBitVisorからUEFI関数を呼び出すことがあるので,既にページテーブル切り替えやUEFI側のメモリ領域を参照する機能が存在します.
また昨年追加されたuefi-loader-loginにおいて,ローダー側からstruct bitvisor_bootというデータ構造をBitVisor側に渡して,BitVisor側はそれを参照する仕組みが実現されているので,これを利用すれば簡単にオプションが渡せます.

要するに

diff -r dd0a7173203a boot/uefi-loader/loadvmm.c
--- a/boot/uefi-loader/loadvmm.c    Sat Dec 01 10:28:52 2018 +0000
+++ b/boot/uefi-loader/loadvmm.c    Sun Dec 02 08:32:19 2018 +0000
@@ -39,6 +39,7 @@

 static EFI_GUID LoadedImageProtocol = EFI_LOADED_IMAGE_PROTOCOL_GUID;
 static EFI_GUID FileSystemProtocol = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+CHAR8 option[256];

 static void
 printhex (EFI_SYSTEM_TABLE *systab, uint64_t val, int width)
@@ -119,6 +120,9 @@
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fileio;
    EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
    EFI_PHYSICAL_ADDRESS paddr = 0x40000000;
+   UINT32 optionsize;
+   CHAR16* loadoption;
+   int i, j;

    status = systab->BootServices->
        HandleProtocol (image, &LoadedImageProtocol, &tmp);
@@ -163,11 +167,31 @@
    entry = *(uint32_t *)(paddr + 0x18);
    entry_func = (entry_func_t *)(paddr + (entry & 0xFFFF));

+   optionsize = loaded_image->LoadOptionsSize;
+   loadoption = (CHAR16*)loaded_image->LoadOptions;
+   // CHAR16 => CHAR8
+   optionsize /= 2;
+   if(optionsize > 255){
+       optionsize = 255;
+   }
+   // skip program name
+   for(i = 0; i < optionsize && (CHAR8)(*loadoption) != ' ' ; i++, loadoption++);
+   if(i < optionsize && (CHAR8)(*loadoption) == ' '){
+       i++, loadoption++;
+   }
+   for(j = 0; i < optionsize; i++, j++){
+       option[j] = (CHAR8)(*loadoption++);
+   }
+   option[j] = '\0';
+
+
    struct bitvisor_boot boot_ext = {
        UEFI_BITVISOR_BOOT_UUID,
        paddr,
        readsize,
-       file2
+       file2,
+       &option[0],
+       j-1
    };
    void *boot_exts[] = {
        &boot_ext,
diff -r dd0a7173203a core/uefi.c
--- a/core/uefi.c   Sat Dec 01 10:28:52 2018 +0000
+++ b/core/uefi.c   Sun Dec 02 08:32:19 2018 +0000
@@ -77,6 +77,8 @@
 ulong SECTION_ENTRY_DATA uefi_protocols_per_handle;
 ulong SECTION_ENTRY_DATA uefi_uninstall_protocol_interface;
 ulong SECTION_ENTRY_DATA uefi_create_event;
+char SECTION_ENTRY_DATA uefi_boot_option[256];
+ulong SECTION_ENTRY_DATA uefi_boot_option_size;
 bool uefi_booted;

 static void SECTION_ENTRY_TEXT
@@ -261,6 +263,10 @@
    uefi_entry_pcpy (uefi_entry_virttophys (&uefi_read),
             &file->Read, sizeof uefi_read);

+   uefi_entry_pcpy (uefi_entry_virttophys (&uefi_boot_option[0]),
+               bitvisor_opt.option, sizeof uefi_boot_option);
+   uefi_boot_option_size = bitvisor_opt.optionsize;
+
    uefi_init_get_vmmsize (&vmmsize, &align);
    vmmsize = (vmmsize + PAGESIZE - 1) & ~PAGESIZE_MASK;
    alloc_addr64 = 0xFFFFFFFF;
diff -r dd0a7173203a core/uefi.h
--- a/core/uefi.h   Sat Dec 01 10:28:52 2018 +0000
+++ b/core/uefi.h   Sun Dec 02 08:32:19 2018 +0000
@@ -52,6 +52,8 @@
 extern ulong uefi_protocols_per_handle;
 extern ulong uefi_uninstall_protocol_interface;
 extern ulong uefi_create_event;
+extern char uefi_boot_option[256];
+extern ulong uefi_boot_option_size;
 extern bool uefi_booted;

 #endif
diff -r dd0a7173203a include/share/uefi_boot.h
--- a/include/share/uefi_boot.h Sat Dec 01 10:28:52 2018 +0000
+++ b/include/share/uefi_boot.h Sun Dec 02 08:32:19 2018 +0000
@@ -61,6 +61,8 @@
    unsigned long long int loadaddr;
    unsigned long long int loadsize;
    void *file;
+   void *option;
+   unsigned int optionsize;
 };

 #define INITIAL_BOOT_EXT {{UEFI_BITVISOR_BOOT_UUID}, 0, 0, 0}

ローダーの中でloaded_image->LoadOptionsを参照してshellに入力された文字列を取得しています.ここで少し注意が必要なのは,UEFIで扱う文字列はUTF16(正確にはUCS-2)であるという点です.したがって1文字2バイトですが,扱いにくいのでここでは無理やり1バイトにキャストしてしまっています.
uefi_init()の中でuefi_entry_pcpyを使って,ローダーのメモリ領域にあるオプション文字列をBitVisor側のメモリ領域にコピーします.あとはBitVisor側から必要に応じてこの文字列を参照するだけです.

実行例

diff -r dd0a7173203a core/main.c
--- a/core/main.c       Sat Dec 01 10:28:52 2018 +0000
+++ b/core/main.c       Sun Dec 02 08:37:06 2018 +0000
@@ -61,6 +61,7 @@
 #include "vramwrite.h"
 #include "vt.h"
 #include "vt_init.h"
+#include "uefi.h"
 #include <share/uefi_boot.h>

 static struct multiboot_info mi;
@@ -86,6 +87,7 @@
 print_startvm_msg (void)
 {
        printf ("Starting a virtual machine.\n");
+       printf ("uefi: option = %s, (len=%ld)\n", uefi_boot_option, uefi_boot_option_size);
 }

 static void

uefi-boot-option.png

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1