内核中有若干个iommu的模块,通过IOMMU_INIT组成成一个依赖树。detect_intel_iommu最先被调用,它被用来做sanity检查并设置好
x86_init.iommu.iommu_init = intel_iommu_init;
x86_platform.iommu_shutdown = intel_iommu_shutdown;
intel_iommu_init
|-> dmar_table_init -> parse_dmar_table -> dmar_walk_dmar_table
|-> dmar_dev_scope_init
|-> dmar_acpi_dev_scope_init -> dmar_acpi_insert_dev_scope
|-> dmar_pci_bus_add_dev -> dmar_insert_dev_scope
|-> bus_register_notifier
|-> dmar_init_reserved_ranges
|-> init_no_remapping_devices
|-> init_dmars
|-> dma_ops = &intel_dma_ops
|-> init_iommu_pm_ops
|-> iommu_device_sysfs_add, iommu_device_set_ops, iommu_device_register
|-> bus_set_iommu(&pci_bus_type, &intel_iommu_ops)
|-> bus_register_notifier(&pci_bus_type, &iommu_bus_notifier)
intel_iommu_init是intel iommu初始化的主函数,主要做下面的事:
1.建立三个cache,iova,dmar_domain,device_domain_info。
2.遍历dmar表下的5个子表,建立dmar_drhd_units,dmar_rmrr_units,dmar_atsr_units链表。设置drhd的node亲和性。
3.对andd声明的acpi namespace类型的设备添加到device scope数组中。数组位于dmar_drhd_units链表中。
4.遍历整个pci设备树并调用dmar_pci_bus_add_dev添加设备到dmar_drhd_units,dmar_rmrr_units,dmar_atsr_units链表的device scope数组。
5.注册pci热插拔回调函数dmar_pci_bus_notifier
6.初始化reserved_iova_list,里面放的是IOAPIC的空间,所有pci设备的MMIO空间。(LAPIC的空间不用考虑吗?如果iova建立到LAPIC空间是不是中断都发不出去了?)
7.init_no_remapping_devices忽略没有挂载任何设备的drhd,忽略只有gfx设备的drhd如果igfx_off
8.init_dmars首先对每一个没有被忽略的drhd初始化qi,分配domain_ids和domains指针表和root_entry页,初始化si_domain(si_domain中一一映射了内存页与rmrr保留页,它是干啥的?貌似是内核自用的?)。最后设置了dmar中断
9.设置dma_ops
10.设置suspend/resume回调函数,iommu硬件在唤醒时需要恢复某些寄存器
11.建立sysfs接口下iommu的属性文件
12.注册pci总线的iommu调用函数,iommu_bus_init设置热插拔设备时的回调函数(这个不同于5中的回调,5中的是dev scope的),并遍历当前已有设备调用iommu_probe_device,iommu_probe_device调用刚才注册的pci总线下iommu回调函数ops->add_device。具体是调用ops->device_group建立iommu_group,并给iommu_group分配default_domain,初始group->domain也指向default_domain,最后建立iommu_group与设备的绑定关系。(TBD:这里好多地方没看明白,每个group建立一个default_domain的意图是什么)
13.probe_acpi_namespace_devices把acpi namespace设备也建立iommu_group。
14.最后是能每个drhd硬件的translation功能
这里有好多容易混淆的结构,iommu_group,iommu_domain,dmar_domain。dmar_drhd_unit,intel_iommu,iommu_device。