BitVisorでPCIデバイスのIDを変える

BitVisorを書き換えて、仮想マシン上のPCIデバイスのIDを変える方法を紹介します。仮想マシンのデバイスといってもBitVisorなので実マシンのデバイスなのですが、IDを変えるといっても物理的なデバイスに何かやるわけではなく、para-passthrough device driverを実装して、IDだけを変えてやります。

# 実装方法

PCIデバイス用のpara-passthrough device driverはdriver/ディレクトリー以下にいくつもの実装例が含まれていますので、それを参考にすると簡単に実装できます。

基本的には、pci_register_driver()関数を呼び出す初期化関数を実装してPCI_DRIVER_INITマクロで同関数が呼び出されるようにすることと、pci_register_driver()関数に渡すstruct pci_driver構造体の準備、および、同構造体の関数ポインターが指す関数等を実装すればOKです。

pci_register_driver()関数で登録が済んだドライバーは、以下の記事にあるようにマッチングが成立すればnewポインターが指す関数が呼び出され、その後のPCI configuration spaceのアクセスでconfig_readおよびconfig_writeポインターが指す関数が呼び出されるようになります。これらの関数でPCI configuration spaceに含まれるデバイスIDなどを変更して見せることも可能になります。

BitVisorのvmm.driver.pciの指定の仕方
http://qiita.com/hdk_2/items/f906c352f44a2afa6ad9

ベースアドレスレジスターが指すI/O空間へのアクセスなどを横取りするには、さらにフックの登録等が必要になります。しかし、今回はデバイスのIDを変更するだけなので、ドライバーの登録だけで十分です。

# 実装例

以下に実装例を差分で示します。(タブは無いので気にしなくて大丈夫です。)

```diff:
diff --git a/drivers/Makefile b/drivers/Makefile
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -18,3 +18,4 @@ objs-$(CONFIG_LOG_TO_IEEE1394) += ieee13
 objs-$(CONFIG_VGA_INTEL_DRIVER) += vga_intel.o
 objs-$(CONFIG_TTY_X540) += x540.o
 objs-$(CONFIG_PCI_MONITOR) += pci_monitor.o
+objs-1 += fakeid.o
diff --git a/drivers/fakeid.c b/drivers/fakeid.c
new file mode 100644
--- /dev/null
+++ b/drivers/fakeid.c
@@ -0,0 +1,17 @@
+#include <core.h>
+#include <core/strtol.h>
+#include "pci.h"
+static int config_read (struct pci_device *dev, u8 iosize, u16 offset,
+  union mem *data) { char *s = dev->driver_options[0], *t;
+  if (s && offset < 4) { union { u8 b[1]; u16 w[6]; } a; memset (&a, 0,
+  sizeof a); a.w[0] = strtol (s, &t, 16); if (s != t && *t++ == ':') {
+  a.w[1] = strtol (t, &s, 16); if (t != s) { memcpy (data, &a.b[offset],
+  iosize); return CORE_IO_RET_DONE; } } } return CORE_IO_RET_DEFAULT; }
+static int config_write (struct pci_device *dev, u8 iosize, u16 offset,
+  union mem *data) { return CORE_IO_RET_DEFAULT; }
+static void new (struct pci_device *dev) { }
+static struct pci_driver fakeid_driver = { .driver_options = "id",
+  .name = "fakeid", .longname = "fakeid", .device = "id=:", .new = new,
+  .config_read = config_read, .config_write = config_write };
+static void fakeid_init (void) { pci_register_driver (&fakeid_driver); }
+PCI_DRIVER_INIT (fakeid_init);
```

このfakeidドライバーは、IDの読み取りに対して、オプションidにvendor:device形式で16進数で指定されたIDを返すようにするものです。このようにわずか17行の簡単なプログラムで実現できます。

# 実行例

以下の記事では/proc/cpuinfoのvendor_idのところをAMDに見えるようにしましたが、これではPCIデバイスにまだIntelのデバイスがいくつも見えてしまいます。

BitVisorでCPUID命令の挙動を変える
http://qiita.com/hdk_2/items/9cc8312e1b4948c51dc6

例えば、iMac Mid-2014ではAHCIコントローラーとして以下のようにIntelのものが見えています。

```
$ lspci -s 1f.2 -nn
00:1f.2 SATA controller [0106]: Intel Corporation 8 Series SATA Controller 1 [AHCI mode] [8086:9c03] (rev 04)
```

これでは似非AMDファンなMacユーザーの皆様は不満だと思いますので、これを変えてみましょう。fakeidを組み込んだBitVisorのvmm.driver.pciに、以下のように設定するだけです。

```
vmm.driver.pci=id=8086:9c03, driver=fakeid, id=1022:7801
```

これで、ベンダーIDとデバイスIDを変えることができます。結果は以下の通りです。どう見てもAMDです。

```
$ lspci -s 1f.2 -nn
00:1f.2 SATA controller [0106]: Advanced Micro Devices, Inc. [AMD] FCH SATA Controller [AHCI mode] [1022:7801] (rev 04)
```

もちろんmacOS上でも確認できます。BitVisorなしの場合、以下のようにIntelと出ています。

![20161209-2.png](https://qiita-image-store.s3.amazonaws.com/0/103733/85460f6f-cc75-1d51-e778-eb3fb3cb5d77.png)

これが、BitVisorで上のIDを見せた場合は以下のようにGenericになります。

![20161209.png](https://qiita-image-store.s3.amazonaws.com/0/103733/8f443ae7-5ece-70b3-22ba-dddfbd58b996.png)


えっ、メモリーコントローラーやVGA互換コントローラーなど、他にもIntelのものがいっぱい見えているだろって? えっと、そうなんですけど、特にIntel Integrated Graphics Controllerを使っている場合、VRAMがRAMの一部から確保されるためだと思いますが、メモリーコントローラーのIDなどもデバイスドライバーの動作に影響するらしいので、そのへんのIDを変えてしまうと (VGA互換コントローラーのIDを変えなくても) 画面が映らなくなってしまうかも知れないです。興味があれば試してみてください。SATAコントローラーやUSBコントローラーなどは標準規格になっていますのでIDを変えてもトラブルは起きにくいと思います。