BitVisor にブートオプションを渡して挙動を変えてみる (PCI デバイス周りだけ)

# はじめに
昨年の BitVisor Advent Calendar で [BitVisor でブートオプションっぽく文字列を取得してみる](http://qiita.com/deep_tkkn/items/8c366819e858fe78a02e) という記事を書きました．
この記事は，それを少しだけ発展させて，ブートオプションで BitVisor の動作を変えてみようというものです．

具体的には，defconfig の config.vmm.driver.pci の設定をブートオプションで指定できるようにしています．

# 検証環境

* Thinkpad X1 (初代)
* BIOS ブート
* Grub から起動


# おさらい: ブートオプションの文字列を取得
昨年の記事の載せているパッチを当てて動かしてみました．
特に問題はなさそうです．

https://bitbucket.org/ftakaaki/bitvisor/commits/af66e43f521407d36ac85cbf1b8dec66d2351ffe

``` diff
diff --git a/core/main.c b/core/main.c
--- a/core/main.c
+++ b/core/main.c
@@ -101,6 +101,30 @@
 	}
 }
 
+#define MAX_CMDLINE_SIZE 1024
+char cmdline_str[MAX_CMDLINE_SIZE];
+
+static void
+get_cmdline_params(struct multiboot_info *mi)
+{
+	char *addr;
+	int i;
+
+	if(!mi->flags.cmdline){
+		printf("no cmdline\n");
+		return;
+	}else{
+       addr = mapmem (MAPMEM_HPHYS, mi->cmdline,
+			       MAX_CMDLINE_SIZE);
+		for(i = 0; i < MAX_CMDLINE_SIZE; i++){
+			cmdline_str[i] = addr[i];
+			if(addr[i] == '\0')
+				break;
+		}
+		printf("cmdline:%s\n", cmdline_str);
+	}
+}
+
 static void
 copy_minios (void)
 {
@@ -300,8 +324,10 @@
 	u8 bios_boot_drive = 0;
 	void *p;
 
-	if (!uefi_booted)
+	if (!uefi_booted) {
 		bios_boot_drive = detect_bios_boot_device (&mi);
+		get_cmdline_params(&mi);
+	}
 	save_bios_data_area ();
 	callrealmode_usevcpu (current);
 	if (minios_startaddr) {
```

```
> log 
Starting BitVisor...
Copyright (c) 2007, 2008 University of Tsukuba
All rights reserved.
(中略)
cmdline:This is command line options
(中略)
Starting a virtual machine.
Loading MBR.
```

気になることとしては，[昨年の記事] (http://qiita.com/deep_tkkn/items/8c366819e858fe78a02e) にあるように，iPXE ブート環境こちらだと，イメージファイル名などがブートオプションに入っていたのに，Grub からのブートだとそれらしきものがありません．
iPXE ブートにも対応しようとすると，この違いを吸収する必要がありそうです．
今回は面倒なのでそこまではしません．



# defconfig の config.vmm.driver.pci をブートオプションで指定しよう
defconfig には，config.vmm.driver.pci という項目があります．設定方法は http://qiita.com/hdk_2/items/f906c352f44a2afa6ad9 や http://www.bitvisor.org/summit3/slides/bitvisor-summit-3-02-eiraku.pdf に書かれています．

## なぜ config.vmm.driver.pci だけなの?
一言でいうと，ほかのに対応するのは面倒だからなんですが，なぜ面倒なのかという話を書きます．

defconfig はよく見ると，C の構造体の初期化の形をしています．
なので，これを C のコードから include してビルドすれば，設定値を解釈して埋め込めるわけです．
ですが，起動時にビルドなしで値をしていするには，自前でパーサーを書かないといけませんが，自分でパーサーを書くのは面倒です．
しかし，config.vmm.driver.pci は，値が一つの文字列となっていて，それを BitVisor が起動時にパースしています．
なので，config.vmm.driver.pci なら自分でパーサーを書かなくてもできるじゃん，というわけです．

また，config.vmm.driver.pci だけでも pci デバイス周りの設定が結構できるので，ここだけでもブートオプションで指定できると楽になるんじゃないかなと思います．

# どうやったか
ブートローダからもらってきた文字列を config.vmm.driver.pci にコピーするだけです．
https://bitbucket.org/ftakaaki/bitvisor/commits/58d63ceedf112acbedb59f7d958ba588b747bcb2 (以下の diff の addr = ... の部分はインデントの修正なので，関係ないです)

``` diff
diff --git a/core/main.c b/core/main.c
--- a/core/main.c
+++ b/core/main.c
@@ -104,7 +104,7 @@
 #define MAX_CMDLINE_SIZE 1024
 char cmdline_str[MAX_CMDLINE_SIZE];
 
-static void
+static bool
 get_cmdline_params(struct multiboot_info *mi)
 {
 	char *addr;
@@ -112,9 +112,9 @@
 
 	if(!mi->flags.cmdline){
 		printf("no cmdline\n");
-		return;
+		return false;
 	}else{
-       addr = mapmem (MAPMEM_HPHYS, mi->cmdline,
+		addr = mapmem (MAPMEM_HPHYS, mi->cmdline,
 			       MAX_CMDLINE_SIZE);
 		for(i = 0; i < MAX_CMDLINE_SIZE; i++){
 			cmdline_str[i] = addr[i];
@@ -123,9 +123,16 @@
 		}
 		printf("cmdline:%s\n", cmdline_str);
 	}
+	return true;
 }
 
 static void
+set_cmdline_to_config_pci_params (void) {
+	memcpy (config.vmm.driver.pci, cmdline_str, 2048);
+}
+
+
+static void
 copy_minios (void)
 {
 	struct multiboot_modules *q;
@@ -326,7 +333,10 @@
 
 	if (!uefi_booted) {
 		bios_boot_drive = detect_bios_boot_device (&mi);
-		get_cmdline_params(&mi);
+		if (get_cmdline_params(&mi)) {
+			set_cmdline_to_config_pci_params ();
+		}
+
 	}
 	save_bios_data_area ();
 	callrealmode_usevcpu (current);
```
# 使い方
Grub で multiboot bitvisor.elf <オプションとなる文字列> でブートすれば OK です．
オプションのフォーマットは，http://qiita.com/hdk_2/items/f906c352f44a2afa6ad9 や http://www.bitvisor.org/summit3/slides/bitvisor-summit-3-02-eiraku.pdf を参考にしてください．

# 既知の問題: defconfig で設定した config.vmm.driver.pci と共存できない

* ブートローダからもらった文字列をコピーしているだけですので，元のdefconfigによる設定は上書きされてしまいます．
* なので，defconfig で指定した設定の一部だけをブートローダで設定を変更する，といった器用なことはできません．
* 空文字列を指定しても，空文字列がコピーされて，defconfig の設定は反映されません．
* 真面目に共存させるにはやはり，ちゃんとパーサーに通す必要がありそうです．

# おわりに

かなり手抜きではありますが，もしかしたらちょっと役に立つかもしれないものを作ってみました．
BitVisor のコードを見ていたら，boot/login-simple/ 以下になにやら config をパースするっぽいコードがあったりするので，それをうまく使えば，もしかしたら自分でパーサーを書かずに，もっとちゃんとしたブートオプション機能を実装できるかもしれまんせん．
また，このコードは https://bitbucket.org/ftakaaki/bitvisor/commits/all にあります．