根据system.map 查看内核中PCIe加载流程:
root@zh-vm:~# cat /boot/System.map-5.15.0-130-generic | grep pci | grep initcall
ffffffff8350ff68 d __initcall__kmod_pci__453_6907_pci_realloc_setup_params0
ffffffff83510098 d __initcall__kmod_probe__266_110_pcibus_class_init2
ffffffff8351009c d __initcall__kmod_pci_driver__419_1685_pci_driver_init2
ffffffff8351014c d __initcall__kmod_pci_acpi__284_1504_acpi_pci_init3
ffffffff83510158 d __initcall__kmod_pci__370_214_register_xen_pci_notifier3
ffffffff83510170 d __initcall__kmod_init__253_51_pci_arch_init3
ffffffff835102c0 d __initcall__kmod_slot__281_380_pci_slot_init4
ffffffff8351043c d __initcall__kmod_legacy__254_77_pci_subsys_init4
ffffffff83510444 d __initcall__kmod_pci_eisa__257_89_pci_eisa_init_early4s
ffffffff83510554 d __initcall__kmod_i386__269_373_pcibios_assign_resources5
ffffffff83510558 d __initcall__kmod_quirks__360_195_pci_apply_final_quirks5s
ffffffff83510564 d __initcall__kmod_pci_dma__265_136_pci_iommu_initrootfs
ffffffff83510758 d __initcall__kmod_pwm_lpss_pci__258_120_pwm_lpss_driver_pci_init6
ffffffff83510760 d __initcall__kmod_pcieportdrv__258_274_pcie_portdrv_init6
ffffffff83510764 d __initcall__kmod_proc__257_469_pci_proc_init6
ffffffff83510768 d __initcall__kmod_pci_hotplug__288_573_pci_hotplug_init6
ffffffff83510770 d __initcall__kmod_pci_ep_cfs__267_731_pci_ep_cfs_init6
ffffffff83510774 d __initcall__kmod_pci_epc_core__286_849_pci_epc_init6
ffffffff83510778 d __initcall__kmod_pci_epf_core__284_561_pci_epf_init6
ffffffff8351077c d __initcall__kmod_pcie_designware_plat__259_192_dw_plat_pcie_driver_init6
ffffffff835107f4 d __initcall__kmod_virtio_pci__289_638_virtio_pci_driver_init6
ffffffff83510810 d __initcall__kmod_platform_pci__364_193_platform_driver_init6
ffffffff83510830 d __initcall__kmod_8250_pci__275_6463_serial_pci_driver_init6
ffffffff83510834 d __initcall__kmod_8250_mid__263_402_mid8250_pci_driver_init6
ffffffff835108d4 d __initcall__kmod_pata_sis__333_909_sis_pci_driver_init6
ffffffff835108d8 d __initcall__kmod_ata_generic__318_250_ata_generic_pci_driver_init6
ffffffff83510900 d __initcall__kmod_vfio_pci_core__326_2248_vfio_pci_core_init6
ffffffff83510904 d __initcall__kmod_vfio_pci__271_264_vfio_pci_init6
ffffffff83510914 d __initcall__kmod_ehci_pci__268_432_ehci_pci_init6
ffffffff83510920 d __initcall__kmod_ohci_pci__274_321_ohci_pci_init6
ffffffff835109a0 d __initcall__kmod_intel_scu_pcidrv__253_55_intel_scu_pci_driver_init6
ffffffff83510ae0 d __initcall__kmod_pci__450_6732_pci_resource_alignment_sysfs_init7
ffffffff83510ae4 d __initcall__kmod_pci_sysfs__304_1424_pci_sysfs_init7
ffffffff83510b64 d __initcall__kmod_mmconfig_shared__282_718_pci_mmcfg_late_insert_resources7
同时:
root@zh-vm:/lib# cat /boot/System.map-5.15.0-130-generic | grep acpi_init | grep initcall
ffffffff835102cc d __initcall__kmod_acpi__408_1364_acpi_init4
整体启动顺序为,整个过程主要分为两部分 扫描设备 和 分配资源 :
--> pcibus_class_init()
--> pci_driver_init()
--> acpi_pci_init()
--> pci_arch_init()
--> pci_slot_init()
--> acpi_init()
--> pci_subsys_init()
(1)pcibus_class_init(): 注册pci_bus class,完成后创建了/sys/class/pci_bus目录。
(2)pci_driver_init(): 注册pci_bus_type, 完成后创建了/sys/bus/pci目录。
(3)acpi_pci_init(): 注册acpi_pci_bus, 并提供了一系列系统资源管理操作,包括:
(4)acpi_init(): apci启动所涉及到的初始化、枚举流程,PCIe基于acpi的启动流程从该接口进入:
该函数也是扫描设备的主要流程入口,主要包括以下几部分:
acpi_init() # ACPI 子系统总入口(drivers/acpi/bus.c)
|--> mmcfg_late_init() # acpi扫描MCFG表,MCFG表中定义了ecam资源
|--> acpi_bus_init() # 初始化 ACPI 总线
|--> acpi_scan_init() # 初始化 ACPI 设备扫描框架
|--> acpi_pci_root_init() # 初始化 PCI Root Bridge 扫描(关键入口)
|--> acpi_scan_add_handler_with_hotplug(&pci_root_handler, "pci_root");
|--> .attach = acpi_pci_root_add
|--> acpi_pci_link_init()
|--> acpi_bus_scan() # 扫描 ACPI 命名空间中的设备
|--> acpi_walk_namespace() # 遍历全部device,为这些acpi device创建数据结构
|--> acpi_bus_attach() # 探测匹配的 ACPI 设备
|--> handler->attach() # 调用 PCI Root Bridge 的 attach 回调
|--> acpi_pci_root_add() # 创建并注册 PCI Root Bridge
|--> pci_acpi_scan_root() # 核心 PCIe 枚举入口(见下文)
| pci_acpi_scan_root() # 处理 ACPI PCI Root Bridge(drivers/acpi/pci_root.c)
|--> acpi_pci_root_create() # 创建 PCI Host Bridge
|--> pci_create_root_bus() # 创建 PCI 根总线(drivers/pci/probe.c)
|--> pci_alloc_host_bridge() # 分配 Host Bridge 结构体
|--> pci_register_host_bridge() # 注册 Host Bridge
|--> pci_scan_child_bus() # 递归扫描子总线(核心扫描逻辑)
|--> pci_scan_child_bus_extend(bus, 0)
|--> for (devfn = 0; devfn < 256; devfn += 8) # 遍历所有可能的设备/功能号
|--> pci_scan_slot(bus, devfn) # 检查指定插槽是否存在设备
|--> pci_scan_single_device()
|--> pci_scan_device(bus, devfn) # 初始化设备
|--> pci_bus_read_dev_vendor_id() # 读取设备厂商ID
|--> pci_alloc_dev() # 分配设备结构体
|--> pci_setup_device() # 配置设备 BAR、Class 等
|--> pci_device_add(dev, bus) # 将设备添加到总线
|--> pci_configure_device() # 配置设备寄存器
|--> pci_init_capabilities() # 初始化 PCIe Capabilities
|--> pcibios_add_device() # 平台特定设备添加逻辑
|--> for_each_pci_bridge(dev, bus)
|--> pci_scan_bridge_extend() # 扫描桥接器扩展总线
|--> pci_add_new_bus() # 添加新总线
|--> pci_scan_child_bus_extend() # 递归扫描新总线
总体流程便是:
ACPI 初始化 → MCFG 解析 → 发现根桥 → 创建根总线 → 递归扫描设备 → 资源分配 → 中断设置 → 驱动绑定
(1) BIOS预扫描与Kernel扫描启动
acpi_init()
初始化ACPI子系统,扫描MCFG表(mmcfglateinit()
)获取ECAM资源,为PCIe枚举做准备。(2) 设备扫描阶段(深度优先)
pci_acpi_scan_root()
进入核心枚举流程。pci_create_root_bus()
创建根总线,并调用pci_scan_child_bus()
扫描根总线下的设备。pci_scan_slot()
,调用pci_scan_device()
检查设备是否存在并初始化。for_each_pci_bridge(dev, bus)
,则扩展总线号(pci_scan_bridge_extend()
),递归扫描子总线(pci_scan_child_bus_extend()
)。(3) 资源分配阶段(深度优先)
| acpi_pci_root_add() // ACPI PCI根总线添加函数
|--> pci_acpi_scan_root() // 扫描ACPI定义的PCI根总线
|--> root_ops->release_info = pci_acpi_generic_release_info; // 设置释放信息回调
|--> root_ops->prepare_resources = pci_acpi_root_prepare_resources; // 设置资源准备回调
|--> acpi_pci_root_create() // 创建ACPI PCI根总线
|--> ops->prepare_resources(info); // 调用资源准备回调
|--> pci_acpi_root_prepare_resources()
|--> acpi_pci_probe_root_resources()
|--> acpi_dev_get_resources()
|--> __acpi_dev_get_resources() // 函数执行完毕,ci->resources 链表上面挂载的就是 ACPI PCI device
_CRS method 传过来的资源(_MIN, _MAX, _LEN, _TRA).
|--> x86_pci_root_bus_resources();
|--> x86_find_pci_root_info()
|--> pci_add_resource()
|--> pci_acpi_root_add_resources()
|--> pci_add_resource()
|--> pci_create_root_bus() // 创建根PCI总线
|--> pci_register_host_bridge() // 注册主机桥
|--> pci_scan_child_bus(bus); // 扫描子总线
|--> pci_scan_child_bus_extend(bus, 0); // 扩展扫描子总线
|--> pci_scan_slot(bus, devfn); // 递归扫描插槽,广度优先
|--> pci_scan_single_device() // 扫描单个设备
|--> pci_scan_device(bus, devfn); // 扫描插槽并创建PCI设备
|--> pci_setup_device() // 初始化设备
|--> pci_read_bases() // 读取BAR资源
|--> pci_device_add(dev, bus); // 添加PCI设备
|--> pci_scan_bridge_extend(bus, dev, max); // 扫描桥设备
|--> pci_add_new_bus(..) // 为桥创建子总线
|--> child = pci_alloc_child_bus(parent, dev); // 分配子总线
|--> child->dev.class = &pcibus_class; // 设置子总线类
|--> dev_set_name(&child->dev, "%04x:%02x", ...); // 设置子总线名称
|--> child->bridge = get_device(&bridge->dev); // 获取桥设备
|--> for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
|--> child->resource[i] = &bridge->resource[...]; // 复制桥资源
|--> child->resource[i]->name = child->name; // 设置资源名称
|--> device_register(&child->dev); // 注册子总线设备
|--> pci_scan_child_bus_extend(bus, 0); // 扩展扫描子总线
|--> pci_scan_slot(bus, devfn); // 递归扫描插槽,广度优先
|--> pci_scan_bridge_extend(bus, dev, max);
|--> pci_assign_unassigned_root_bus_resources() // 分配未分配的根总线资源
|--> __pci_bus_size_bridges() // 调整桥资源大小
|--> pci_bridge_check_ranges() // 检查桥资源范围
|--> pbus_size_io() // 调整I/O资源大小
|--> pbus_size_mem() // 调整内存资源大小
|--> __pci_bus_assign_resources() // 分配总线资源
|--> pbus_assign_resources_sorted() // 排序分配资源
|--> __dev_sort_resources() // 排序设备资源
|--> pdev_sort_resources() // 排序PCI设备资源
|--> __assign_resources_sorted() // 排序分配资源
|--> assign_requested_resources_sorted() // 分配请求资源
|--> pci_assign_resource() // 分配单个资源
|--> __pci_assign_resource() // 内部分配资源
|--> pci_bus_alloc_resource() // 从总线分配资源
|--> pci_bus_alloc_from_region() // 从区域分配资源
|--> pci_clip_resource_to_region() // 裁剪资源到区域
|--> pcibios_bus_to_resource() // 转换总线到资源
|--> pci_update_resource() // 更新资源
|--> pci_std_update_resource() // 标准更新资源
|--> pci_setup_bridge(); // 设置桥资源
|--> __pci_setup_bridge()
|--> pci_setup_bridge_io() // 设置桥I/O资源
|--> pci_setup_bridge_mmio() // 设置桥内存资源
|--> pci_setup_bridge_mmio_pref() // 设置桥预取内存资源
资源分配流程分析
(1)ACPI资源获取:
pci_acpi_root_prepare_resources()
从ACPI的 _CRS
方法中获取根总线资源。pci_acpi_root_add_resources()
将这些资源添加到根总线。(2)根总线创建与扫描:
pci_create_root_bus()
创建根PCI总线并注册主机桥。pci_scan_child_bus()
递归扫描子总线,包括设备插槽和桥设备。(3)设备资源读取:
pci_scan_device()
读取设备的BAR资源。pci_setup_device()
初始化设备并记录BAR资源。(4)资源分配:
pci_assign_unassigned_root_bus_resources()
分配未分配的根总线资源。__pci_bus_size_bridges()
调整桥资源大小。__pci_bus_assign_resources()
分配总线资源,包括排序和分配单个资源。(5)桥资源设置:
pci_setup_bridge()
设置桥的I/O和内存资源。__pci_setup_bridge()
分别设置桥的I/O、内存和预取内存资源。