首页 理论教育PCIExpress基本准备工作

PCIExpress基本准备工作

【摘要】:源代码14-19 acpi_pci_init函数该函数首先分析“Boot Architecture Flags”字段,确定当前处理器系统是否需要使能MSI中断机制和PCIe设备的ASPM机制,ASPM机制的详细描述见第8.3节,而MSI机制的详细说明见第10章。acpi_pci_root_add函数在./drivers/acpi/pci_root.c文件中,该函数的主要功能是遍历PCI总线树,如源代码14-22~23和源代码14-31所示。源代码14-23 acpi_pci_root_add函数片段2在一个x86处理器系统中,如果没有使能ACPI机制,则Linux系统调用pci_legacy_init→pcibios_scan_root函数枚举PCI设备。

setup_arch函数将分别调用acpi_boot_table_init、early_acpi_boot_init和acpi_boot_init函数完成ACPI系统的初始化,这几个函数的源代码在./arch/x86/kernel/acpi/boot.c文件中。

acpi_boot_table_init函数调用acpi_table_init函数在内存中找到RSDP和RSDT/XSDT,从而定位ACPI表。BIOS在系统初始化时将ACPI表放到一块固定物理地址区域中;early_acpi_boot_init函数调用early_acpi_process_madt函数进一步处理MADT;而acpi_boot_init函数依次分析SBFT[14](Simple Boot Flag Table)、FADT和HPET(IA-PC High Precision Event Timer Ta-ble),其中HPET是Intel定义的一个高精度定时器

setup_arch函数执行完毕后,Linux系统将调用do_initcalls函数执行与ACPI系统相关的一些模块,其中与PCI总线有关的模块有acpi_pci_init、acpi_pci_root_init和acpi_pci_link_init函数。这些函数的说明如下。

1.acpi_pci_init函数

acpi_pci_init函数的执行过程较为简单,该函数在./drivers/pci/pci-acpi.c文件中,如源代码14-19所示。

源代码14-19 acpi_pci_init函数

该函数首先分析“Boot Architecture Flags”字段,确定当前处理器系统是否需要使能MSI中断机制和PCIe设备的ASPM(Active State Power Management)机制,ASPM机制的详细描述见第8.3节,而MSI机制的详细说明见第10章。该函数调用register_acpi_bus_type函数,将acpi_pci_bus结构加入到全局链表bus_type_list,最后调用pci_set_platform_pm函数将全局变量pci_platform_pm赋值为acpi_pci_platform_pm。

2.acpi_pci_root_init函数

acpi_pci_root_init函数调用acpi_pci_root_add和acpi_pci_root_start函数遍历处理器系统中的PCI总线树。在Linux系统中,acpi_pci_root_init函数的调用关系较为复杂,本节仅介绍其调用过程,并不详细介绍其实现机制。

acpi_pci_root_init函数的调用过程如源代码14-20所示。

源代码14-20 acpi_pci_root_init函数的调用过程

acpi_pci_root_init->acpi_bus_register_driver->driver_register

->bus_add_driver->driver_attach->__driver_attach

->driver_probe_device->really_probe->(dev->bus->probe)

由以上过程可见acpi_pci_root_init函数将调用really_probe函数中的(dev->bus->probe)函数,而dev->bus->probe函数在acpi_device_register函数中被赋值为acpi_device_probe函数。(www.chuimin.cn)

acpi_device_probe函数又经过了一系列复杂的调用,最终调用acpi_pci_root_add和acpi_pci_root_start函数,其调用过程如源代码14-21所示。

源代码14-21 acpi_pci_root_init函数的调用过程

其中driver->ops.add函数与acpi_pci_root_add函数对应;而driver->ops.start函数与acpi_pci_root_start函数对应。acpi_pci_root_add函数在./drivers/acpi/pci_root.c文件中,该函数的主要功能是遍历PCI总线树,如源代码14-22~23和源代码14-31所示。

源代码14-22 acpi_pci_root_add函数片段1

这段代码通过ACPI表中的_SEG和_BBN参数获得HOST主桥使用的Segment和Bus号,创建一个acpi_pci_root结构,并对该结构进行初始化,随后将acpi_pci_root结构加入到acpi_pci_roots队列中。acpi_pci_root结构的主要功能是对当前HOST主桥控制器进行描述,而在acpi_pci_roots队列中包含当前x86处理器系统所有HOST主桥[15]的信息。

当x86处理器系统中只有一个HOST主桥时,acpi_pci_root_add函数仅会被Linux调用一次,此时acpi_pci_roots队列中只有一个数据成员,即root,其Segment和Bus号均为0;如果存在多个HOST主桥时,acpi_pci_root_add函数将在PCI总线初始化时被调用多次,并将所有主桥信息加入到acpi_pci_roots队列中。

这段代码还将HOST主桥的_OSC参数的PCI Segment Groups supported位设置为1,该参数在ACPI规范中定义,该位为1时表示当前处理器系统支持PCI Segment Group。

源代码14-23 acpi_pci_root_add函数片段2

在一个x86处理器系统中,如果没有使能ACPI机制,则Linux系统调用pci_legacy_init→pcibios_scan_root函数枚举PCI设备。如果Linux系统使能了ACPI机制,则由这段程序调用pci_acpi_scan_root函数完成PCI设备的枚举。pci_acpi_scan_root和pcibios_scan_root函数对PCI总线树的枚举过程类似。

pci_acpi_scan_root函数在./arch/x86/pci/acpi.c文件中,如源代码14-24所示。

源代码14-24 pci_acpi_scan_root函数

这段代码首先判断当前总线号是否已经存在,如果存在说明这条总线已经被遍历过,该函数将直接退出。否则将首先调用pci_create_bus函数,pci_create_bus函数的源代码在./drivers/pci/probe.c文件中,其主要作用是为当前HOST主桥创建pci_bus结构,并初始化这个pci_bus结构的部分参数如resource[0/1],secondary参数[16]等,然后将这个pci_bus结构加入到全局链表pci_root_buses中,最后进行一些与sysfs相关的初始化工作。

之后调用pci_scan_child_bus函数对当前PCI总线上的设备进行枚举,pci_scan_child_bus函数将完成对PCI总线树的枚举操作,该函数是Linux遍历PCI总线树的要点,下一节将专门介绍讨论该函数的实现机制。