過去にMultiboot specificationに基づいてブートする際にBitVisorへオプションを渡せるようにしたという話がありました.
- @deep_tkkn, BitVisor でブートオプションっぽく文字列を取得してみる, BitVisor Advent Calendar 2015.
起動時にオプションが渡せると何かと便利なことがあります.
今回はUEFI shellからBitVisorを起動する際にオプション(と言う名の文字列)を渡せるようにしてみたいと思います.
おさらい: UEFI shellからの起動
本題の前に,USBメモリブートしてUEFI shellからBitVisorを起動する方法を簡単に書きます.
まずUSBメモリをFAT32でフォーマットしたのち, /BOOT/EFI
ディレクトリを作成します.
その中にbitvisor.elf
とloadvmm.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.elf
をBOOTX64.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