BitVisorのIntel Graphicsドライバ


BitVisorの使ったことがないドライバを試してみるシリーズ第二弾．
`drivers/vga_intel.c`にIntel Graphicsのドライバがあるようですが，動いているのを実際に見たことないのでこれを試してみます．
この機能に関しては2015年に以下の記事があります．

- @y_oyama, [BitVisorのADvisor機能の利用](https://qiita.com/y_oyama/items/088e6ccc29af3144a5b5), BitVisor Advent Calendar 2015.

上記記事ではHaswell以降のチップセットでは動かないかもしれないと書いてありますが果たして..

## 前準備
VGAコントローラのIDを調べてドライバを読み込ませるようにします．

```sh
$ lspci -nn | grep -i vga
00:02.0 VGA compatible controller [0300]: Intel Corporation Xeon E3-1200 v3/4th Gen Core Processor Integrated Graphics Controller [8086:0412] (rev 06)
```

使用したグラフィックハードウェアはIntel HD 4600 (CPUはi7 4790K)です．

defconfigを以下のように修正します．

```
pci ="class_code=030000,id=8086:0412,number=0,driver=vga_intel"
```

また，`make config`で`INTEL_VGA_DRIVER`, `TTY_VGA`を有効にしておきます．

## ログの出力
`TTY_VGA`を有効にしておくと，問題がなければログが画面左上部に表示されると思います．
自分の場合特に問題なく動きました．

## モザイク化
[BitVisorのADvisor機能の利用](https://qiita.com/y_oyama/items/088e6ccc29af3144a5b5)に書いてあるのと同じことをやってみます．

core/vga.c

```c
// https://qiita.com/y_oyama/items/088e6ccc29af3144a5b5
int vga_tessellate_screen(unsigned int tile_size)
{
  unsigned int width, height;
  unsigned int x, y;
  u32 *tile;

  if (!vga_is_ready()) {
    return 0;
  }

  if (!vga_get_screen_size(&width, &height)) {
    return 0;
  }

  tile = alloc(tile_size * tile_size * sizeof(u32));
  for (y = 0; y < height; y += tile_size) {
    for (x = 0; x < width; x += tile_size) {
      int j;
      if ((x + tile_size > width) || (y + tile_size > height)) {
        continue;
      }
      if (vga_transfer_image(VGA_FUNC_TRANSFER_DIR_GET, tile,
                             VGA_FUNC_IMAGE_TYPE_BGRX_8888,
                             sizeof tile[0] * tile_size,
                             tile_size, tile_size,
                             x, y) < 0) {
        goto out;
      }
      for (j = 1; j < tile_size * tile_size; j++) {
        tile[j] = tile[0];
      }
      if (vga_transfer_image(VGA_FUNC_TRANSFER_DIR_PUT, tile,
                             VGA_FUNC_IMAGE_TYPE_BGRX_8888,
                             sizeof tile[0] * tile_size,
                             tile_size, tile_size,
                             x, y) < 0) {
        goto out;
      }
    }
  }

 out:
  free(tile);
  return 1;
}

static int
vga_mosaic_msghandler (int m, int c){
    if(m == 0){
        if (c <= 0){
            return 0;
        }else{
            vga_tessellate_screen(c);
        }
    }
    return 0;
}

static void
vga_init (void)
{
    rw_spinlock_init (&vga_lock);
    LIST1_HEAD_INIT (vga_list);
    vga_active = NULL;
    msgregister ("mosaic", vga_mosaic_msghandler);
}
```

```sh
% ./dbgsh
> sendint
sendint> mosaic 100
send 100 to mosaic
```

![advisor.gif](https://qiita-image-store.s3.amazonaws.com/0/153099/d108d52f-54e9-1759-0206-9781c51d043b.gif)


無事に動きました．ただし，`sendint`したタイミングでフレームバッファを読み取って書き換えているだけなのでゲストが画面を更新すればそのまま更新されます．

~~(遥か昔の記事の下書きが残っていたので供養でした(ぉ)~~

## Skylakeプロセ ッサの場合
もっと新しいプロセッサの場合はどうでしょうか．

Intel Core i7-6700 (Intel HD 530)でも試してみました．lspciの情報は以下の通りです．

```
00:02.0 VGA compatible controller [0300]: Intel Corporation Sky Lake Integrated Graphics [8086:1912] (rev 06) (prog-if 00 [VGA controller])
```

さて，このマシン上で同じような設定で試してみましたが...残念ながら動作しませんでした:cry:．

### 原因調査
少し調べてみると，デバイスが利用可能か調べている初期段階のコードでエラーでリターンしてしまっています．

- https://bitbucket.org/bitvisor/bitvisor/src/124a31eea07d4e4ea340e974e5183775a6546c6b/drivers/vga_intel.c#lines-228

どうもSkylakeからIntel graphicsの仕様が変わったようです．以下にIntel Graphicsの仕様があります．

- [Intel® Graphics for Linux* - Hardware Specification](https://01.org/linuxgraphics/documentation/hardware-specification-prms)

先ほどのBitVisorのドライバのコードが一体なにをしているのかという話ですが，[この資料](https://01.org/sites/default/files/documentation/ilk_ihd_os_vol3_part2r2_0.pdf)のp.176あたりをみる限り，control registerを読み取ってデバイスが有効かどうかや，ピクセルの深度情報を取得しているみたいです．Skylakeではこの辺りから変わってしまったようです．第９世代からVGAが廃止されてしまったので，その辺りと関係あるのかもしれないです（あまりよくグラフィックのことは分かっていない）．

確かに，Linuxのi915ドライバをみると，第９世代(skylake)以降からところどころ処理を変えているようです ([例](https://github.com/torvalds/linux/blob/v4.20/drivers/gpu/drm/i915/intel_display.c#L13834)）．

## どうでもいい話
そもそも本来は，VMWare上でこのドライバが使えるか試そうと思ったら，VMwareではIntel GraphicsではなくVMWare SVGA IIという準仮想化デバイスを利用していたので，じゃぁSVGA II用のドライバでも作るかーと作業していましたが，SVGA IIからは直接VRAMにアクセスすることはできず（仮想化環境なので当たり前ですが），FIFOキューを通じてホストとやりとりするようになっており，このFIFOキューをフックしようとしたらlock prefix付き命令[^1]でアクセスしてきてあえなく撃沈しました[^2]．
VTでlock prefix付き命令を正しくエミュレーションする方法ご存知の方，教えてください.. (global lock取ればいいんでしょうか）

[^1]: lock cmpxchg
[^2]: BitViorは現状lock prefix付き命令のエミュレーションに対応していません．

## まとめ
- Intel HD 4600 :ok_woman:
- Intel HD 530 :sob:

残念ながら最近のプロセッサでは動作しないようなので，今後この機能を使いたい場合はドライバの改良が必要そうです．

## ところで
この記事がQiitaのBitVisorタグ100本目の記事らしいです．
今年でBitVisor Advent Calendarが4回目なので100本以上記事があるのは当然なのですが．実際にはタグが付いてない記事もあるみたいなので，もうちょっと総数としてはありそうです．
しかしこれだけあるBitVisorの記事がほとんどAdvent Calendarの期間にのみ書かれているというのはなんか異様ですね..笑

何はともあれ，一年間お疲れ様でした:christmas_tree: