4.设备虚拟化-11.INTEL-IOMMU源码分析-《计算机知识》

admin 2025-11-02 22:28:35 系统网络 来源:ZONE.CI 全球网 0 阅读模式
  • IOMMU源码分析
    • 相关参考
    • 相关类图
    • 本文目标
    • 基础知识
      • 关于DMAR的相关寄存器
    • 代码汇总
      • IOMMU_TABLE
      • 入口部分
      • 初始化部分
        • dmar_table_init-解析DMAR的ACPI表
        • dmar_dev_scope_init(可以跳过)
        • dmar_init_reserved_ranges
        • init_dmars() 重点函数
        • set_ops
        • 初始化操作接口
        • dma_ops = &intel_dma_ops;
        • bus_set_iommu
        • add_iommu_group(重点难点 )
  • 附录
    • 附录1 Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz的ACPI DMAR

    IOMMU源码分析

    相关参考

    • Intel IOMMU Introduction
    • DMAR表组织结构
    • Intel® Virtualization Technology for Directed I/O (官方文档)
    • Intel-IOMMU.txt 内核文档
    • iommu & intel-iommu实现 和 intel IOMMU driver analysis

    相关类图

    IOMMU图表

    本文目标

    • 理解Intel下IOMMU实现的原理,配合Intel® Virtualization Technology for Directed I/O 的代码实现。
    • 设备是如何分到各个IOMMU_GROUP上的?
    • 有了IOMMU的后,DMA操作的接口发生了什么概变?
    • 解答 Intel IOMMU Introduction 中提到的问题。
    • 有了IOMMU后,中断的处理是如何分发和获取的?

    基础知识

    关于DMAR的相关寄存器

    在学习 10th Generation Intel® Core™ Processors Datasheet Volume 2 of 2 中我们了解了地址表信息

    1. baiy@baiy-ThinkPad-E470c:testacpi$ sudo cat /proc/iomem # 参考
    2. ......
    3. fed90000-fed90fff : dmar0
    4. fed91000-fed91fff : dmar1
    5. fee00000-fee00fff : Local APIC
    6. .....
    7. baiy@baiy-ThinkPad-E470c:testacpi$ cat dmar.dsl # 参考VT-d 第8章
    8. ......
    9. [030h 0048 2] Subtable Type : 0000 [Hardware Unit Definition]
    10. [032h 0050 2] Length : 0018
    11. [034h 0052 1] Flags : 00
    12. [035h 0053 1] Reserved : 00
    13. [036h 0054 2] PCI Segment Number : 0000 ### PCI的domain地址
    14. [038h 0056 8] Register Base Address : 00000000FED90000
    15. ......
    16. [048h 0072 2] Subtable Type : 0000 [Hardware Unit Definition]
    17. [04Ah 0074 2] Length : 0020
    18. [04Ch 0076 1] Flags : 01
    19. [04Dh 0077 1] Reserved : 00
    20. [04Eh 0078 2] PCI Segment Number : 0000 ### PCI的domain地址
    21. [050h 0080 8] Register Base Address : 00000000FED91000 # 参考VT-d 10章
    22. ......

    中间有部分存储了dmar的地址(这里是00000000FED90000 和 00000000FED91000) 与ACPI的硬件地址一致,这个地址映射了VT-d的相关寄存器这些寄存器描述在 : Intel® Virtualization Technology for Directed I/O 第10章

    在 Intel® Virtualization Technology for Directed I/O 第8章 描述了DMAR;配合 DMAR表组织结构 进行学习

    The system BIOS is responsible for detecting the remapping hardware functions in the platform andfor locating the memory-mapped remapping hardware registers in the host system address space.The BIOS reports the remapping hardware units in a platform to system software through the DMARemapping Reporting (DMAR) ACPI table described below

    系统BIOS负责检测平台中硬件功能的映射 和 将硬件寄存器映射系统地址空间(物理地址)。BIOS通过DMAR ACPI表将这些信息传递给操作系统

    image.png

    代码汇总

    参考:英特尔IOMMU驱动程序分析

    IOMMU_TABLE

    注:Linux 解析ACPI表的方法非常巧妙, 将所有解析表封装成一个函数,然后注册一个回调接口,全部完成;666

    1. // arch/x86/include/asm/iommu_table.h
    2. struct iommu_table_entry {
    3. initcall_t detect;
    4. initcall_t depend;
    5. void (*early_init)(void); /* No memory allocate available. */
    6. void (*late_init)(void); /* Yes, can allocate memory. */
    7. #define IOMMU_FINISH_IF_DETECTED (1<<0)
    8. #define IOMMU_DETECTED (1<<1)
    9. int flags;
    10. };
    11. 该结构体:系统在初始化时,通过 __IOMMU_INIT 系列宏去注册一些列在 ”.iommu_table“段的结构体
    12. 然后在 ./arch/x86/mm/init_64.c 中
    13. mem_init
    14. pci_iommu_alloc();
    15. // 在__IOMMU_INIT宏注释中说明:添加到这个表的字段需要系统去进行排序
    16. // 根据depend依赖关系去排序,然后分别初始化
    17. sort_iommu_table(__iommu_table, __iommu_table_end);
    18. for (p = __iommu_table; p < __iommu_table_end; p++) { // 去遍历排序后的表,分别初始化
    19. if (p && p->detect && p->detect() > 0) { // 调用detect接口 ******
    20. p->flags |= IOMMU_DETECTED;
    21. if (p->early_init) p->early_init(); // 调用early_init接口,这里未实现
    22. IOMMU_INIT_POST(detect_intel_iommu);
    23. detect_intel_iommu
    24. dmar_table_detect(); // 会从acpi获取dmar信息, ACPI的不了解,可以用系统中打印出acpi表
    25. dmar_walk_dmar_table((struct acpi_table_dmar *)dmar_tbl,
    26. &validate_drhd_cb);
    27. dmar_walk_remapping_entries // 这个函数从ACPI表中找出了对应回调接口,然后调用回调
    28. // // 遍历时,只有ACPI_DMAR_TYPE_HARDWARE_UNIT类型有回调接口,调用回调去初始化
    29. cb->cb[iter->type](iter, cb->arg[iter->type]);
    30. dmar_validate_one_drhd // ACPI_DMAR_TYPE_HARDWARE_UNI 的回调函数
    31. 只是检测下 是否是支持VT-D的有效的DRHD,cap和ecap至少要支持一个。
    32. pci_request_acs(); -> pci_acs_enable = 1;
    33. x86_init.iommu.iommu_init = intel_iommu_init; // 重点接口
    34. acpi_put_table(dmar_tbl); // dmar_table_detect()的释放ACPI接口

    注:打印dmar的acpi表信息(详细可参考 ACPI)

    1. sudo apt-get install -y iasl acpica-tools
    2. mkdir -p testacpi && cd testacpi
    3. acpidump > acpidump.out # 将ACPI表二进制打印到文件
    4. acpixtract -a acpidump.out # 解析acpi表,生成各个dat文件
    5. iasl -d dmar.dat # iasl会解析acpi 二进制表,生成xxx.dsl描述文件

    dmar表的获取-dmar_tbl全局变量(重点)

    1. dmar_table_detect
    2. |- status = acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_tbl);
    3. // DMAR.dsl
    4. [000h 0000 4] Signature : "DMAR" [DMA Remapping table]
    5. [004h 0004 4] Table Length : 000001E0
    6. [008h 0008 1] Revision : 01
    7. [009h 0009 1] Checksum : 5A
    8. [00Ah 0010 6] Oem ID : "INTEL "
    9. [010h 0016 8] Oem Table ID : "S2600WF "
    10. [018h 0024 4] Oem Revision : 00000001
    11. [01Ch 0028 4] Asl Compiler ID : "INTL"
    12. [020h 0032 4] Asl Compiler Revision : 20091013
    13. [024h 0036 1] Host Address Width : 2D
    14. [025h 0037 1] Flags : 03
    15. [026h 0038 10] Reserved : 00 00 00 00 00 00 00 00 00 00
    16. 这个刚好对应 全局变量 dmar_tbl 中的信息 ***********
    17. /*******************************************************************************
    18. *
    19. * Master ACPI Table Header. This common header is used by all ACPI tables
    20. * except the RSDP and FACS.
    21. *
    22. ******************************************************************************/
    23. struct acpi_table_header {
    24. char signature[ACPI_NAME_SIZE]; /* ASCII table signature */
    25. u32 length; /* Length of table in bytes, including this header */
    26. u8 revision; /* ACPI Specification minor version number */
    27. u8 checksum; /* To make sum of entire table == 0 */
    28. char oem_id[ACPI_OEM_ID_SIZE]; /* ASCII OEM identification */
    29. char oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; /* ASCII OEM table identification */
    30. u32 oem_revision; /* OEM revision number */
    31. char asl_compiler_id[ACPI_NAME_SIZE]; /* ASCII ASL compiler vendor ID */
    32. u32 asl_compiler_revision; /* ASL compiler version */
    33. };
    34. struct acpi_table_dmar {
    35. struct acpi_table_header header; /* Common ACPI table header */
    36. u8 width; /* Host Address Width */
    37. u8 flags;
    38. u8 reserved[10];
    39. };
    40. struct acpi_table_header * __initdata dmar_tbl;

    dmar表解析 dmar_validate_one_drhd配合附录中 ACPI的DMAR数据 和 DMAR表组织结构 进行查看学习这部分主要看: DRHD(DMA Remapping Hardware Unit Definition) 表

    DRHD表主要包括两方面的信息,一是提供VT-d重定向硬件寄存器基地址,为系统软件访问VT-d硬件寄存器提供入口(各个偏移量所指向的具体寄存器在VT-d的spec中有详细的约定,即VT-d硬件的具体实现);另一个是该VT-d重定向硬件所管辖的硬件,由Segment Number和Device Scope两个区域来定义。Device Scope结构体由Device Scope Entry组成,每个Device Scope Entry可以用来指明一个PCI endpoint device,一个PCI sub-hierarchy,或者其他设备,如I/O xAPIC或者HPET。

    1. struct acpi_dmar_header {
    2. u16 type;
    3. u16 length;
    4. };
    5. struct acpi_dmar_hardware_unit { // DRHD表结构体
    6. struct acpi_dmar_header header;
    7. u8 flags;
    8. u8 reserved;
    9. u16 segment;
    10. u64 address; /* Register Base Address */
    11. };
    12. /* 判断ACPI是否枚举出dmar的drhd配置, 且是否有效 */
    13. /* 检查下 VT-d重定向硬件寄存器基地址(参考基础知识-DMAR寄存器)的cap或ecap寄存器是否存在 */
    14. dmar_validate_one_drhd(entry = ACPI_DMAR_TYPE_HARDWARE_UNI对应ACPI表信息, args=NULL)
    15. addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
    16. cap = dmar_readq(addr + DMAR_CAP_REG);
    17. ecap = dmar_readq(addr + DMAR_ECAP_REG);
    18. early_iounmap(addr, VTD_PAGE_SIZE);
    19. check....

    入口部分

    1. ./arch/x86/kernel/pci-dma.c
    2. iommu_setup // grub参数
    3. rootfs_initcall(pci_iommu_init);
    4. pci_iommu_init
    5. dma_debug_add_bus(&pci_bus_type);
    6. x86_init.iommu.iommu_init(); => intel_iommu_init
    7. /* 这个IOMMU_INIT_POST(detect_intel_iommu) 未实现late_init,不用看 */
    8. /*
    9. for (p = __iommu_table; p < __iommu_table_end; p++) {
    10. if (p && (p->flags & IOMMU_DETECTED) && p->late_init)
    11. p->late_init(); // 未实现
    12. }
    13. */

    初始化部分

    1. Copy Intel IOMMU Introduction
    2. intel_iommu_init
    3. |-> dmar_table_init -> parse_dmar_table -> dmar_walk_dmar_table //重点分析
    4. |-> dmar_dev_scope_init
    5. |-> dmar_acpi_dev_scope_init -> dmar_acpi_insert_dev_scope //重点分析
    6. |-> dmar_pci_bus_add_dev -> dmar_insert_dev_scope
    7. |-> bus_register_notifier
    8. |-> dmar_init_reserved_ranges // init RMRR
    9. |-> init_no_remapping_devices // init no remapping devices
    10. |-> init_dmars //重点分析
    11. |-> dma_ops = &intel_dma_ops
    12. |-> iommu_device_sysfs_add, iommu_device_set_ops, iommu_device_register
    13. |-> bus_set_iommu(&pci_bus_type, &intel_iommu_ops)
    14. |-> bus_register_notifier(&pci_bus_type, &device_nb)

    dmar_table_init-解析DMAR的ACPI表

    关于 dmar_table_init -> parse_dmar_table -> dmar_walk_dmar_table 这条路和IOMMU TABLE部分解析方式一样,这里不具体分析代码

    1. // 用acpi去回调这些所有的接口
    2. struct dmar_res_callback cb = {
    3. .print_entry = true,
    4. .ignore_unhandled = true,
    5. .arg[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &drhd_count,
    6. .cb[ACPI_DMAR_TYPE_HARDWARE_UNIT] = &dmar_parse_one_drhd,
    7. .cb[ACPI_DMAR_TYPE_RESERVED_MEMORY] = &dmar_parse_one_rmrr,
    8. .cb[ACPI_DMAR_TYPE_ROOT_ATS] = &dmar_parse_one_atsr,
    9. .cb[ACPI_DMAR_TYPE_HARDWARE_AFFINITY] = &dmar_parse_one_rhsa,
    10. .cb[ACPI_DMAR_TYPE_NAMESPACE] = &dmar_parse_one_andd,
    11. };
    12. // https://www.intel.com/content/dam/www/public/us/en/documents/guides/txt-enabling-guide.pdf
    13. dmar_tbl = tboot_get_dmar_table(dmar_tbl);
    14. // 和IOMMU_TABLE一样,都是获取ACPI,去调用各个回调接口
    15. dmar_table_detect();
    16. ret = dmar_walk_dmar_table(dmar, &cb);

    dmar_parse_one_drhd 函数 (重点)

    调试:内核启动过程中打印 pr_info(“DMAR dmar0/1: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n”) 就是在这个函数中调用了 alloc_iommu 的结果

    dmar_drhd_units全局存 dmar_drhd_unit 的结构体

    1. # 先看一个drhd全不结构:
    2. ================== header===================================
    3. [030h 0048 2] Subtable Type : 0000 [Hardware Unit Definition]
    4. [032h 0050 2] Length : 0018
    5. [034h 0052 1] Flags : 00
    6. [035h 0053 1] Reserved : 00
    7. [036h 0054 2] PCI Segment Number : 0000
    8. [038h 0056 8] Register Base Address : 00000000FED90000
    9. ===================end header=================================
    10. ===================dev scope==============================
    11. [040h 0064 1] Device Scope Type : 01 [PCI Endpoint Device]
    12. [041h 0065 1] Entry Length : 08
    13. [042h 0066 2] Reserved : 0000
    14. [044h 0068 1] Enumeration ID : 00
    15. [045h 0069 1] PCI Bus Number : 00
    16. [046h 0070 2] PCI Path : 02,00
    17. ===================dev scope==============================
    18. struct acpi_dmar_hardware_unit {
    19. struct acpi_dmar_header header;
    20. u8 flags;
    21. u8 reserved;
    22. u16 segment;
    23. u64 address; /* Register Base Address */
    24. }drhd;
    25. struct dmar_drhd_unit {
    26. struct list_head list; /* list of drhd units */
    27. struct acpi_dmar_header *hdr; /* ACPI header */
    28. u64 reg_base_addr; /* register base address*/
    29. struct dmar_dev_scope *devices;/* target device array */
    30. int devices_cnt; /* target device count */
    31. u16 segment; /* PCI domain */
    32. u8 ignored:1; /* ignore drhd */
    33. u8 include_all:1;
    34. u8 gfx_dedicated:1; /* graphic dedicated */
    35. struct intel_iommu *iommu;
    36. }dmaru;
    37. // drivers/iommu/intel/dmar.c
    38. dmar_parse_one_drhd(DRHD, &drhd_count)
    39. drhd = (struct acpi_dmar_hardware_unit *)header // drhd 是 ACPI表中DRHD的地址
    40. dmaru = dmar_find_dmaru(drhd); //dmar_drhd_units全局存drhd的链表,此时未初始化=false
    41. // 分配一个dmaru描述 + DRHD大小,并将DRHD结构 拷贝到dmaru结构体底部
    42. dmaru = kzalloc(sizeof(*dmaru) + header->length, GFP_KERNEL);
    43. ..... // init dmar unit结构
    44. |->dmaru->devices = dmar_alloc_dev_scope // 解析 ACPI中DMAR的DRHD dev scope
    45. |->alloc_iommu(dmaru) // *** 重点,每一个dmaru都分配一套struct intel_iommu的结构
    46. struct intel_iommu *iommu; //
    47. map_iommu(iommu, drhd->reg_base_addr); // 映射VT-D中的寄存器表
    48. dmaru->iommu = iommu; // 这就是下边UML图
    49. |->dmar_register_drhd_unit(dmaru)
    50. dmar_drhd_units 全局链表中添加自己的dmaru结构

    image.png调试小知识:

    seq_id是个bitmap,其实对应就是dmar%d。 在alloc_iommu->dmar_alloc_seq_id 中去赋值的 sprintf(iommu->name, “dmar%d”, iommu->seq_id); 然后后边有打印: reg_base_addr就是 每一个dmar对应VT-D的寄存器表基地址,cap和ecap的值是VT-D中寄存器的值。

    [ 0.136135] DMAR: DRHD base: 0x000000d37fc000 flags: 0x0 [ 0.136143] DMAR: dmar0: reg_base_addr d37fc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136144] DMAR: DRHD base: 0x000000e0ffc000 flags: 0x0 [ 0.136149] DMAR: dmar1: reg_base_addr e0ffc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136150] DMAR: DRHD base: 0x000000ee7fc000 flags: 0x0 [ 0.136155] DMAR: dmar2: reg_base_addr ee7fc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136156] DMAR: DRHD base: 0x000000fbffc000 flags: 0x0 [ 0.136160] DMAR: dmar3: reg_base_addr fbffc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136161] DMAR: DRHD base: 0x000000aaffc000 flags: 0x0 [ 0.136167] DMAR: dmar4: reg_base_addr aaffc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136168] DMAR: DRHD base: 0x000000b87fc000 flags: 0x0 [ 0.136172] DMAR: dmar5: reg_base_addr b87fc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136173] DMAR: DRHD base: 0x000000c5ffc000 flags: 0x0 [ 0.136176] DMAR: dmar6: reg_base_addr c5ffc000 ver 1:0 cap 8d2078c106f0466 ecap f020de [ 0.136177] DMAR: DRHD base: 0x0000009d7fc000 flags: 0x1 [ 0.136181] DMAR: dmar7: reg_base_addr 9d7fc000 ver 1:0 cap 8d2078c106f0466 ecap f020de

    dmar_parse_one_rmrrRMRR(Reserved Memory Region Reporting)表 :RMRR表用于表示BIOS或者UEFI为了DMA的使用而保留的一些系统物理内存,这些内存从操作系统的角度来看其属性为Reserved Memory,因为有一些比较传统的设备(比如USB、UMA显卡等)可能会需要用到一些固定的,或者专用的系统内存,这时候就需要BIOS或UEFI为其保留。

    全局链表 dmar_rmrr_units 存放了ACPI中的 acpi_dmar_reserved_memory 预留内存单元结构体

    1. [068h 0104 2] Subtable Type : 0001 [Reserved Memory Region]
    2. [06Ah 0106 2] Length : 0020
    3. [06Ch 0108 2] Reserved : 0000
    4. [06Eh 0110 2] PCI Segment Number : 0000
    5. [070h 0112 8] Base Address : 00000000BBDE2000
    6. [078h 0120 8] End Address (limit) : 00000000BBE01FFF
    7. [080h 0128 1] Device Scope Type : 01 [PCI Endpoint Device]
    8. [081h 0129 1] Entry Length : 08
    9. [082h 0130 2] Reserved : 0000
    10. [084h 0132 1] Enumeration ID : 00
    11. [085h 0133 1] PCI Bus Number : 00
    12. [086h 0134 2] PCI Path : 14,00
    13. ...
    14. dmar_parse_one_rmrr
    15. 这部分和drhd的初始化类似,根据ACPI 初始化自己rmrr的结构,并添加到链表中

    **dmar_parse_one_atsrdmar_parse_one_rhsadmar_parse_one_andd (namespace,好像可以没有)这三个不考虑,可选项,根据 DMAR表组织结构 去分析

    dmar_dev_scope_init(可以跳过)
    1. dmar_dev_scope_init
    2. // 判断ACPI中是否支持dmar_drhd_unit
    3. // 从全局dmar_drhd_units链表中是否有 dmar_drhd_unit 的结构体
    4. if (list_empty(&dmar_drhd_units))
    5. dmar_dev_scope_status = -ENODEV;
    6. dmar_acpi_dev_scope_init(); // *** 重要函数,但找了几台机器,都没这玩意namespace,跳过
    7. // 给PCI总线注册 BUS_NOTIFY_ADD_DEVICE 和 BUS_NOTIFY_REMOVED_DEVICE 通知接口
    8. // **** 这部分也很关键,设备起来后,添加驱动,都需要走这个
    9. bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb);

    dmar_init_reserved_ranges

    init_dmars() 重点函数
    1. // 初始化每个iommu,并分配root_entry
    2. for_each_drhd_unit(drhd)
    3. g_num_of_iommus // g_num_of_iommus = drhd 硬件dmar的个数
    4. for_each_active_iommu(iommu, drhd) // 每个drhd都有intel_iommu对象,存放在drhd->iommu
    5. g_iommus[iommu->seq_id] = iommu; // g_iommus 根据seq_id存放了intel_iommu对象
    6. // 注:iommu分配和seq_id初始化在 alloc_iommu 接口实现
    7. intel_iommu_init_qi // 参考VT-D 6.5 什么寄存器缓存啥的??? TBD
    8. iommu_init_domains(iommu); // 重点:
    9. ndomains = cap_ndoms(iommu->cap); // 判断当前drhd支持多少个domains, cap[2:0]表示
    10. iommu->domain_ids; // 对支持的域建立bitmap
    11. // 难点:三级指针, 最终格式如下边的图片
    12. size = (ALIGN(ndomains, 256) >> 8) * sizeof(struct dmar_domain **);
    13. iommu->domains = kzalloc(size, GFP_KERNEL);
    14. size = 256 * sizeof(struct dmar_domain *);
    15. iommu->domains[0] = kzalloc(size, GFP_KERNEL);
    16. init_translation_status(iommu); // 判断GSTS_REG[30]寄存器是否使能DMAR,修改iommu->flags
    17. translation_pre_enabled(iommu); // 同上,验证下环境
    18. iommu_alloc_root_entry(iommu); // 给每个intel_iommu分配root_entry,大小1页大小
    19. copy_translation_tables(iommu); // 暂时跳过,后边详解
    20. intel_svm_alloc_pasid_tables(iommu); // 支持PASID,以后看的时候补充
    21. iommu_set_root_entry(iommu); // 更新iommu的entry地址

    iommu_init_domains 的结构描述:image.pngimage.pngimage.png

    copy_translation_tables 说明

    这里有个说明: ecap[24] 和 Root Table Address Register[11]都为0,旧版本支持扩展的空间,新版本已不支持,直接为0

    1. struct root_entry {
    2. u64 lo;
    3. u64 hi;
    4. };
    5. new_ext == ext == 0;
    6. rtaddr_reg = Root Table Address Register 的值
    7. // old_rt_phys 和 old_rt 旧的Root Table Address
    8. old_rt_phys = rtaddr_reg & VTD_PAGE_MASK;
    9. old_rt = memremap(old_rt_phys, PAGE_SIZE, MEMREMAP_WB);
    10. ctxt_tbls=分配成256大小的指针数组=> void * ctxt_tbls[256]
    11. for (bus = 0; bus < 256; bus++) {
    12. copy_context_table(iommu, &old_rt[bus], ctxt_tbls, bus, ext);
    13. }

    set_ops

    初始化操作接口
    1. for_each_active_iommu(iommu, drhd) {
    2. iommu_device_sysfs_add(&iommu->iommu, NULL,
    3. intel_iommu_groups,
    4. "%s", iommu->name);
    5. iommu_device_set_ops(&iommu->iommu, &intel_iommu_ops);
    6. iommu_device_register(&iommu->iommu);
    7. }

    初始化一个sysfs/classs/iommu/dmarx 接口, 并关联起相关的接口image.png

    dma_ops = &intel_dma_ops;

    更新dma操作接口,dma_alloc_coherent 后期分配内存就不走通用的接口了,走这里了。

    bus_set_iommu

    建议先复习下 内核通知链

    1. bus_set_iommu(&pci_bus_type, &intel_iommu_ops);
    2. bus->iommu_ops = ops; // 给pci_bus_type注册 iommu_ops
    3. iommu_bus_init(bus, ops);
    4. nb->notifier_call = iommu_bus_notifier;
    5. // 给pci_bus_type在注册一个notifier 通知链
    6. // 内核通知链里分析过:device_add会触发一次notifier
    7. bus_register_notifier(bus, call_func:iommu_bus_notifier);
    8. bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
    9. 对pci_bus_type下的设备都调用一次 add_iommu_group(pci_dev, &cb)
    10. cb->ops = intel_iommu_ops
    11. // 在给pci_bus_type注册一个notifier通知
    12. bus_register_notifier(&pci_bus_type, call_func:device_notifier;

    add_iommu_group(重点难点 )

    先简单整理下代码调用关系注:iommu & intel-iommu实现 中开始对这部分代码有了比较详细的跟踪

    1. add_iommu_group
    2. const struct iommu_ops *ops = cb->ops; // cb->ops = intel_iommu_ops
    3. intel_iommu_ops->add_device(dev); // 这就是对每个PCI设备都添加组的最终操作
    4. intel_iommu_add_device(dev)
    5. // 根据PCI设备寻找 对应PCI总线域的DRHD的intel_iommu结构
    6. // 根据dmar_drhd_unit找到当前device属于哪个 intel_iommu
    7. iommu = device_to_iommu(dev, &bus, &devfn);
    8. segment = pci_domain_nr(pdev->bus); // 每个PCI总线的domain,也就是segment
    9. 当前PCI设备的segment 肯定会落在drhd的描述中,说明该PCI设备属于匹配的drhd下
    10. 在对应drhd下找到匹配的设备,返回intel_iommu
    11. iommu_device_link(&iommu->iommu, dev); // 将该设备链接到sysfs下
    12. // 获取或者分配设备的iommu_group,并分配响应的iommu_domain
    13. // *** 重点***
    14. group = iommu_group_get_for_dev(dev);
    15. iommu_group_get(dev); // dev->iommu_group第一次肯定为NULL,
    16. group = ops->device_group(dev); // 调用 intel_iommu 的 pci_device_group
    17. // 给当前group分配的 domain, 调用 intel_iommu_domain_alloc
    18. group->domain = group->default_domain = __iommu_domain_alloc
    19. // 上边是配合和查找group,然后当然是将设备添加到group了
    20. iommu_group_add_device(group, dev)
    21. // 在sysfs下创建iommu_groups
    22. sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group");
    23. // 很关键:以后直接从dev获取group即可
    24. dev->iommu_group = group;
    25. // intel_iommu_attach_device 将设备挂到domain上,对其进行页表管理
    26. __iommu_attach_device(group->domain, dev);
    27. // 给组内兄弟喊一声:来新人了,发红包
    28. blocking_notifier_call_chain(,IOMMU_GROUP_NOTIFY_ADD_DEVICE,);
    29. // [关键打印信息] iommu: Adding device 0000:00:00.0 to group 0
    30. iommu_group_put(group);

    domain的分配

    1. __iommu_domain_alloc // 初始化dmar_domain和iommu_domain
    2. bus->iommu_ops->domain_alloc(type);

    image.pngiommu_group_get_for_dev 和 iommu_group_add_device这个函数分组关系比较复杂,具 intel IOMMU driver analysis 和 IOMMU group and PCIe ACS问题 描述

    有几种情况可以从现有设备获取设备IOMMU组。 例如,如果一个网桥支持ACS,则需要转到上游总线。. 多功能设备的所有功能设备也需要共享同一IOMMU组

    这部分牵扯到PCIE的ACS功能,这部分跳过,只要知道: GROUP是虚拟化分组的最小单位,所以SR-IOV设备要把pcie分到不同的分组中,才能用。

    1. iommu_group_get_for_dev
    2. ops->device_group(dev);
    3. pci_device_group
    4. pci_device_group
    5. iommu_group_alloc(); // 这里只看创建group
    6. iommu_group_add_device
    7. dev->iommu_group = group;
    8. __iommu_attach_device(group->domain, dev);

    附录

    附录1 Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz的ACPI DMAR

    1. baiy@baiy-ThinkPad-E470c:testacpi$ cat dmar.dsl
    2. /*
    3. * Intel ACPI Component Architecture
    4. * AML/ASL+ Disassembler version 20180105 (64-bit version)
    5. * Copyright (c) 2000 - 2018 Intel Corporation
    6. *
    7. * Disassembly of dmar.dat, Sun Nov 29 21:34:36 2020
    8. *
    9. * ACPI Data Table [DMAR]
    10. *
    11. * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue
    12. */
    13. [000h 0000 4] Signature : "DMAR" [DMA Remapping table]
    14. [004h 0004 4] Table Length : 000000A8
    15. [008h 0008 1] Revision : 01
    16. [009h 0009 1] Checksum : DE
    17. [00Ah 0010 6] Oem ID : "LENOVO"
    18. [010h 0016 8] Oem Table ID : "TP-R0D "
    19. [018h 0024 4] Oem Revision : 00000830
    20. [01Ch 0028 4] Asl Compiler ID : "PTEC"
    21. [020h 0032 4] Asl Compiler Revision : 00000002
    22. [024h 0036 1] Host Address Width : 26
    23. [025h 0037 1] Flags : 01
    24. [026h 0038 10] Reserved : 00 00 00 00 00 00 00 00 00 00
    25. [030h 0048 2] Subtable Type : 0000 [Hardware Unit Definition]
    26. [032h 0050 2] Length : 0018
    27. [034h 0052 1] Flags : 00
    28. [035h 0053 1] Reserved : 00
    29. [036h 0054 2] PCI Segment Number : 0000
    30. [038h 0056 8] Register Base Address : 00000000FED90000
    31. [040h 0064 1] Device Scope Type : 01 [PCI Endpoint Device]
    32. [041h 0065 1] Entry Length : 08
    33. [042h 0066 2] Reserved : 0000
    34. [044h 0068 1] Enumeration ID : 00
    35. [045h 0069 1] PCI Bus Number : 00
    36. [046h 0070 2] PCI Path : 02,00
    37. [048h 0072 2] Subtable Type : 0000 [Hardware Unit Definition]
    38. [04Ah 0074 2] Length : 0020
    39. [04Ch 0076 1] Flags : 01
    40. [04Dh 0077 1] Reserved : 00
    41. [04Eh 0078 2] PCI Segment Number : 0000
    42. [050h 0080 8] Register Base Address : 00000000FED91000
    43. [058h 0088 1] Device Scope Type : 03 [IOAPIC Device]
    44. [059h 0089 1] Entry Length : 08
    45. [05Ah 0090 2] Reserved : 0000
    46. [05Ch 0092 1] Enumeration ID : 02
    47. [05Dh 0093 1] PCI Bus Number : F0
    48. [05Eh 0094 2] PCI Path : 1F,00
    49. [060h 0096 1] Device Scope Type : 04 [Message-capable HPET Device]
    50. [061h 0097 1] Entry Length : 08
    51. [062h 0098 2] Reserved : 0000
    52. [064h 0100 1] Enumeration ID : 00
    53. [065h 0101 1] PCI Bus Number : 00
    54. [066h 0102 2] PCI Path : 1F,00
    55. [068h 0104 2] Subtable Type : 0001 [Reserved Memory Region]
    56. [06Ah 0106 2] Length : 0020
    57. [06Ch 0108 2] Reserved : 0000
    58. [06Eh 0110 2] PCI Segment Number : 0000
    59. [070h 0112 8] Base Address : 00000000BBDE2000
    60. [078h 0120 8] End Address (limit) : 00000000BBE01FFF
    61. [080h 0128 1] Device Scope Type : 01 [PCI Endpoint Device]
    62. [081h 0129 1] Entry Length : 08
    63. [082h 0130 2] Reserved : 0000
    64. [084h 0132 1] Enumeration ID : 00
    65. [085h 0133 1] PCI Bus Number : 00
    66. [086h 0134 2] PCI Path : 14,00
    67. [088h 0136 2] Subtable Type : 0001 [Reserved Memory Region]
    68. [08Ah 0138 2] Length : 0020
    69. [08Ch 0140 2] Reserved : 0000
    70. [08Eh 0142 2] PCI Segment Number : 0000
    71. [090h 0144 8] Base Address : 00000000BD000000
    72. [098h 0152 8] End Address (limit) : 00000000BF7FFFFF
    73. [0A0h 0160 1] Device Scope Type : 01 [PCI Endpoint Device]
    74. [0A1h 0161 1] Entry Length : 08
    75. [0A2h 0162 2] Reserved : 0000
    76. [0A4h 0164 1] Enumeration ID : 00
    77. [0A5h 0165 1] PCI Bus Number : 00
    78. [0A6h 0166 2] PCI Path : 02,00
    79. Raw Table Data: Length 168 (0xA8)
    80. 0000: 44 4D 41 52 A8 00 00 00 01 DE 4C 45 4E 4F 56 4F // DMAR......LENOVO
    81. 0010: 54 50 2D 52 30 44 20 20 30 08 00 00 50 54 45 43 // TP-R0D 0...PTEC
    82. 0020: 02 00 00 00 26 01 00 00 00 00 00 00 00 00 00 00 // ....&...........
    83. 0030: 00 00 18 00 00 00 00 00 00 00 D9 FE 00 00 00 00 // ................
    84. 0040: 01 08 00 00 00 00 02 00 00 00 20 00 01 00 00 00 // .......... .....
    85. 0050: 00 10 D9 FE 00 00 00 00 03 08 00 00 02 F0 1F 00 // ................
    86. 0060: 04 08 00 00 00 00 1F 00 01 00 20 00 00 00 00 00 // .......... .....
    87. 0070: 00 20 DE BB 00 00 00 00 FF 1F E0 BB 00 00 00 00 // . ..............
    88. 0080: 01 08 00 00 00 00 14 00 01 00 20 00 00 00 00 00 // .......... .....
    89. 0090: 00 00 00 BD 00 00 00 00 FF FF 7F BF 00 00 00 00 // ................
    90. 00A0: 01 08 00 00 00 00 02 00 // ........
    01-shell脚本介绍-《shell脚本》 系统网络

    01-shell脚本介绍-《shell脚本》

    一、shell脚本是什么二、为什么要学shell,而不是其他计算机语言三、学习这门课程的优势四、学了能干什么五、学习什么内容六、学习的技巧七、成长路径八、学习环
    评论:0   参与:  0