BitVisorでUSBキーボードのフック

BitVisorのドライバのコード量のおよそ半分を占めるのにもかかわらずほとんど情報がないusbドライバを触ってみようと思います．USBのことは良く分からないので簡単そうなUSBキーボードのフックをしてみます．

## 前準備
USBキーボードを接続してどこに繋がっているか確認します．

```sh
% lsusb
...
Bus 001 Device 010: ID 0853:011d Topre Corporation

% lsusb -t
...
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/14p, 480M
    |__ Port 13: Dev 10, If 0, Class=Human Interface Device, Driver=usbhid, 12M

% ls -l /sys/bus/usb/devices/
...
... usb1 -> ../../../devices/pci0000:00/0000:00:14.0/usb1

% cat /sys/bus/usb/devices/1-13/product
Realforce 108U

% lspci -s 00:14.0 -nn
01:14.0 USB controller [0c03]: Intel Corporation 9 Series Chipset Family USB xHCI Controller [8086:8cb1]
```

キーボードが繋がっているUSBコントローラのDevice ID, Vendor IDが分かったのでdefconfigで以下のようにしてxhciドライバを読み込ませるようにします．

```c
.driver = {
    .pci ="class_code=0c0330,id=8086:8cb1,number=0,driver=xhci",
}
```

また，configの中でUSBドライバ周りを有効するようにしておきます．(おそらくデフォルトで有効)
さらに，BitVisorが割り込みを捕捉する必要があるため，defconfigの中で`.no_intr_intercept = 0`を設定しておきます．

## フックハンドラの登録

`drivers/usb/usb_hid.c`を以下のように書き換えます．

```c
#include <core.h>
#include "usb.h"
#include "usb_log.h"
#include "usb_hook.h"
#include "usb_device.h"
#include "uhci.h"

#define USB_ICLASS_HID  0x3
#define USB_PROTOCOL_KEYBOARD  0x1

static int
hid_intercept(struct usb_host *usbhc,
      struct usb_request_block *urb, void *arg)
{
    struct usb_buffer_list *ub;
    for(ub = urb->shadow->buffers; ub; ub = ub->next){
        if (ub->pid != USB_PID_IN)
            continue;
        u8 *cp;
        cp = (u8 *)mapmem_gphys(ub->padr, ub->len, 0);
        for(int i = 0; i < ub->len; i++){
            if(i >= 2 && *cp == 0x1d){ // z
                *cp = 0x04; // a
            }
            cp++;
        }
    }

    return USB_HOOK_PASS;
}

void
usbhid_init_handle (struct usb_host *host, struct usb_device *dev)
{
    u8 class, protocol;
    int i;
    struct usb_interface_descriptor *ides;

    if (!dev || !dev->config || !dev->config->interface ||
        !dev->config->interface->altsetting ||
        !dev->config->interface->num_altsetting) {
        dprintft(1, "HID(%02x): interface descriptor not found.\n",
             dev->devnum);
        return;
    }
    for (i = 0; i < dev->config->interface->num_altsetting; i++) {
        ides = dev->config->interface->altsetting + i;
        class = ides->bInterfaceClass;
        protocol = ides->bInterfaceProtocol;
        if (class == USB_ICLASS_HID && protocol == USB_PROTOCOL_KEYBOARD)
            break;
    }

    if (i == dev->config->interface->num_altsetting)
        return;

    printf("HID(%02x): an USB keyboard found.\n", dev->devnum);

    spinlock_lock(&host->lock_hk);
    struct usb_endpoint_descriptor *epdesc;
    for(i = 1; i <= ides->bNumEndpoints; i++){
        epdesc = &ides->endpoint[i];
        if (epdesc->bEndpointAddress & USB_ENDPOINT_IN) {
            usb_hook_register(host, USB_HOOK_REPLY,
                      USB_HOOK_MATCH_DEV | USB_HOOK_MATCH_ENDP,
                      dev->devnum, epdesc->bEndpointAddress,
                      NULL, hid_intercept, dev, dev);
            printf("HID(%02x, %02x): HID device monitor registered.\n",
                    dev->devnum, epdesc->bEndpointAddress);
        }
    }
    spinlock_unlock(&host->lock_hk);

    return;
}
```

また`drivers/usb/xhci.c:xhci_new()`の末尾付近の適当なところで`usb_init_device_monitor()`を呼ぶようにします．

```c
static void
xhci_new (struct pci_device *pci_device){
    ...
    usb_init_device_monitor(host->usb_host);
    ...
}
```

## 結果
接続したUSBキーボードでzキーを押すとaが入力されます．

## 補足
BitVisorのusbドライバの動作について参考資料1.に大変分かり易い図があるのでそのまま引用します．

<img width="764" alt="bitvisor_usb.png" src="https://qiita-image-store.s3.amazonaws.com/0/153099/e2114ae0-3afe-1898-da8c-7db03d2a4253.png">


`USB_HOOK_REPLY`を引数として`usb_hook_register()`をすると上図の4のところでコールバック関数が呼ばれます．多分．
このとき`urb->shadow->buffers`にUSB通信でやりとりするパケットデータが格納されているようので，そこを直接いじっています．`mapmem_gphys()`していることから分かるようにゲストの領域を書き換えます．
USBキーボードのデータ構造に関して詳細は参考資料2.を見てください．
Endpoint 0は制御通信用なのでフックからは除外しています．xhci以外で動くのかは試してません．


## 参考資料
1. [xHCI Driver Implementation](https://bitvisor.org/summit5/slides/bitvisor-summit-5-4-ake.pdf)
2. [osdev - USB Human Interface Devices](http://wiki.osdev.org/USB_Human_Interface_Devices)
3. [USB Made Simple - Part 5 Typical Human Interface Device (HID)](http://www.usbmadesimple.co.uk/ums_5.htm)
4. [Intel xHCI manual](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf)