在PCIe IOMMU 概述中,IOMMU虚拟化最重要的功能就是DMA重映射和中断重映射。中断重映射会将来自外部设备的中断(包括来自IOAPIC和PCIe设备的MSI/INTX等)拦截下来然后通过查询中断映射表的找到真正的中断路由信息然后发送给真正的CPU。中断重映射可以用来实现中断隔离和中断迁移从而达到提升虚拟化环境下设备的中断处理效率的目的。
在Intel的实现(VT-d),中断重映射是否支持取决于Extended Capability Register BIT3 IR 如果为1 表示支持中断重映射。
X86架构中断都是通过对物理地址0xFEEX_XXXXh的内存写请求(不仅仅MSI/MSIX是这样,IOAPIC产生的中断也是如此)。这个写请求的Address和Data的定义格式如下图所示:
VT-d spec定义了 5 张DMA Remapping Reporting (DMAR) ACPI table,对于DMA重映射,主要使用的是DRHD和RMRR这两张表格,这两张表中记录的设备的信息也是以B/D/F表示。
当Address的Bit4为‘0’时表示兼容模式,中断会直接传递给CPU不会被重映射,当Bit4为‘1’时就表示可重映射模式,这时的Address的格式会按照下图来解析:
在新的Address,Bits [19:5] 构成了Handle[14:0] Bits[2]构成了Handle[15],Bit 3 SHV (SubHandle Valid) 表示MSI支持单个地址多个数据的情况,当它 为1时,Data中的Bits[15:0]包含了Sub-Handle。这些值用于索引中断重映射表。
Interrupt Remap Table Address Register用来指向中断重映射表的位置。系统软件通常是OS/VMM需要program这个寄存器。
中断重映射表每个item大小为16Bytes叫做Interrupt Remap Table Entry(IRET)它包含了重映射之后的中断相关的Destination ID、Vector、Trigger Mode、Delivery Mode等信息。
当IOMMU硬件逻辑检测到0xFEEX_XXXXh的内存写请求之后,它就认为这是一个中断请求,然后检测中断重映射有没有开启,以及中断请求的格式是否是可重映射格式,如果都符合就进一步检测Address/Data中数据的有效性,然后算出interrupt_index并用它作为索引找到对应的IRTE,然后按照IRTE的设置产生一个新的传递给CPU的中断。
对于设备产生MSI/MSIX类型的中断我们比较容易理解怎么让它产生可重映射的中断,因为它的Address/Data的寄存器和上文提到的格式很像,我们只要把Bit4设置成1就是可重映射类型的中断了。
但是IOAPIC是没有类似的寄存器的,它有的是RedirectionTable,我们需要做的是把Redirection Table Entry里面的Interrupt Format设置成1,就表示是中断可以重映射。当IOAPIC的IRQx上收到中断信号,它会按照Redirection Table Entry里面的设置格式产生一个0xFEEX_XXXXh的内存写请求,当Redirection Table Entryd的Interrupt Format是1,那么这个内存写请求的Bit4 Interrupt Format也会是1,接下来的过程就和MSI/MSIX的可重映射的中断处理过程一样了。
参考:
-
《vt-directed-io-spec》
-
Intel VT-d(3)- 中断重映射: https://zhuanlan.zhihu.com/p/50785797
-
VT-d Interrupt Remapping: https://kernelgo.org/interrupt-remapping.html