HyperV 上で BitVisor は動くのか？

# はじめに
おとといくらいまで，今日の記事の予定は「Bhyve の上で BitVisor は動くのか?」になっていたと思います．
が，よくよく考えたら，Bhyve は Nested Virtualization 対応してないので，自分で Bhyve を改造して Nested Virtualization 対応させでもしない限り，動くわけないじゃん...ということに一昨日気づいたので変えました．
予約したときの僕は寝ぼけていたのか，はたまた酔っていたのか...

というわけで，今日の記事は HyperV 上で BitVisor が動くかどうか試してみたという記事です．

# 先に結論を言うと
動かなかった...

# 検証環境

僕の検証環境は，以下の通りです．

* マシン: Thinkpad X220
* Windows: Windows 10 Pro Insider Preview Build 14931
* L2 Guest: Ubuntu Server 16.04.1 

# 準備
## Windows の用意
Hyper-V は Windows に組み込まれている仮想マシンモニタです．
Linux でいうところの，QEMU/KVM 的な立ち位置だと思います．
そういうわけなので，まずは Windows が必要です．
ただし，Windows でも Home エディションはダメで，Windows Server 2008 以降とか Windows 8 以降の Pro とか Enterprise とか Education とかが必要です．
対応バージョンの公式情報がどこにあるのかよくわかりません... (Wikipedia には書いてあったが出典ないし...)


## Hyper-V を有効化
「Windows の機能の有効化または無効化」にある「Hyper-V」っぽいものをチェック．
Windows の再起動が必要だったかもしれないです(覚えてない)

## VM の作成
なんか適当にやったらできたので，適当にやりましょう．
わからなかったらきっとぐーぐる先生が教えてくれます．

UEFI ブートにするか，BIOS ブートにするか選ぶところがありますが，今回は BIOS ブートにしました．

VM には Ubuntu Server 16.04.1 をインストールして，Hyper-V の VM はできあがり．

## Nested Virtualization の有効化
http://www.altaro.com/hyper-v/nested-virtualization-hyper-v-windows-server-2016/　

のページの中ごろにあるビデオ通りにやればいけると思います．
VM は一度停止しないと設定変更できないので，一度停止しましょう．

## BitVisor ビルド
特に変わったことはしてないので，ざっくり書く．

1. VM にインストールした Ubuntu に Mercurial, Make, GCC, build-essential をインストール
2. BitBucket のリポジトリをクローン
3. make config で要らなさそうなオプションを外す
4. make 
5. /boot/ に bitvisor.elf をコピー
6. grub のエントリーを追加
7. update-grub2,
8. reboot

# Let's Try
とりあえず，動かしてみる．
ばばーん

![image](https://qiita-image-store.s3.amazonaws.com/0/59503/914b0d30-f122-b5bb-007e-45836dceb6ea.png)

... 普通にインストールしただけじゃ動かないっぽい...

## どこで止まったのか

エラーメッセージから，起動のかなり初期の段階で止まったことが察せられます．
dbgsh も立ち上がらないので，起動処理の途中に while (1) 入れて panic するか無限ループで止まるかの Try & Error をやりまくる．
(Hyper-V のデバッグ機能か何かあるんですかね? 僕はよく知らないので今回はそういうの一切使ってないです...)
で，30回くらいコンパイルと再起動を繰り返した結果，IA32_STAR という MSR に何か書こうとしているところで死んでるっぽいことが分かった．

https://bitbucket.org/bitvisor/bitvisor/src/76db0ae4260b398f939486faedbd6dedb3e79b5f/core/process.c?at=default&fileviewer=file-view-default#process.c-140


IA32_STAR は 64 bit システムでモダンなシステムコール呼び出し機構を使うのに必要．
しかし，今はとりあえず動いてほしいので，32 bit でコンパイルしてみる．


## 32bit BitVisor をビルド
再度，make config，64-bit オプションを外す．
これで，ビルドすると，なんとビルドが通らない．
以下のパッチを当てるとビルドが通る．

``` diff
diff --git a/core/cpu_interpreter.c b/core/cpu_interpreter.c
--- a/core/cpu_interpreter.c
+++ b/core/cpu_interpreter.c
@@ -2507,8 +2507,8 @@
 		     : "=&rm" (newflags) \
 		     , "=&rm" (dst) \
 		     : "i" (~flagmask) \
-		     , "r" (flags & flagmask) \
-		     , "r" (src) \
+		     , "q" (flags & flagmask) \
+		     , "q" (src) \
 		     , "1" (dst) \
 		     : "cc"); \
 		newflags = (flags & ~flagmask) | (newflags & flagmask); \
```

# Let't Try (32 bit 版)
ばばーん

![image](https://qiita-image-store.s3.amazonaws.com/0/59503/8b293f11-75d3-c7c6-bfc0-46aaad26681d.png)

やっぱり動かないぞ...
けど，64bit の BitVisor よりは処理が進んだみたい.

"VM entry failed. Error 8." の意味を調べてみる．
Intel SDM によると，Error 8 は "VM entry with invalid host-state field(s)" ってことらしい...
host-state っていったい何個あると思ってるねん...

めげずに調べるために，デバッグ用のシェルに入ろうとキーボードの s 押しても反応がない...

余談:
あとで，Ubuntu から挙動を見る限り，どうも PS/2 でも USB でもない，不思議な力でキー入力がゲストに配送されているっぽい挙動をしている．
(`$ cat /proc/interrupts` してみると，Hypervisor callback interrupt とか言うのが入っていて，たぶんこれじゃないかと思う) 

## ログが見たいので，COM ポートをつなごう
何はともあれ，これでは不便なので，COMポート(シリアルポート)経由で BitVisor のログを出す．

* BitVisor側
    * make config して TTY_SERIAL を有効にしておく
    * ついでに，以下のようにデバッグプリントを足してみた．

``` diff
diff --git a/core/vt_main.c b/core/vt_main.c
--- a/core/vt_main.c
+++ b/core/vt_main.c
@@ -389,12 +389,47 @@
 	return VT__VMEXIT;
 }
 
+#define DBG(i)	do { \
+			ulong tmp; \
+			asm_vmread (i, &tmp);\
+			printf ( #i "==0x%lx\n", tmp);\
+		} while(0)
+
+static void
+debug_print_host_state ()
+{
+	DBG (VMCS_HOST_ES_SEL);
+	DBG (VMCS_HOST_CS_SEL);
+	DBG (VMCS_HOST_SS_SEL);
+	DBG (VMCS_HOST_DS_SEL);
+	DBG (VMCS_HOST_FS_SEL);
+	DBG (VMCS_HOST_GS_SEL);
+	DBG (VMCS_HOST_TR_SEL);
+	DBG (VMCS_HOST_IA32_PAT);
+	DBG (VMCS_HOST_IA32_PAT_HIGH);
+	DBG (VMCS_HOST_IA32_EFER);
+	DBG (VMCS_HOST_IA32_EFER_HIGH);
+	DBG (VMCS_HOST_IA32_SYSENTER_CS);
+	DBG (VMCS_HOST_CR0);
+	DBG (VMCS_HOST_CR3);
+	DBG (VMCS_HOST_CR4);
+	DBG (VMCS_HOST_FS_BASE);
+	DBG (VMCS_HOST_GS_BASE);
+	DBG (VMCS_HOST_TR_BASE);
+	DBG (VMCS_HOST_GDTR_BASE);
+	DBG (VMCS_HOST_IDTR_BASE);
+	DBG (VMCS_HOST_IA32_SYSENTER_ESP);
+	DBG (VMCS_HOST_IA32_SYSENTER_EIP);
+	DBG (VMCS_HOST_RSP);
+	DBG (VMCS_HOST_RIP);
+}
 static void
 vt__vm_run_first (void)
 {
 	enum vt__status status;
 	ulong errnum;
 
+	debug_print_host_state ();
 	status = call_vt__vmlaunch ();
 	if (status != VT__VMEXIT) {
 		asm_vmread (VMCS_VM_INSTRUCTION_ERR, &errnum);
@@ -845,6 +880,7 @@
 	ulong exit_reason;
 
 	asm_vmread (VMCS_EXIT_REASON, &exit_reason);
+	printexitreason (exit_reason);
 	if (exit_reason & EXIT_REASON_VMENTRY_FAILURE_BIT)
 		panic ("Fatal error: VM Entry failure.");
 	switch (exit_reason & EXIT_REASON_MASK) {
```


* Hyper-V 側
    * Hyper-V の VM 設定で，COM を設定
    * 名前付きパイプとやらに名前を付ける．
    * 適当に，bitvisor とか名前を付ける．
    * TeraTerm を "管理者として実行"
    * ホストのところに，\\.\pipe\bitvisor と入力


![com-setting.PNG](https://qiita-image-store.s3.amazonaws.com/0/59503/8af97fe1-42c8-e38d-090a-52b60e806f67.png)

![image](https://qiita-image-store.s3.amazonaws.com/0/59503/e329c776-bced-fa99-3074-3550d2e0230f.png)


## ログ出してみた

```
> log
Starting BitVisor...
Copyright (c) 2007, 2008 University of Tsukuba
All rights reserved.
1073282048 bytes (1023 MiB) RAM available.
VMM will use 0x37C00000-0x3FC00000 (128 MiB).
ACPI DMAR not found.
FACS address 0x3FFFF000
Module not found.
Processor 0 (BSP)
Processor 1 (AP)
..................................................                                                  oooooooooooooooooooooooooooooooooooooooooooooooooo                                                  OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
Disable \_SB_.PCI0.SBRG.UAR2._DIS
Disable \_SB_.PCI0.SBRG.UAR1._DIS
Using VMX.
Processor 0 2497709136 Hz
Processor 1 2497717520 Hz
Loading drivers.
PCI device concealer registered
PCI device monitor registered
PCI: finding devices...
PCI: 5 devices found
Starting a virtual machine.
VMCS_HOST_ES_SEL==0x10
VMCS_HOST_ES_SEL==0x10
VMCS_HOST_CS_SEL==0x8
VMCS_HOST_CS_SEL==0x8
VMCS_HOST_SS_SEL==0x10
VMCS_HOST_SS_SEL==0x10
VMCS_HOST_DS_SEL==0x10
VMCS_HOST_DS_SEL==0x10
VMCS_HOST_FS_SEL==0x10
VMCS_HOST_FS_SEL==0x10
VMCS_HOST_GS_SEL==0x40
VMCS_HOST_GS_SEL==0x40
VMCS_HOST_TR_SEL==0x60
VMCS_HOST_TR_SEL==0x60
VMCS_HOST_IA32_PAT==0x289
VMCS_HOST_IA32_PAT==0xec8348e5
VMCS_HOST_IA32_PAT_HIGH==0x850fc085
VMCS_HOST_IA32_PAT_HIGH==0x8b486510
VMCS_HOST_IA32_EFER==0x0
VMCS_HOST_IA32_EFER==0x0
VMCS_HOST_IA32_EFER_HIGH==0x0
VMCS_HOST_IA32_EFER_HIGH==0x0
VMCS_HOST_IA32_SYSENTER_CS==0x8
VMCS_HOST_IA32_SYSENTER_CS==0x8
VMCS_HOST_CR0==0x80000039
VMCS_HOST_CR0==0x80000039
VMCS_HOST_CR3==0x38009000
VMCS_HOST_CR3==0x37fed000
VMCS_HOST_CR4==0x20a0
VMCS_HOST_CR4==0x20a0
VMCS_HOST_FS_BASE==0x0
VMCS_HOST_FS_BASE==0x0
VMCS_HOST_GS_BASE==0x402adde0
VMCS_HOST_GS_BASE==0x403ee300
VMCS_HOST_TR_BASE==0x402adf24
VMCS_HOST_TR_BASE==0x403f6504
VMCS_HOST_GDTR_BASE==0x402ade24
VMCS_HOST_GDTR_BASE==0x403f6404
VMCS_HOST_IDTR_BASE==0x401bc600
VMCS_HOST_IDTR_BASE==0x401bc600
VMCS_HOST_IA32_SYSENTER_ESP==0x402adde0
VMCS_HOST_IA32_SYSENTER_ESP==0x403f8000
VMCS_HOST_IA32_SYSENTER_EIP==0x40136000
VMCS_HOST_IA32_SYSENTER_EIP==0x40136000
VMCS_HOST_RSP==0xdeadbeef
VMCS_HOST_RSP==0xdeadbeef
VMCS_HOST_RIP==0xdeadbeef
panic(CPU0): Fatal error: VM entry failed. Error 8
VMCS_HOST_RIP==0xdeadbeef
CR0 80000039    CR2 00000000    CR3 37FED000    CR4 000020A0
RFLAGS 00200006  GDTR 402ADE24+000000FF  IDTR 401BC600+00000800
stackdump: 40145564 40145564 8005AC1 401BC600 0 402AE164 D 40124162 D 403FFE7C 40125B6A 40135C7B 401241BF 40135C90 D 402AE164 0 0 4011D47C 401241BF 4012415B 403FFF08 40125B8B 402A81A0 400 401470AB 40124965 4012415B 401241BF 403FFF08 40124BDE 402A81A0
backtrace: 40124162 40125B6A 40135C7B 401241BF 40135C90 4011D47C 401241BF 4012415B 40125B8B 40124965 4012415B 401241BF 40124BDE 40124D00 40132702 40132564 40125B2D 401310D7 40130DB5 4013253D 4011D68F 40125AC1 4011D68F 40125B0A 4012C8B5 40125B2D 4012E1AD 4011E7A3 4011B6EF 4011E59C 4010D87C
Guest state and registers of cpu 0 ------------
RAX 00000001    RCX 00000000    RDX 00000000    RBX 00000000
RSP 00004FC0    RBP 00000000    RSI 00000000    RDI 00000000
R8  00000000    R9  00000000    R10 00000000    R11 00000000
R12 00000000    R13 00000000    R14 00000000    R15 00000000
CR0 00000030    CR2 00000000    CR3 00000000    CR4 00000000
ACR   ES 000000F3 CS 000000F3 SS 000000F3 DS 000000F3 FS 000000F3 GS 000000F3
LIMIT ES 0000FFFF CS 0000FFFF SS 0000FFFF DS 0000FFFF FS 0000FFFF GS 0000FFFF
BASE  ES 00000000 CS 00000000 SS 00000000 DS 00000000 FS 00000000 GS 00000000
SEL   ES 00000000 CS 00000000 SS 00000000 DS 00000000 FS 00000000 GS 00000000
RIP 00005016  RFLAGS 00000002  GDTR 402ADE24+000000FF  IDTR 00000000+000003FF
EFER 00000000
Exit reason: -1958193920=0x8B485500 (unknown error)  VM entry failure
Exit qualification 00E82878  VM exit interrupt information C0314507
VM entry interruption-information 00000000  errcode 00000000  instlen 00000000
VM exit errcode 0000DFB9  VMCS IDTR 00000000+00000000   VMCS RFLAGS 00020002
re=1 pg=0 sw:en=0x0 es=0x0 cs=0x0 ss=0x0 ds=0x0 fs=0x0 gs=0x0
------------------------------------------------
panic(CPU1): Fatal error: VM entry failed. Error 8
CR0 80000039    CR2 00000000    CR3 38009000    CR4 000020A0
RFLAGS 00200002  GDTR 403F6404+000000FF  IDTR 401BC600+00000800
stackdump: 40145564 40145564 8005AC1 401BC600 1 403F6744 D 40124162 40407EEC 40407E5C 1 40135C7B 401241BF 40135C90 D 403F6744 1 1 4011D47C 401241BF 4012415B 40407EE8 40125B2D 4014570E 40407E98 402A7DD3 40124965 4012415B 401241BF 40407EE8 40124BB9 4014570E
backtrace: 40124162 40135C7B 401241BF 40135C90 4011D47C 401241BF 4012415B 40125B2D 40124965 4012415B 401241BF 40124BB9 40124D00 40132702 40132564 40125B2D 401310D7 40130DB5 4013253D 4011F560 4013332E 4011F51F 4011F560 4013332E 40130939 4011E7A3 4011B6EF 4011E55C 4011E57A 4011E55C 4010D4D5
Guest state and registers of cpu 1 ------------
RAX 00000000    RCX 00000000    RDX 00000000    RBX 00000000
RSP 00000000    RBP 00000000    RSI 00000000    RDI 00000000
R8  00000000    R9  00000000    R10 00000000    R11 00000000
R12 00000000    R13 00000000    R14 00000000    R15 00000000
CR0 00000030    CR2 00000000    CR3 00000000    CR4 00000000
ACR   ES 000000F3 CS 000000F3 SS 000000F3 DS 000000F3 FS 000000F3 GS 000000F3
LIMIT ES 0000FFFF CS 0000FFFF SS 0000FFFF DS 0000FFFF FS 0000FFFF GS 0000FFFF
BASE  ES 00000000 CS 00000000 SS 00000000 DS 00000000 FS 00000000 GS 00000000
SEL   ES 00000000 CS 00000000 SS 00000000 DS 00000000 FS 00000000 GS 00000000
RIP 00000000  RFLAGS 00000002  GDTR 403F6404+000000FF  IDTR 00000000+000003FF
EFER 00000000
Exit reason: -1924530177=0x8D49FFFF (unknown error)  VM entry failure
Exit qualification 00E80000  VM exit interrupt information 0002208D
VM entry interruption-information 00000000  errcode 00000000  instlen 00000000
VM exit errcode 4CF63100  VMCS IDTR 00000000+00000000   VMCS RFLAGS 00020002
re=1 pg=0 sw:en=0x0 es=0x0 cs=0x0 ss=0x0 ds=0x0 fs=0x0 gs=0x0
------------------------------------------------
panic(CPU0): Fatal error: VM entry failed. Error 8
```

VMCS_HOST_* が各2行ずつ出てるのは，仮想CPUを2つ割り当ててるから．

# 力尽きた
ので，あとは誰か頑張ってみてください．
時間があればリベンジするかも．
32bit ビルドしたりデバッグプリンタ足したコードを，https://bitbucket.org/ftakaaki/bitvisor-try-to-run-on-hyperv に置いたので，見たい人はどうぞ．

# おまけ: 64bit ゲスト OS は動かない?
BitVisor は動かなかったけど，VMWare を動かした人はいるらしい．
以下のフォーラムのやり取りから察するに，64bit ゲストは動かないらしい．
試してみたい人は，32 bit ゲストで挑戦した方がいいかも．
https://social.technet.microsoft.com/Forums/systemcenter/en-US/aa10f817-5a46-4c5d-b041-b11abd3c46a8/does-hyper-v-support-nested-vm?forum=virtualmachingmgrhyperv

# おわりに
BitVisor は，Hyper-V の上で，動かなかった．