2. 列举设备:BIOS会逐个列举PCIe总线上的设备,识别设备的厂商ID、设备ID等信息,以便后续配置。
复位阶段:在PCIe设备上电或复位后,设备会发送复位旗子暗记给主机,开始链路演习的过程
速率协商:PCIe设备和主机会进行速率协商,确定链路的事情速率,PCIe1.0->PCIe2.0->PCIe 3.0->PCIe 4.0...
(图片来自网络侵删)创造和初始化:设备和主机会进行链路创造和初始化,确认彼此的存在并建立通信
发送TS1和TS2:设备会发送Training Sequence 1(TS1)和Training Sequence 2(TS2)旗子暗记给主机,用于时钟和数据规复
吸收TS1和TS2:主机吸收并相应设备发送的TS1和TS2旗子暗记,进行时钟和数据规复
发送TS1和TS2确认:设备吸收主机发送的TS1和TS2旗子暗记后,发送确认旗子暗记给主机,表示链路演习成功
3. 分配资源:BIOS会为每个PCIe设备分配资源,包括内存地址、I/O地址、中断等,以确保设备能够正常事情。
内存地址空间分配:系统会为每个PCIe设备分配一定范围的内存地址空间,用于设备与主机之间的数据传输和通信。这些内存地址常日包括BAR(Base Address Registers)寄存器,设备可以通过BAR来访问分配给自己的内存地址空间。
I/O地址空间分配:除了内存地址空间,系统还会为PCIe设备分配一定范围的I/O地址空间,用于设备与主机之间的输入输出操作。
其他资源分配:如MSI/MSI-X中断分配、DMA通道、时钟资源等,以确保设备能够正常运行。
4. 天生ACPI表:BIOS会根据扫描结果天生ACPI(Advanced Configuration and Power Interface)表,操作系统可以通过ACPI表获取PCIe设备的信息。
Linux内核支持两种PCI扫描办法:ACPI办法和非ACPI办法(Legacy mode)。
1. ACPI办法:在ACPI办法下,Linux内核利用ACPI(Advanced Configuration and Power Interface)来扫描PCI总线和设备。通过ACPI办法,Linux内核可以利用ACPI表格中的信息来创造和配置PCI设备,实现对PCI设备的管理和掌握。
2. 非ACPI办法(Legacy mode):在非ACPI办法下,Linux内核利用传统的BIOS接口来扫描PCI总线和设备。在这种模式下,Linux内核会直接访问BIOS供应的PCI配置空间来创造和配置PCI设备,而不依赖于ACPI表格。如果想利用非ACPI的办法列举PCIE/PCI设备,可以通过在kernel 启动参数中加入“pci=noacpi”
网络系统信息:BIOS会首先网络系统中各个设备的信息,包括CPU、内存、PCI设备、中断掌握器等硬件信息。
添补表格:BIOS将构建好的ACPI数据构造添补到ACPI表格中,这些表格包括RSDP(Root System Description Pointer)、RSDT(Root System Description Table)、XSDT(Extended System Description Table)等。
5. 操作系统加载:操作系统启动后,会连续扫描PCIe总线,加载相应的驱动程序来管理PCIe设备。
内核pci初始化流程关系
root@ubuntu:~# cat /proc/kallsyms | grep pci | grep initcall
ffffffffb02b8d08 t __initcall_pcibus_class_init2
ffffffffb02b8d10 t __initcall_pci_driver_init2
ffffffffb02b8e10 t __initcall_acpi_pci_init3
ffffffffb02b8e30 t __initcall_register_xen_pci_notifier3
ffffffffb02b8e58 t __initcall_pci_arch_init3
ffffffffb02b9020 t __initcall_pci_slot_init4
ffffffffb02b9240 t __initcall_pci_subsys_init4
ffffffffb02b9478 t __initcall_pcibios_assign_resources5
ffffffffb02b94b0 t __initcall_pci_apply_final_quirks5s
ffffffffb02b94c8 t __initcall_pci_iommu_initrootfs
ffffffffb02b9970 t __initcall_pci_proc_init6
ffffffffb02b9978 t __initcall_pcie_portdrv_init6
ffffffffb02b9988 t __initcall_pcie_pme_service_init6
ffffffffb02b9998 t __initcall_pci_hotplug_init6
ffffffffb02b99a0 t __initcall_pcied_init6
ffffffffb02b99a8 t __initcall_pci_ep_cfs_init6
ffffffffb02b99b0 t __initcall_pci_epc_init6
ffffffffb02b99b8 t __initcall_pci_epf_init6
ffffffffb02b99c0 t __initcall_dw_plat_pcie_driver_init6
ffffffffb02b9a68 t __initcall_virtio_pci_driver_init6
ffffffffb02b9ae0 t __initcall_serial_pci_driver_init6
ffffffffb02b9c20 t __initcall_sis_pci_driver_init6
ffffffffb02b9c28 t __initcall_ata_generic_pci_driver_init6
ffffffffb02b9c70 t __initcall_ehci_pci_init6
ffffffffb02b9c88 t __initcall_ohci_pci_init6
ffffffffb02b9ca8 t __initcall_xhci_pci_init6
ffffffffb02b9f38 t __initcall_pci_resource_alignment_sysfs_init7
ffffffffb02b9f40 t __initcall_pci_sysfs_init7
ffffffffb02b9fe8 t __initcall_pci_mmcfg_late_insert_resources7
下面挑几个看看
1.pcibus_class初始化
Linux内核中注册一个名为 pci_bus 的设备类,并在内核初始化阶段调用 pcibus_class_init 函数来完成设备类的注册事情。
static struct class pcibus_class = {.name= "pci_bus",.dev_release= &release_pcibus_dev,.dev_groups= pcibus_groups,};static int __init pcibus_class_init(void){return class_register(&pcibus_class);}postcore_initcall(pcibus_class_init);
2.pci_driver注册
Linux内核中注册一个名为pci的总线类型,用于管理和操作与 PCI 总线干系的设备。通过注册总线类型,可以为PCI设备供应相应的探测、移除、关机等功能。
struct bus_type pci_bus_type = {.name= "pci",.match= pci_bus_match,.uevent= pci_uevent,.probe= pci_device_probe,.remove= pci_device_remove,.shutdown= pci_device_shutdown,.dev_groups= pci_dev_groups,.bus_groups= pci_bus_groups,.drv_groups= pci_drv_groups,.pm= PCI_PM_OPS_PTR,.num_vf= pci_bus_num_vf,.dma_configure= pci_dma_configure,.dma_cleanup= pci_dma_cleanup,};EXPORT_SYMBOL(pci_bus_type);static int __init pci_driver_init(void){int ret;ret = bus_register(&pci_bus_type);if (ret)return ret;#ifdef CONFIG_PCIEPORTBUSret = bus_register(&pcie_port_bus_type);if (ret)return ret;#endifdma_debug_add_bus(&pci_bus_type);return 0;}postcore_initcall(pci_driver_init);
3.acpi_pci初始化
Linux内核中初始化 ACPI 干系的 PCI 功能,包括根据系统支持情形禁用 MSI 和 ASPM,以及初始化 ACPI PCI 槽和热插拔功能。
static int __init acpi_pci_init(void){if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) {pr_info("ACPI FADT declares the system doesn't support MSI, so disable it\n");pci_no_msi();}if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {pr_info("ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n");pcie_no_aspm();}if (acpi_pci_disabled)return 0;acpi_pci_slot_init();acpiphp_init();return 0;}arch_initcall(acpi_pci_init);
4.register_xen_pci_notifier
Linux内核中注册 Xen PCI 关照器,以便在 Xen 初始域中能够吸收到与 PCI 设备干系的事宜关照
static int __init register_xen_pci_notifier(void){if (!xen_initial_domain())return 0;return bus_register_notifier(&pci_bus_type, &device_nb);}arch_initcall(register_xen_pci_notifier);
5.pci_slot初始化
Linux内核中初始化PCI插槽,通过创建和添加一个名为 "slots" 的内核对象集来管理PCI插槽。
static int pci_slot_init(void){struct kset pci_bus_kset;pci_bus_kset = bus_get_kset(&pci_bus_type);pci_slots_kset = kset_create_and_add("slots", NULL,&pci_bus_kset->kobj);if (!pci_slots_kset) {pr_err("PCI: Slot initialization failure\n");return -ENOMEM;}return 0;}subsys_initcall(pci_slot_init);