MacでBitVisor

BitVisor 1.4からUEFIに対応し、制約があるもののMacのEFIでも動作するようになっています。MacでBitVisorを使う方法の例を紹介します。

## ビルド

BitVisorのビルドはGNU/Linux環境で行うのが簡単です。言うまでもなく、皆さんのMacにもすでにEFI/UEFIブートのGNU/Linuxがインストールされていることと思います。ここではUbuntuを例にしてデフォルト設定でのビルド手順を示します。

1. 必要なパッケージのインストール: Ubuntuでは build-essentials, mercurial, mingw-w64 などが必要です。
2. ソースコードの取得: 最新のソースコードをBitbucketから取得します。<br>`hg clone https://bitbucket.org/bitvisor/bitvisor`
3. VMM本体のビルド: `make -C bitvisor`
4. VMMローダーのビルド: `make -C bitvisor/boot/uefi-loader`

できあがった以下のファイルを使用します。

- bitvisor/bitvisor.elf
- bitvisor/boot/uefi-loader/loadvmm.efi

## 起動

起動の方法はいくつかあります。

- USBフラッシュドライブ
- 内蔵ドライブ (EFIシステムパーティション)
- EFI Shell
- ネットワーク

### USBフラッシュドライブ

USBフラッシュドライブから起動するのは最も手軽な方法のひとつです。FAT32でフォーマットされているUSBフラッシュドライブを用意しましょう。

1. USBフラッシュドライブをマウントし、EFI/BOOTディレクトリーを (なければ) 作成します。<br>`mkdir -p /mnt/EFI/BOOT`
2. bitvisor.elfをEFI/BOOTディレクトリーにコピーします。<br>`cp bitvisor/bitvisor.elf /mnt/EFI/BOOT`
3. loadvmm.efiをbootx64.efiという名前でEFI/BOOTディレクトリーにコピーします。<br>`cp bitvisor/boot/uefi-loader/loadvmm.efi /mnt/EFI/BOOT/bootx64.efi`
4. USBフラッシュドライブをアンマウントし、システムを再起動します。<br>`reboot`
5. Macの起動音がなる頃に、option (Alt) キーを押しっぱなしにしてStartup Managerを起動させます。
6. USBフラッシュドライブを選択して起動すると、BitVisorが起動します。オペレーティングシステムは続けて自動的に起動します。

![Startup Managerの画面](http://www.e-hdk.com/tmp/20151204-1.jpeg)

この方法では起動メッセージを拝むことができませんが、非常に手軽な方法です。なお、起動するオペレーティングシステムはシステムに設定されている順番通りになりますので、事前にefibootmgrコマンド等を用いて設定しておきましょう。

### 内蔵ドライブ (EFIシステムパーティション)

EFI/UEFIブートのGNU/Linux環境であれば、内蔵ドライブのEFIシステムパーティションにインストールして起動するのも非常に簡単です。EFIシステムパーティションは通常/boot/efiディレクトリーにマウントされています。

1. EFIシステムパーティションに適当にディレクトリーを作ります。<br>`mkdir -p /boot/efi/EFI/BitVisor`
2. 作ったディレクトリーにファイルをコピーします。<br>`cp bitvisor/bitvisor.elf bitvisor/boot/uefi-loader/loadvmm.efi /boot/efi/EFI/BitVisor`
3. efibootmgrコマンドを用いて起動の設定を行います。デバイス名 (/dev/sda) およびパーティション番号 (1) は適切に変更してください。<br>`efibootmgr -c -l \\EFI\\BitVisor\\loadvmm.efi -L BitVisor -d /dev/sda -p 1`
4. 再起動すればBitVisorが起動し、続けてオペレーティングシステムが起動します。

この方法ではefibootmgrコマンドが丁寧に起動順序を設定してくれますので、何も操作しなくても自動的にBitVisorが起動するようになります。自動的に起動してほしくない場合は、efibootmgrコマンドの-o (--bootorder) オプションを使って、起動順序に含まれないようにしましょう。

efibootmgrコマンドの-n (--bootnext) オプションを使えば、次回起動時だけBitVisorを起動させるといったこともできます。詳しくはefibootmgrコマンドのマニュアルを参照してください。

### EFI Shell

EFI Shellから起動する方法では、EFI Shellによってテキストモードに画面が設定されるため、起動メッセージを拝むことができます。EFI Shellを起動する方法はいくつかあります。

1. rEFItを使用する: rEFItにはEFI Shellが付属しており、選択するだけで起動できます。
2. USBフラッシュドライブを使用する: rEFIt付属のshell.efiファイルを、FAT32でフォーマットされたUSBフラッシュドライブのEFI/BOOTディレクトリーにbootx64.efiというファイル名で置いて、Startup ManagerからUSBフラッシュドライブを選択することでEFI Shellを起動できます。
3. 内蔵ドライブ (EFIシステムパーティション) を使用する: EFIシステムパーティションにrEFIt付属のshell.efiを置き、efibootmgrコマンドで起動順序を設定することで、EFI Shellを起動させることができます。EFI Shellからアクセス可能なファイルシステムにbitvisor.elfおよびloadvmm.efiを置いておけば、loadvmm.efiを起動することができます。

EFI ShellはデフォルトではDOSのコマンドインタープリター風のプロンプトが表示されます。ドライブ文字の代わりにfs0:, fs1:といったドライブ名を使用します。ディレクトリーの区切り文字は\です。USキーボード配列で動作します。例えばfs0:にEFIシステムパーティションが見えている場合、以下のようにしてBitVisorを起動し、さらに、UbuntuのGNU GRUBを起動する、といったことができます。

`fs0:\EFI\BitVisor\loadvmm.efi`
`fs0:\EFI\ubuntu\shimx64.efi`

![EFI Shellの画面](http://www.e-hdk.com/tmp/20151204-3.jpeg)
![EFI ShellからBitVisorを起動したあとの画面](http://www.e-hdk.com/tmp/20151204-4.jpeg)

### ネットワーク

MacのネットワークブートはBoot Service Discovery Protocolと呼ばれる仕組みが使われています。適切に設定されたDHCPサーバーが動作していれば、Nキーを押しながらMacを起動することで、EFIアプリケーションを直接TFTPで転送させて、実行させることが可能です。

そこで、loadvmm.efiをロードさせたいところですが、loadvmm.efiは同じディレクトリーのbitvisor.elfを読み込もうとするので、TFTPブートの場合そこで失敗するはずです。よって、ローダーの書き換えが必要です。頑張ってください!

## ログ確認

デフォルトで有効になっているデバッグ用機能dbgshコマンドを使用すると、BitVisorのログを確認したり、メモリーダンプを行ったりすることができます。dbgshコマンドは、ゲストOS上でBitVisorのソースコードのtools/dbgshディレクトリーでmakeコマンドを実行することによりビルドできます。GNU/Linuxの場合はだいたいこれでいけると思いますが (Windows用のバイナリーも同時にビルドされます)、ゲストOSの都合によりclangをご利用の場合は、ひと工夫必要かも知れません。

![dbgshの画面](http://www.e-hdk.com/tmp/20151204-2.jpeg)

logコマンドでログの参照、debugコマンドでメモリーダンプが行えるデバッガーが起動します。

## 設定変更

何かBitVisorらしいことをしてみないと楽しくないですよね。でもストレージ暗号化はちょっと面倒なので (準備が大変、xHCI/NVMe未対応、など)、ネットワークでいきましょう。Macに有線LANポートが内蔵されている場合は、BitVisorに組み込まれているbnxドライバーを使用することで、BitVisor内のTCP/IPスタックを有効にしたり、ゲストOSにvirtio-netデバイスを見せたりすることができます。たぶん。

設定はBitVisorソースコードのdefconfigファイルを編集して再ビルドすることによって変更できます。まず、TCP/IPスタックの設定を入れます。IPアドレスはDHCPで取得する設定にもできますが、同時にvirtio-netでゲストOSからもDHCPを使うと混乱のもとですので (同じMACアドレスを使用するため)、静的に割り振る例を示します。

```
  .ip = {
     .use_dhcp = 0,
     .ipaddr = { 192, 168, 0, 100 },
     .netmask = { 255, 255, 255, 0 },
     .gateway = { 192, 168, 0, 1 },
  },
```

次に、bnxドライバーを有効にします。.driverの中に`.pci = "",`というのがあるので、これを書き換えます。

```
  .pci = "device=bnx, number=0, driver=bnx, net=ippass, virtio=1",
```

これは、最初に見つかったBroadcom有線LANに対してbnxドライバーを割り当て、オプションとしてvirtio-netを有効にし、ゲストOSとTCP/IPスタックの送信データをそのままNICから送信し、NICから受信したデータをゲストOSとTCP/IPスタックの両方に渡すようにする設定です。

もしかしたら、deep_tkkn様の記事 http://qiita.com/deep_tkkn/items/ee6563aafb0d33de8450 や、いおりん様の記事 http://iorin.hatenablog.jp/entry/2015/12/03/204653 にあるように、ドライバー内のデバイスIDが足りてない場合もあるかも知れません。このような時には、実はソースコードをいじらなくても、以下のように指定して強引にロードさせることも可能です。

```
  .pci = "class_code=020000, id=14e4:165f, number=0, driver=bnx, net=ippass, virtio=1",
```

設定は以上です。再度ビルドを行い、bitvisor.elfを更新してください。

さて、起動の際にコツがいります。BitVisorを起動する前に、MacのEFIファームウェアがロードしたネットワークドライバーをアンロードしなければなりません。これにはEFI Shellを使うのが簡単です。EFI Shellからdriversというコマンドを実行し、Broadcom Gigabit Ethernet Driverの番号を見つけて、unloadコマンドに指定することでアンロードします。

![EFI Shellのドライバーアンロード画面](http://www.e-hdk.com/tmp/20151204-5.jpeg)

そうだ、rEFItから立ち上げたEFI Shellでは、ドライバーが出てこないかも知れません。Mac特有のblessed HFS+ partitionからの起動では、ネットワーク関連機能が初期化されていない場合があります。それならいいじゃないか、と思うところですが、今の実装ではたぶんそれではちゃんと動きません。ちゃんと動かすには、EFIファームウェアのネットワークドライバーに初期化をしてもらって、それからアンロードして、それからBitVisorという流れになります。rEFItをご使用の場合でも、Startup Manager経由で起動すればネットワーク関連機能が初期化されます。

後は通常通りBitVisorを起動して、次にゲストOSを起動すればOKです。アンロードを忘れると、BitVisor起動中にフリーズしたり、OS起動後のネットワーク通信がうまくいかなかったりすることがあります。

ゲストOSにvirtio-netデバイスのデバイスドライバーが組み込まれている場合、virtio-netデバイスが検出されて、ネットワークが使用可能になります。ip aコマンドの出力にstate UNKNOWNなどと出ているかと思いますが、これはリンクアップ・リンクダウンの情報をゲストOSに見せる部分 (VIRTIO_NET_F_STATUS) が実装されていないことによるものです。

BitVisorのログを確認すると、BitVisorが使用しているIPアドレスがわかります。今回は静的に割り振っていますので確認の必要はありませんが。

![ログの中のIPアドレス](http://www.e-hdk.com/tmp/20151204-8.jpeg)

TCP/IPスタックはICMP echoに応答します。また、BitVisorにはテスト用のechoサーバー・クライアントが組み込まれていますので、簡単な動作チェックができます。テスト用のechoサーバーを開始するには、dbgshからechoctlコマンドを実行し、server startコマンドにポート番号を指定して実行します。

![テスト用のechoサーバー開始](http://www.e-hdk.com/tmp/20151204-7.jpeg)

うまくいったら他のホストから試しに接続してみてください。

![テスト用のechoサーバー動作確認](http://www.e-hdk.com/tmp/20151204-6.jpeg)