介绍过IOMMU是提供DMA Remapping功能的硬件模块,可以把DMA地址从虚拟地址翻译成物理地址。Linux kernel有两个引导参数(boot parameter)与iommu有关:iommu=[on/off] 和 intel_iommu=[on/off],它们有什么区别呢?答案是:参数iommu控制的是GART iommu(AMD)功能,参数intel_iommu控制的是基于Intel VT-d的iommu(Intel)功能。  

bios在哪开启iommu bios里的iommu在哪里_sed

下面的代码表明:CONFIG_IOMMU控制的是GART iommu(AMD),CONFIG_DMAR控制的是intel_iommu(Intel)。(CONFIG_IOMMU对应的是引导参数iommu,CONFIG_DMAR对应的是引导参数intel_iommu,注意:CONFIG_DMAR名称中没有Intel字样,这里比较容易误导,但你看它下面对应的函数intel_iommu_init就很明显了):

负责DMA remapping操作的硬件称为IOMMU。做个类比:大家都知道MMU是支持内存地址虚拟化的硬件,MMU是为CPU服务的;而IOMMU是为I/O设备服务的,是将DMA地址进行虚拟化的硬件。

arch/x86_64/kernel/pci-dma.c:
 
0331 static int __init pci_iommu_init(void)
0332 {
0333 #ifdef CONFIG_CALGARY_IOMMU
0334         calgary_iommu_init();
0335 #endif
0336 
0337 #ifdef CONFIG_DMAR
0338         intel_iommu_init();
0339 #endif
0340 
0341 #ifdef CONFIG_AMD_IOMMU
0342         amd_iommu_init();
0343 #endif
0344 
0345 #ifdef CONFIG_IOMMU
0346         gart_iommu_init();
0347 #endif
0348 
0349         no_iommu_init();
0350         return 0;
0351 }

引导参数iommu控制的是GART iommu前文(《DMAR(DMA remapping)与 IOMMU》)介绍过GART (Graphics Address Remapping Table),最初是为了方便图形芯片直接读取内存而设计的:使用地址转译功能将收集到内存中的数据映射到一个图形芯片可以“看”到的地址。这个地址转译功能自然也可以充当IOMMU,于是GART被Linux kernel用来帮助传统的32位PCI设备访问可寻址范围之外的内存区域。GART iommu有局限性(比如仅限于显存范围内),不具备Intel IOMMU的完整功能。

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

“iommu”参数默认是打开的,以2.6.18 kernel为例,

configs/kernel-2.6.18-x86_64.config:
...
0187 CONFIG_IOMMU=y
...

注:GART iommu功能是按需激活的,并有前提条件,比如系统内存必须在3GB以上、而且只对有限的设备,参见:

arch/x86_64/Kconfig:
...
0481 config IOMMU
0482         bool "IOMMU support" if EMBEDDED
0483         default y
0484         select SWIOTLB
0485         select AGP
0486         depends on PCI && !X86_64_XEN
0487         help
0488           Support for full DMA access of devices with 32bit memory access only
0489           on systems with more than 3GB. This is usually needed for USB,
0490           sound, many IDE/SATA chipsets and some other devices.
0491           Provides a driver for the AMD Athlon64/Opteron/Turion/Sempron GART
0492           based IOMMU and a software bounce buffer based IOMMU used on Intel
0493           systems and as fallback.
0494           The code is only active when needed (enough memory and limited
0495           device) unless CONFIG_IOMMU_DEBUG or iommu=force is specified
0496           too.
...

引导参数intel_iommu控制的是基于Intel VT-d的iommu,该参数默认是关闭的,在config文件中对应的配置如下(注意:名称中用的是DMAR而不是iommu,不留意的话容易错过):

configs/kernel-2.6.18-x86_64.config
...
0303 # CONFIG_DMAR_DEFAULT_ON is not set
...

从以下的代码中我们看到:[默认情况]与[显式设置intel_iommu=off]的效果是一样的,结果都是”dmar_disabled=1″,所以 intel_iommu=off 设不设置其实都一样:

drivers/pci/intel-iommu.c:
 
0330 #ifdef CONFIG_DMAR_DEFAULT_ON
0331 int dmar_disabled = 0;
0332 #else
0333 int dmar_disabled = 1;
0334 #endif /*CONFIG_DMAR_DEFAULT_ON*/
 
0346 static int __init intel_iommu_setup(char *str)
0347 {
0348         if (!str)
0349                 return -EINVAL;
0350         while (*str) {
0351                 if (!strncmp(str, "on", 2)) {
0352                         dmar_disabled = 0;
0353                         printk(KERN_INFO "Intel-IOMMU: enabled\n");
0354                 } else if (!strncmp(str, "off", 3)) {
0355                         dmar_disabled = 1;
0356                         printk(KERN_INFO "Intel-IOMMU: disabled\n");
0357                 } else if (!strncmp(str, "igfx_off", 8)) {
...

注:启动参数intel_iommu有点复杂的是:存在两个与DMAR有关的配置,除了上面看到的CONFIG_DMAR_DEFAULT_ON之外,另外还有一个是CONFIG_DMAR,默认是打开的:

configs/kernel-2.6.18-x86_64.config
...
0302 CONFIG_DMAR=y
0303 # CONFIG_DMAR_DEFAULT_ON is not set
...

CONFIG_DMAR告诉内核在编译时要准备好支持DMA Remapping设备(见注一);而CONFIG_DMAR_DEFAULT_ON 是告诉内核在引导时是否激活DMAR设备。也就是说,默认情况下(CONFIG_DMAR=y)内核已经具备了支持DMAR的功能,设置内核引导参数intel_iommu=off(等效于缺省情况:即CONFIG_DMAR_DEFAULT_ON未设置)并不是关闭内核的DMAR功能,仅仅是告诉内核在引导过程中不要把DMAR设备激活而已。这两个配置参数的详细解释参见以下文件。

arch/x86_64/Kconfig:
...
0703 config DMAR
0704         bool "Support for DMA Remapping Devices (EXPERIMENTAL)"
0705         depends on X86_64 && PCI_MSI && ACPI && EXPERIMENTAL && !XEN
0706         help
0707           DMA remapping (DMAR) devices support enables independent address
0708           translations for Direct Memory Access (DMA) from devices.
0709           These DMA remapping devices are reported via ACPI tables
0710           and include PCI device scope covered by these DMA
0711           remapping devices.
0712 
0713 config DMAR_DEFAULT_ON
0714         def_bool n
0715         prompt "Enable DMA Remapping Devices by default"
0716         depends on DMAR
0717         help
0718           Selecting this option will enable a DMAR device at boot time if
0719           one is found. If this option is not selected, DMAR support can
0720           be enabled by passing intel_iommu=on to the kernel. It is
0721           recommended you say N here while the DMAR code remains
0722           experimental.
...

(注一)事实上更准确地说,内核不仅仅是在编译时具备了支持DMAR设备的功能,而且在引导过程中始终会根据ACPI table把DMAR设备有关的数据结构都初始化好–无论是否加了引导参数intel_iommu=off。

内核怎么知道哪些设备需要DMA Remapping呢?是通过ACPI table知道的,因为需要DMA Remapping的设备必须在firmware/BIOS中登记。以下是内核初始化DMAR的代码,可以看到无论是否dmar_disabled,都会调用dmar_table_initdmar_dev_scope_init

drivers/pci/intel-iommu.c:
 
3317 int __init intel_iommu_init(void)
3318 {
3319         int ret = 0;
3320 
3321         if (dmar_table_init())
3322                 return  -ENODEV;
3323 
3324         if (dmar_dev_scope_init())
3325                 return  -ENODEV;
3326 
3327         /*
3328          * Check the need for DMA-remapping initialization now.
3329          * Above initialization will also be used by Interrupt-remapping.
3330          */
3331         if (no_iommu || swiotlb || dmar_disabled)
3332                 return -ENODEV;
3333 
...

这就是为什么只要BIOS中打开了Intel VT-d,我们就总会在kernel messages中看到类似下面的初始化DMAR table的信息,无论intel_iommu参数是on还是off。

...
DMAR:Host address width 46
DMAR:DRHD base: 0x000000efefe000 flags: 0x0
IOMMU efefe000: Number of IOMMU domains reduced from 64K to 4K
IOMMU efefe000: ver 1:0 cap d2078c106f0464 ecap f020de
DMAR:DRHD base: 0x000000dcffe000 flags: 0x1
IOMMU dcffe000: Number of IOMMU domains reduced from 64K to 4K
IOMMU dcffe000: ver 1:0 cap d2078c106f0464 ecap f020de
DMAR:RMRR base: 0x000000bdffd000 end: 0x000000bdffffff
DMAR:RMRR base: 0x000000bdff6000 end: 0x000000bdffcfff
DMAR:RMRR base: 0x000000bdf83000 end: 0x000000bdf84fff
...

如果你想让kernel中与Intel VT-d有关的软件模块完全关闭,仅仅使用启动参数intel_iommu=off是不够的,而必须重新编译内核–在config中配置CONFIG_DMAR=n,或者用另一种方法:在BIOS中关闭Intel VT-d