前文介绍过IOMMU是提供DMA Remapping功能的硬件模块,可以把DMA地址从虚拟地址翻译成物理地址。Linux kernel有两个引导参数(boot parameter)与iommu有关:iommu=[on/off] 和 intel_iommu=[on/off],它们有什么区别呢?答案是:参数iommu控制的是GART iommu功能,参数intel_iommu控制的是基于Intel VT-d的iommu功能。
下面的代码表明:CONFIG_IOMMU控制的是GART iommu,CONFIG_DMAR控制的是intel_iommu。(CONFIG_IOMMU对应的是引导参数iommu,CONFIG_DMAR对应的是引导参数intel_iommu,注意:CONFIG_DMAR名称中没有Intel字样,这里比较容易误导,但你看它下面对应的函数intel_iommu_init就很明显了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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,前文介绍过GART (Graphics Address Remapping Table),最初是为了方便图形芯片直接读取内存而设计的:使用地址转译功能将收集到内存中的数据映射到一个图形芯片可以“看”到的地址。这个地址转译功能自然也可以充当IOMMU,于是GART被Linux kernel用来帮助传统的32位PCI设备访问可寻址范围之外的内存区域。GART iommu有局限性(比如仅限于显存范围内),不具备Intel IOMMU的完整功能。
“iommu”参数默认是打开的,以2.6.18 kernel为例,
1 2 3 4 |
configs/kernel-2.6.18-x86_64.config: ... 0187 CONFIG_IOMMU=y ... |
注:GART iommu功能是按需激活的,并有前提条件,比如系统内存必须在3GB以上、而且只对有限的设备,参见:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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,不留意的话容易错过):
1 2 3 4 |
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 设不设置其实都一样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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,默认是打开的:
1 2 3 4 5 |
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设备激活而已。
这两个配置参数的详细解释参见以下文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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_init和dmar_dev_scope_init:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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。
1 2 3 4 5 6 7 8 9 10 11 12 |
... 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。