BitVisorのドライバのコード量のおよそ半分を占めるのにもかかわらずほとんど情報がないusbドライバを触ってみようと思います.USBのことは良く分からないので簡単そうなUSBキーボードのフックをしてみます.
前準備
USBキーボードを接続してどこに繋がっているか確認します.
% 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ドライバを読み込ませるようにします.
.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
を以下のように書き換えます.
#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()
を呼ぶようにします.
static void
xhci_new (struct pci_device *pci_device){
...
usb_init_device_monitor(host->usb_host);
...
}
結果
接続したUSBキーボードでzキーを押すとaが入力されます.
補足
BitVisorのusbドライバの動作について参考資料1.に大変分かり易い図があるのでそのまま引用します.
USB_HOOK_REPLY
を引数としてusb_hook_register()
をすると上図の4のところでコールバック関数が呼ばれます.多分.
このときurb->shadow->buffers
にUSB通信でやりとりするパケットデータが格納されているようので,そこを直接いじっています.mapmem_gphys()
していることから分かるようにゲストの領域を書き換えます.
USBキーボードのデータ構造に関して詳細は参考資料2.を見てください.
Endpoint 0は制御通信用なのでフックからは除外しています.xhci以外で動くのかは試してません.