玖叶教程网

前端编程开发入门

PCIe扫描&枚举(pci设备扫描)

PCIe扫描流程:

1. 初始化阶段:系统上电后,BIOS会初始化PCIe总线,并开始扫描已连接的PCIe设备。

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_PCIEPORTBUS
	ret = bus_register(&pcie_port_bus_type);
	if (ret)
		return ret;
#endif
	dma_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);

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言