[Switch] vfio Overview (vfio / iommu / device passthrough)

Transfer from   http://element-ui.cn/news/show-44900.aspx

Article Directory

  • 1.IOMMU
  • 1.1 IOMMU Features
  • 1.2 IOMMU role
  • 1.3 IOMMU works
  • 1.4 Identify Source
  • 2.VFIO
  • 2.1 Concepts
  • 2.2 Example of use
  • 3. The device pass-through analysis
  • 3.1 Virtual Machine Address Mapping
  • 3.2 pass-through device to achieve

1.IOMMU

1.1 IOMMU Features

IOMMU Key features include DMA Remapping and Interrupt Remapping, here mainly on the DMA Remapping, Interrupt Remapping independence will explain. For DMA Remapping, IOMMU similar to the MMU. IOMMU device may access a memory address into an address, the figure illustrates the case IOMMU IOMMU effect against absence.

 

 


In the absence IOMMU case, the NIC receive data address translation process, the RC network card will request sent directly to the write address addr1 DDR controller, and then access the address addr1 DRAM, RC where the request address on the card without any conversion address card access must be a physical address.

 

 

For IOMMU case, the card will be requested write address addr1 addr2 IOMMU converted to, and then transmitted to the DDR controller, the last access address addr2 is a DRAM, the card will be accessible address addr1 IOMMU converted into real physical address addr2 herein may be understood as a virtual machine address addr1.

 

 

Left IOMMU is no case for the virtual machine can not achieve transparent transmission equipment, for two main reasons: first, because in the absence of IOMMU, the device must access the actual physical address HPA, while the virtual machine is visible GPA ; if the second is to make the virtual machine to fill a real HPA, in that case the equivalent of a virtual machine direct access to a physical address, there will be a security risk. So for the IOMMU no circumstances, can not pass through the way for direct access devices will have to take over the VMM, it will not be exposed to the virtual machine HPA.

右图是有IOMMU的情况,虚机可以将GPA直接写入到设备,当设备进行DMA传输时,设备请求地址GPA由IOMMU转换为HPA(硬件自动完成),进而DMA操作真实的物理空间。IOMMU的映射关系是由VMM维护的,HPA对虚机不可见,保障了安全问题,利用IOMMU可实现设备的透传。这里先留一个问题,既然IOMMU可以将设备访问地址映射成真实的物理地址,那么对于右图中的Device A和Device B,IOMMU必须保证两个设备映射后的物理空间不能存在交集,否则两个虚机可以相互干扰,这和IOMMU的映射原理有关,后面会详细介绍。

1.2 IOMMU作用

根据上一节内容,总结IOMMU主要作用如下:

  • 屏蔽物理地址,起到保护作用。典型应用包括两个:一是实现用户态驱动,由于IOMMU的映射功能,使HPA对用户空间不可见,在vfio部分还会举例。二是将设备透传给虚机,使HPA对虚机不可见,并将GPA映射为HPA
  • IOMMU可以将连续的虚拟地址映射到不连续的多个物理内存片段,这部分功能于MMU类似,对于没有IOMMU的情况,设备访问的物理空间必须是连续的,IOMMU可有效的解决这个问题

1.3 IOMMU工作原理

前面简单介绍了IOMMU的映射功能,下面讲述IOMMU到底如何实现映射的,为便于分析,这里先不考虑虚拟化的场景,以下图为例,阐述工作原理。

 

 

 

IOMMU的主要功能就是完成映射,类比MMU利用页表实现VA->PA的映射,IOMMU也需要用到页表,那么下一个问题就是如何找到页表。在设备发起DMA请求时,会将自己的Source Identifier(包含Bus、Device、Func)包含在请求中,IOMMU根据这个标识,以RTADDR_REG指向空间为基地址,然后利用Bus、Device、Func在Context Table中找到对应的Context Entry,即页表首地址,然后利用页表即可将设备请求的虚拟地址翻译成物理地址。这里做以下说明:

  • 图中红线的部门,是两个Context Entry指向了同一个页表。这种情况在虚拟化场景中的典型用法就是这两个Context Entry对应的不同PCIe设备属于同一个虚机,那样IOMMU在将GPA->HPA过程中要遵循同一规则
  • 由图中可知,每个具有Source Identifier(包含Bus、Device、Func)的设备都会具有一个Context Entry。如果不这样做,所有设备共用同一个页表,隶属于不同虚机的不同GPA就会翻译成相同HPA,会产生问题,

有了页表之后,就可以按照MMU那样进行地址映射工作了,这里也支持不同页大小的映射,包括4KB、2MB、1GB,不同页大小对应的级数也不同,下图以4KB页大小为例说明,映射过程和MMU类似,不再详细阐述。

 

 

 

1.4 Source Identifier

在讲述IOMMU的工作原理时,讲到了设备利用自己的Source Identifier(包含Bus、Device、Func)来找到页表项来完成地址映射,不过存在下面几个特殊情况需要考虑。

  • 对于由PCIe switch扩展出的PCI桥及桥下设备,在发送DMA请求时,Source Identifier是PCIe switch的,这样的话该PCI桥及桥下所有设备都会使用PCIe switch的Source Identifier去定位Context Entry,找到的页表也是同一个,如果将这个PCI桥下的不同设备分给不同虚机,由于会使用同一份页表,这样会产生问题,针对这种情况,当前PCI桥及桥下的所有设备必须分配给同一个虚机,这就是VFIO中组的概念,下面会再讲到。
  • 对于SRIO-V,之前介绍过VFBusdevfn的计算方法,所以不同VF会有不同的Source Identifier,映射到不同虚机也是没有问题的

2.VFIO

VFIO就是内核针对IOMMU提供的软件框架,支持DMA Remapping和Interrupt Remapping,这里只讲DMA Remapping。VFIO利用IOMMU这个特性,可以屏蔽物理地址对上层的可见性,可以用来开发用户态驱动,也可以实现设备透传。

2.1 概念介绍

先介绍VFIO中的几个重要概念,主要包括Group和Container。

Group:group 是IOMMU能够进行DMA隔离的最小硬件单元,一个group内可能只有一个device,也可能有多个device,这取决于物理平台上硬件的IOMMU拓扑结构。 设备直通的时候一个group里面的设备必须都直通给一个虚拟机。 不能够让一个group里的多个device分别从属于2个不同的VM,也不允许部分device在host上而另一部分被分配到guest里, 因为就这样一个guest中的device可以利用DMA攻击获取另外一个guest里的数据,就无法做到物理上的DMA隔离。

Container:对于虚机,Container 这里可以简单理解为一个VM Domain的物理内存空间。对于用户态驱动,Container可以是多个Group的集合。

 

 

 

上图中PCIe-PCI桥下的两个设备,在发送DMA请求时,PCIe-PCI桥会为下面两个设备生成Source Identifier,其中Bus域为红色总线号bus,device和func域为0。这样的话,PCIe-PCI桥下的两个设备会找到同一个Context Entry和同一份页表,所以这两个设备不能分别给两个虚机使用,这两个设备就属于一个Group。

2.2 使用示例

这里先以简单的用户态驱动为例,在设备透传小节中,在分析如何利用vfio实现透传。

 1 int container, group, device, i;
 2         struct vfio_group_status group_status =
 3                                           { .argsz = sizeof(group_status) };
 4         struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) };
 5         struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) };
 6         struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
 7  
 8         /* Create a new container */
 9         container = open("/dev/vfio/vfio", O_RDWR);
10  
11         if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
12                  /* Unknown API version */
13  
14         if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU))
15                  /* Doesn't support the IOMMU driver we want. */
16  
17         /* Open the group */
18         group = open("/dev/vfio/26", O_RDWR);
19  
20         /* Test the group is viable and available */
21         ioctl(group, VFIO_GROUP_GET_STATUS, &group_status);
22  
23         if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
24                  /* Group is not viable (ie, not all devices bound for vfio) */
25  
26         /* Add the group to the container */
27         ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
28  
29         /* Enable the IOMMU model we want */   // type 1 open | attatch
30         ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
31  
32         /* Get addition IOMMU info */
33         ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
34  
35         /* Allocate some space and setup a DMA mapping */
36         dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,
37                               MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
38         dma_map.size = 1024 * 1024;
39         dma_map.iova = 0; /* 1MB starting at 0x0 from device view */
40         dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
41  
42         ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
43  
44         /* Get a file descriptor for the device */
45         device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0");
46  
47         /* Test and setup the device */
48         ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);

 

对于dev下Group就是按照上一节介绍的Group划分规则产生的,上述代码描述了如何使用VFIO实现映射,对于Group和Container的相关操作这里不做过多解释,主要关注如何完成映射,下图解释具体工作流程。

 

 

 

首先,利用mmap映射出1MB字节的虚拟空间,因为物理地址对于用户态不可见,只能通过虚拟地址访问物理空间。然后执行ioctl的VFIO_IOMMU_MAP_DMA命令,传入参数主要包含vaddr及iova,其中iova代表的是设备发起DMA请求时要访问的地址,也就是IOMMU映射前的地址,vaddr就是mmap的地址。VFIO_IOMMU_MAP_DMA命令会为虚拟地址vaddr找到物理页并pin住(因为设备DMA是异步的,随时可能发生,物理页面不能交换出去),然后找到Group对应的Contex Entry,建立页表项,页表项能够将iova地址映射成上面pin住的物理页对应的物理地址上去,这样对用户态程序完全屏蔽了物理地址,实现了用户空间驱动。IOVA地址的00x100000对应DRAM地址0x100000000x10100000,size为1024 * 1024。一句话概述,VFIO_IOMMU_MAP_DMA这个命令就是将iova通过IOMMU映射到vaddr对应的物理地址上去。

3.设备透传分析

设备透传就是由虚机直接接管设备,虚机可以直接访问MMIO空间,VMM配置好IOMMU之后,设备DMA读写请求也无需VMM借入,需要注意的是设备的配置空间没有透传,因为VMM已经配置好了BAR空间,如果将这部分空间也透传给虚机,虚机会对BAR空间再次配置,会导致设备无法正常工作。

3.1 虚机地址映射

在介绍透传之前,先看下虚机的GPA与HVA和HPA的关系,以及虚机是如何访问到真实的物理地址的,过程如下图。

 

 

 

一旦页表建立好后,整个映射过程都是硬件自动完成的,对于上图有如下几点说明:

  • 对于虚机内的页表,完成GVA到GPA的映射,虽然整个过程都是硬件自动完成,但有一点要注意下,在虚机的中各级页表也是存储在HPA中的,而CR3及各级页表中装的地址都是GPA,所以在访问页表时也需要借助EPT,上图中以虚线表示这个过程
  • 利用虚机页表完成GVA到GPA的映射后,此时借助EPT实现GPA到HPA的映射,这里没有什么特殊的,就是一层层页表映射
  • 看完上图,有没有发现少了点啥,是不是没有HVA。单从上图整个虚机寻址的映射过程来看,是不需要HVA借助的,硬件会自动完成GVA->GPA->HPA映射,那么HVA有什么用呢?这里从下面两方面来分析:1)Qemu利用iotcl控制KVM实现EPT的映射,映射的过程中必然要申请物理页面。Qemu是应用程序,唯一可见的只是HVA,这时候又需要借助mmap了,Qemu会根据虚机的ram大小,即GPA大小范围,然后mmap出与之对应的大小,即HVA。通过KVM_SET_USER_MEMORY_REGION命令控制KVM,与这个命令一起传入的参数主要包括两个值,guest_phys_addr代表虚机GPA地址起始,userspace_addr代表上面mmap得到的首地址(HVA)。传入进去后,KVM就会为当前虚机GPA建立EPT映射表实现GPA->HPA,同时会为VMM建立HVA->HPA映射。2)当vm_exit发生时,VMM需要对异常进行处理,异常发生时VMM能够获取到GPA,有时VMM需要访问虚机GPA对应的HPA,VMM的映射和虚机的映射方式不同,是通过VMM完成HVA->HPA,且只能通过HVA才能访问HPA,这就需要VMM将GPA及HVA的对应关系维护起来,这个关系是Qemu维护的,这里先不管Qemu的具体实现(后面会有专门文档介绍),当前只需要知道给定一个虚机的GPA,虚机就能获取到GPA对应的HVA。下图描述VMM与VM的地址映射关系。

 

 

 

3.2 设备透传实现

在前面介绍VFIO的使用实例时,核心思想就是IOVA经过IOMMU映射出的物理地址与HVA经过MMU映射出的物理地址是同一个。对于设备透传的情况,先上图,然后看图说话。

 

 

 

先来分析一下设备的DMA透传的工作流程,一旦设备透传给了虚机,虚机在配置设备DMA时直接使用GPA。此时GPA经由EPT会映射成HPA1,GPA经由IOMMU映射的地址为HPA2,此时的HPA1和HPA2必须相等,设备的透传才有意义。下面介绍在配置IOMMU时如何保证HPA1和HPA2相等,在VFIO章节讲到了VFIO_IOMMU_MAP_DMA这个命令就是将iova通过IOMMU映射到vaddr对应的物理地址上去。对于IOMMU来讲,此时的GPA就是iova,我们知道GPA经由EPT会映射为HPA1,对于VMM来讲,这个HPA1对应的虚机地址为HVA,那样的话在传入VFIO_IOMMU_MAP_DMA命令时讲hva作为vaddr,IOMMU就会将GPA映射为HVA对应的物理地址及HPA1,即HPA1和HPA2相等。上述流程帮助理清整个映射关系,实际映射IOMMU的操作很简单,前面提到了qemu维护了GPA和HVA的关系,在映射IOMMU的时候也可以派上用场。注:IOMMU的映射在虚机启动时就已经建立好了,映射要涵盖整个GPA地址范围,同时虚机的HPA对应的物理页都不会交换出去(设备DMA交换是异步的)。

本文链接http://element-ui.cn/news/show-44900.aspx

 

 

Guess you like

Origin www.cnblogs.com/yi-mu-xi/p/12370626.html