在虚拟机中访问USB设备是非常常见的需求。我们可以从USB基础知识入手,加上虚拟化的基本概念,便可掌握USB虚拟化的方法。
理解USB虚拟化首先得具备一些基础的USB知识,我们可以从硬件角度和软件角度建立基本的USB的概念。
从硬件架构示意图上我们可以看出USB的系统组成、硬件拓扑结构。USB的关键组成一个是挂在在PCI总线上的USB主机控制器,另一个就是可热插拔的USB设备。USB主机控制器有不同的版本,分别是UHCI/OHCI对应USB1.1,EHCI对应USB2.0,XHCI对应USB 3.0。对于USB的虚拟化,要添加虚拟的USB设备,我们首先需要给虚拟机添加USB主机控制器。XHCI向前兼容USB1.1和USB2.0的设备,在2010年以后的系统中都支持XHCI,所以如果客户机操作系统如果是2010以后的系统,推荐使用XHCI。另外由于有些老的系统不支持XHCI,我们需要创建UHCI或EHCI以兼容老的Guest系统。
基于硬件之上的USB软件栈,内核部分主要包括USB总线驱动和USB设备驱动,USB总线驱动负责驱动USB主机控制器,用于主机和设备间的数据传输。基于USB总线驱动,USB设备驱动是对设备命令的封装,控制USB设备按照命令进行工作。用户空间的USB应用程序利用文件系统接口调用USB设备驱动实现设备的读写控制。
虚拟化就是将物理机上的CPU、内存、存储、网络等资源以虚拟化的方式让多个虚拟机共享。我们先介绍虚拟化的基本架构,然后再看看在虚拟化环境下虚拟机如何实现对USB的访问。
一个虚拟化系统由硬件平台、虚拟机管理器(Hypervisor)和若干个虚拟机组成。所有的硬件资源由虚拟机管理器统一掌握,由它向上呈现出一组虚拟的CPU、内存及其他相关资源给虚拟机,并实现虚拟机之间的相互隔离,另外提供全局资源的保护。
虚拟机对设备的访问是透过一种前后端分离模型来实现的。
虚拟机管理器里面通过程序一个虚拟的设备给虚拟机.在虚拟机内部,应用程序按照传统的方式对设备发起访问。虚拟机内部的驱动称为前端驱动。来自虚拟机的对设备的访问都会被虚拟机管理器拦截,然后交由由虚拟机管理器的后端驱动去执行。而后端驱动本质上是调用主机上的本地驱动程序去完成对物理设备的访问。
具体到USB设备的虚拟化分两种形式,一种是让虚拟机访问主机本地的USB设备,这种叫做USB虚拟化;一种是在虚拟机访问位于远端的USB设备,这种叫做USB重定向。
USB虚拟机又主要有两种方式,一种是通过将USB主机控制器透传给虚拟机,一种是将USB端口透传给虚拟机。
这种形式是通过系统的VFIO框架和IOMMU硬件实现USB主机控制器的透传,这样在虚拟机里面就可以不受虚拟机管理器的拦截而直接访问物理的USB主机控制器。在物理的主机管理器动态插拔USB设备也都能被虚拟机直接感知到。但是这样透传整个USB主机控制器的只能被一个虚拟机所使用,主机和其他虚拟机都无法使用其下所挂载的USB设备,缺乏灵活性。
基于USB端口的透传允许仅仅透传指定的USB端口给虚拟机,这样挂载在同一主机控制器下的不同的USB设备也可以分配给不同的虚拟机。
USB重定向通过网络协议将USB总线拉远,在客户端我们安装一个组件用于监听本地设备的USB设备接入、移除等状态信息,同时实现USB端口或着USB设备的重定向,将USB设备的相应操作通过截获,封装成行相应格式用过网络将其传输到远端的主机服务器,同时接收远端主机服务器发送回来的数据,并进行相应处理。
编译时根据需要打开相应的编译开关
“CONFIG_USB_OHCI=y”
“CONFIG_USB_UHCI=y”
“CONFIG_USB_EHCI=y”
“CONFIG_USB_XHCI=y”
虚拟USB3.0主机控制器: -device qemu-xhci
虚拟USB2.0主机控制器: -device usb-ehci,
虚拟USB1.1主机控制器: -usb
比如透传BDF为0000:02:00.0的主机控制器: -device vfio-pci,host=0000:02:00.0,id=hostdev0
根据USB的总线和端口地址透传USB端口: -device usb-host,hostbus=bus,hostaddr=addr
根据USB设备的PID/VID透传USB端口: -device usb-host,vendorid=vendor,productid=product
根据USB的总线和端口号进行USB透传: -device usb-host,hostbus=bus,hostport=port
创建一个spice虚拟机通道:-chardev spicevmc,id=charredir0,name=usbredir
创建一个USB重定向设备:-device usb-redir,chardev=charredir0,id=redir0,bus=usb.0,port=2
根据需要可以创建多个USB重定向设备:
-chardev spicevmc,id=charredir1,name=usbredir
-device usb-redir,chardev=charredir1,id=redir1,bus=usb.0,port=3
…
在Qemu的Monitor控制台上可以进行查询主机上和客户机上的USB设备:
在Qemu的Monitor控制台上可以进行USB设备的动态热插拔: