dmar 与 IOMMU

在分析Linux kernel dump的时候经常会看到一个叫做dmar的东西,查看中断信息的时候也时常见到一个名称为dmar0的设备,到底什么是dmar呢?

大家知道,I/O设备可以直接存取内存,称为DMA(Direct Memory Access);DMA要存取的内存地址称为DMA地址(也可称为BUS address)。在DMA技术刚出现的时候,DMA地址都是物理内存地址,简单直接,但缺点是不灵活,比如要求物理内存必须是连续的一整块而且不能是高位地址等等,也不能充分满足虚拟机的需要。后来dmar就出现了。 dmar意为DMA remapping,是Intel为支持虚拟机而设计的I/O虚拟化技术,I/O设备访问的DMA地址不再是物理内存地址,而要通过DMA remapping硬件进行转译,DMA remapping硬件会把DMA地址翻译成物理内存地址,并检查访问权限等等。负责DMA remapping操作的硬件称为IOMMU。做个类比:大家都知道MMU是支持内存地址虚拟化的硬件,MMU是为CPU服务的;而IOMMU是为I/O设备服务的,是将DMA地址进行虚拟化的硬件。   IOMMUIOMMA不仅将DMA地址虚拟化,还起到隔离、保护等作用,如下图所示意,详细请参阅Intel Virtualization Technology for Directed I/O DMA remapping现在我们知道了dmar的概念,那么Linux中断信息中出现的dmar0又是什么呢? 还是用MMU作类比吧,便于理解:当CPU访问一个在地址翻译表中不存在的地址时,就会触发一个fault,Linux kernel的fault处理例程会判断这是合法地址还是非法地址,如果是合法地址,就分配相应的物理内存页面并建立从物理地址到虚拟地址的翻译表项,如果是非法地址,就给进程发个signal,产生core dump。IOMMU也类似,当I/O设备进行DMA访问也可能触发fault,有些fault是recoverable的,有些是non-recoverable的,这些fault都需要Linux kernel进行处理,所以IOMMU就利用中断(interrupt)的方式呼唤内核,这就是我们在/proc/interrupts中看到的dmar0那一行的意思。 我们看到的中断号48,据此还可以进一步发掘更多的信息:

上面最有意思的信息是ACTION的handler,表示IOMMU发生fault之后的中断处理例程,我们看到的例程名是dmar_fault,源代码如下:

请注意行号1343,dmar_fault_do_one()会报告fault的具体信息,包括对应设备的物理位置。由于一个dmar对应着很多个I/O设备,这条信息可以帮助定位到具体哪一个设备。源代码如下:

 

dmar的初始化是kernel根据ACPI中的dmar table进行的,每一个表项对应一个dmar设备,名称从dmar0开始依次递增,涉及取名的代码如下:

上面也揭示了boot过程留在dmesg中信息的来历:

附注: 过去的AMD64芯片也提供一个功能有限的地址转译模块——GART (Graphics Address Remapping Table),有时候它也可以充当IOMMU,这导致了人们对GART和新的IOMMU的混淆。最初设计GART是为了方便图形芯片直接读取内存:使用地址转译功能将收集到内存中的数据映射到一个图形芯片可以“看”到的地址。后来GART被Linux kernel用来帮助传统的32位PCI设备访问可寻址范围之外的内存区域。这件事新的IOMMU当然也可以做到,而且没有GART的局限性(它仅限于显存的范围之内),IOMMU可以将I/O设备的任何DMA地址转换为物理内存地址。


参考资料:
Linux Kernel的Intel-IOMMU.txt
Intel’s Virtualization for Directed I/O (a.k.a IOMMU)
Wikipedia IOMMU
理解IOMMU、北桥、MMIO和ioremap
Intel Virtualization Technology for Directed I/O