IOMMU的一个主要作用就是将IO设备发出的请求地址IOVA(I/O Virtual Address)转化为物理内存地址,如果没有IOMMU,那么所有的IO设备都将使用相同的物理地址空间访问物理内存。引入IOMMU后,就会引入IOVA这个地址空间,IO设备可以通过IOVA虚拟地址访问物理内存。
在虚拟化引入之前,IOMMU主要有两个功能:
- 创建IOVA到HPA的映射,让设备能够访问任意物理内存。比如,有些I/O设备的寻址空间只有4G,但是平台的物理内存大于4G,为了让这样的I/O设备能够访问4G空间以上的内存,就需要建立一个IOVA -> HPA的映射。
- 通过IOVA -> HPA的映射,创建内存连续的DMA访问,提高系统性能。比如,内核分配2个不相邻的4KB内存页,若没有IOMMU,需要建立两次DMA操作才能访问到这两个内存页,若有IOMMU,则可以将这两个不相邻的物理内存页映射到相邻的IOVA上,这样设备只需要建立一次DMA操作,即可访问到这两个不相邻的物理内存页。
除了以上功能,虚拟化对IOMMU的主要应用就是对设备的隔离。在引入PCIe之前,传统的PCI总线对设备的隔离是很难实现的,因为传统的PCI总线采用的是总线仲裁机制,同一时间只有一个PCI设备独占整个PCI总线,设备发出的TLP包不包含requester ID,导致RC(Root Complex)或者其他接收设备无法分辨出接收到的TLP包来自哪个设备,也就无法达到设备区分和隔离的目的。虽然PCI-X在一定程度上引入了requester ID,但是有些规则还是不够完善,还做不到完全的隔离。
对于PCIe架构而言,PCIe设备发出的所有TLP包都会包含一个requester ID(即PCI设备的Bus、Device和Function Number),这个ID可以唯一地辨别一个PCI设备,TLP的接收设备可以使用这个ID来查找IOVA的地址转换页表,这样PCIe设备就可以使用虚拟地址(IOVA)访问物理内存。这时候,对于透传给虚拟机的PCI设备,软件需要做的就是将PCIe设备所在的虚拟机的虚拟机物理地址GPA(Guest Physical Address)到主机物理地址HPA(Host Physical Address)的映射告知IOMMU,当PCIe设备发生DMA的时候,