PCIE设备插入主板上后内核识别流程

2024-01-11 15:59

本文主要是介绍PCIE设备插入主板上后内核识别流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

当PCIe设备插入服务器后,内核会自动进行设备的探测和识别。这个过程通常包括以下几个步骤:

  1. PCIe总线枚举:当PCIe设备插入服务器时,PCIe总线会自动进行枚举,将每个设备的信息存储在PCIe配置空间中。

  2. ACPI设备枚举:内核会通过ACPI(高级配置和电源管理接口)协议来识别PCIe设备。ACPI是一种标准的系统硬件抽象层,它提供了一种与设备无关的方法来控制硬件。

  3. PCIe驱动加载:内核会根据设备的PCIe ID(厂商ID和设备ID)来加载相应的驱动程序。驱动程序通常是以模块的形式加载到内核中的。一旦驱动程序被加载,内核就会调用驱动程序的probe函数来初始化设备。

  4. 系统日志输出:内核会将设备的信息输出到系统日志中,以便管理员进行诊断和调试。

下面是内核调用栈,展示了内核如何识别PCIe设备的过程:

  1. pcie_bus_add_device():PCIe总线枚举函数,用于将PCIe设备添加到总线上。

  2. acpi_scan_add_device():ACPI设备枚举函数,用于根据设备的ACPI路径来识别设备。

  3. pci_device_add():PCIe设备添加函数,用于将设备添加到PCIe总线上。

  4. pci_scan_device():PCIe设备扫描函数,用于扫描设备的PCIe配置空间,获取设备的厂商ID和设备ID。

  5. pci_match_device():PCIe设备匹配函数,用于根据设备的PCIe ID来查找相应的驱动程序。

  6. pci_device_probe():PCIe设备探测函数,用于调用驱动程序的probe函数来初始化设备。

  7. driver_probe_device():驱动程序探测函数,用于初始化驱动程序并调用probe函数来初始化设备。

  8. device_initialize():设备初始化函数,用于初始化设备的属性和状态。

  9. sysfs_create_link():创建sysfs链接函数,用于将设备添加到sysfs文件系统中。

  10. dev_set_name():设置设备名称函数,用于设置设备的名称。

  11. driver_sysfs_add():驱动程序添加函数,用于将驱动程序添加到sysfs文件系统中。

  12. printk():系统日志输出函数,用于将设备的信息输出到系统日志中。

内核识别PCIe设备的过程是一个比较复杂的过程,需要涉及到多个函数和模块的协同工作。不同的内核版本和硬件平台可能会有一些差异,但是整个流程大致是相同的。

函数调用栈:

调用栈
pcie
解释:
subsys_initcall()是Linux内核中用于注册子系统初始化函数的宏定义,pcic_init是一个函数名,表示子系统初始化函数的入口地址。具体来说,subsys_initcall()宏定义会将pcic_init函数注册到内核子系统初始化序列中,当内核启动时,该函数会被自动调用,用于初始化PCI子系统。

PCI子系统是Linux内核中的一个重要子系统,负责管理PCI和PCIe设备。在PCI子系统初始化时,需要进行一系列的初始化操作,例如创建PCI总线、扫描PCI设备、初始化PCI设备和驱动程序、注册PCI中断等等。因此,内核会在启动时自动调用PCI子系统初始化函数,以确保PCI子系统正确地初始化。

subsys_initcall()宏定义是用于将初始化函数注册到内核子系统初始化序列中的,其中subsys表示子系统名称,initcall表示初始化函数类型。在内核启动时,内核会按照子系统初始化序列的顺序依次调用各个子系统的初始化函数,以完成整个内核的初始化。

bar


/** Main entry point from the PCI subsystem.*/
static int __init pcic_init(void)
{struct linux_pcic *pcic;/** PCIC should be initialized at start of the timer.* So, here we report the presence of PCIC and do some magic passes.*/if(!pcic0_up)return 0;pcic = &pcic0;/**      Switch off IOTLB translation.*/writeb(PCI_DVMA_CONTROL_IOTLB_DISABLE, pcic->pcic_regs+PCI_DVMA_CONTROL);/**      Increase mapped size for PCI memory space (DMA access).*      Should be done in that order (size first, address second).*      Why we couldn't set up 4GB and forget about it? XXX*/writel(0xF0000000UL, pcic->pcic_regs+PCI_SIZE_0);writel(0+PCI_BASE_ADDRESS_SPACE_MEMORY, pcic->pcic_regs+PCI_BASE_ADDRESS_0);pcic_pbm_scan_bus(pcic);return 0;
}

这是Linux内核中的一个PCI子系统初始化函数,名为pcic_init()。该函数是PCI子系统的入口函数,用于初始化PCI控制器。具体来说,该函数会进行以下操作:

  1. 检查PCI控制器是否存在,如果不存在则直接返回。
  2. 获取PCI控制器的结构体指针,并关闭PCI控制器的IOTLB转换。
  3. 增加PCI内存空间的映射大小。
  4. 扫描PCI总线,探测PCI设备并加载相应的驱动程序。

该函数主要是针对Sun Sparc体系结构的PCI控制器进行初始化的,其中包括关闭IOTLB转换和增加PCI内存空间的映射大小等操作。在最后,该函数会调用pcic_pbm_scan_bus()函数来扫描PCI总线并探测PCI设备。在探测到PCI设备后,PCI子系统会自动加载相应的驱动程序,从而完成PCI设备的初始化和驱动加载操作。

总之,pcic_init()函数是Linux内核中一个非常重要的PCI子系统初始化函数,用于初始化PCI控制器并扫描PCI总线,是PCI子系统的入口函数。

static void __init pcic_pbm_scan_bus(struct linux_pcic *pcic)
{struct linux_pbm_info *pbm = &pcic->pbm;pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, &pcic_ops, pbm);if (!pbm->pci_bus)return;#if 0 /* deadwood transplanted from sparc64 */pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);pci_record_assignments(pbm, pbm->pci_bus);pci_assign_unassigned(pbm, pbm->pci_bus);pci_fixup_irq(pbm, pbm->pci_bus);
#endifpci_bus_add_devices(pbm->pci_bus);
}

这是Linux内核中用于PCI总线扫描的函数,名为pcic_pbm_scan_bus()。该函数的作用是扫描PCI总线,探测PCI设备并加载相应的驱动程序。具体来说,该函数会进行以下操作:

  1. 获取PCI总线信息结构体指针,并调用pci_scan_bus()函数扫描PCI总线,探测PCI设备并加载相应的驱动程序。
  2. 如果扫描失败,则直接返回。
  3. 调用pci_bus_add_devices()函数将PCI设备添加到总线上。

在PCI总线扫描过程中,PCI子系统会自动探测PCI设备并加载相应的驱动程序。在驱动程序中,可以使用pci_register_driver()函数将驱动程序注册到PCI子系统中,从而使得驱动程序能够正确地探测和管理PCI设备。在探测到PCI设备后,PCI子系统会自动加载相应的驱动程序,从而完成PCI设备的初始化和驱动加载操作。

pcic_pbm_scan_bus()函数是Linux内核中一个用于PCI总线扫描的函数,用于探测PCI设备并加载相应的驱动程序,是PCI子系统的重要组成部分。


/*** pci_scan_child_bus_extend() - Scan devices below a bus* @bus: Bus to scan for devices* @available_buses: Total number of buses available (%0 does not try to*		     extend beyond the minimal)** Scans devices below @bus including subordinate buses. Returns new* subordinate number including all the found devices. Passing* @available_buses causes the remaining bus space to be distributed* equally between hotplug-capable bridges to allow future extension of the* hierarchy.*/
static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,unsigned int available_buses)
{unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0;unsigned int start = bus->busn_res.start;unsigned int devfn, fn, cmax, max = start;struct pci_dev *dev;int nr_devs;dev_dbg(&bus->dev, "scanning bus\n");/* Go find them, Rover! */for (devfn = 0; devfn < 256; devfn += 8) {nr_devs = pci_scan_slot(bus, devfn);/** The Jailhouse hypervisor may pass individual functions of a* multi-function device to a guest without passing function 0.* Look for them as well.*/if (jailhouse_paravirt() && nr_devs == 0) {for (fn = 1; fn < 8; fn++) {dev = pci_scan_single_device(bus, devfn + fn);if (dev)dev->multifunction = 1;}}}/* Reserve buses for SR-IOV capability */used_buses = pci_iov_bus_range(bus);max += used_buses;/** After performing arch-dependent fixup of the bus, look behind* all PCI-to-PCI bridges on this bus.*/if (!bus->is_added) {dev_dbg(&bus->dev, "fixups for bus\n");pcibios_fixup_bus(bus);bus->is_added = 1;}/** Calculate how many hotplug bridges and normal bridges there* are on this bus. We will distribute the additional available* buses between hotplug bridges.*/for_each_pci_bridge(dev, bus) {if (dev->is_hotplug_bridge)hotplug_bridges++;elsenormal_bridges++;}/** Scan bridges that are already configured. We don't touch them* unless they are misconfigured (which will be done in the second* scan below).*/for_each_pci_bridge(dev, bus) {cmax = max;max = pci_scan_bridge_extend(bus, dev, max, 0, 0);/** Reserve one bus for each bridge now to avoid extending* hotplug bridges too much during the second scan below.*/used_buses++;if (cmax - max > 1)used_buses += cmax - max - 1;}/* Scan bridges that need to be reconfigured */for_each_pci_bridge(dev, bus) {unsigned int buses = 0;if (!hotplug_bridges && normal_bridges == 1) {/** There is only one bridge on the bus (upstream* port) so it gets all available buses which it* can then distribute to the possible hotplug* bridges below.*/buses = available_buses;} else if (dev->is_hotplug_bridge) {/** Distribute the extra buses between hotplug* bridges if any.*/buses = available_buses / hotplug_bridges;buses = min(buses, available_buses - used_buses + 1);}cmax = max;max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1);/* One bus is already accounted so don't add it again */if (max - cmax > 1)used_buses += max - cmax - 1;}/** Make sure a hotplug bridge has at least the minimum requested* number of buses but allow it to grow up to the maximum available* bus number of there is room.*/if (bus->self && bus->self->is_hotplug_bridge) {used_buses = max_t(unsigned int, available_buses,pci_hotplug_bus_size - 1);if (max - start < used_buses) {max = start + used_buses;/* Do not allocate more buses than we have room left */if (max > bus->busn_res.end)max = bus->busn_res.end;dev_dbg(&bus->dev, "%pR extended by %#02x\n",&bus->busn_res, max - start);}}/** We've scanned the bus and so we know all about what's on* the other side of any bridges that may be on this bus plus* any devices.** Return how far we've got finding sub-buses.*/dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);return max;
}

这段代码是用于扫描PCI子总线的函数。它接受两个参数:PCI总线对象和可用总线数。它首先迭代PCI总线下的所有设备,并扫描它们。然后,它为SR-IOV能力保留总线。接下来,它计算总线上的热插拔桥和普通桥的数量,并在第一次扫描中扫描已配置的桥。它还分配了额外的可用总线,以便将其分配给热插拔桥。在第二次扫描中,它扫描需要重新配置的桥,并将额外的总线分配给热插拔桥。最后,它确保热插拔桥至少拥有最小数量的总线,并返回扫描到的最大总线号。

这篇关于PCIE设备插入主板上后内核识别流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/594908

相关文章

redis-sentinel基础概念及部署流程

《redis-sentinel基础概念及部署流程》RedisSentinel是Redis的高可用解决方案,通过监控主从节点、自动故障转移、通知机制及配置提供,实现集群故障恢复与服务持续可用,核心组件包... 目录一. 引言二. 核心功能三. 核心组件四. 故障转移流程五. 服务部署六. sentinel部署

SpringBoot集成XXL-JOB实现任务管理全流程

《SpringBoot集成XXL-JOB实现任务管理全流程》XXL-JOB是一款轻量级分布式任务调度平台,功能丰富、界面简洁、易于扩展,本文介绍如何通过SpringBoot项目,使用RestTempl... 目录一、前言二、项目结构简述三、Maven 依赖四、Controller 代码详解五、Service

MySQL 临时表与复制表操作全流程案例

《MySQL临时表与复制表操作全流程案例》本文介绍MySQL临时表与复制表的区别与使用,涵盖生命周期、存储机制、操作限制、创建方法及常见问题,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小... 目录一、mysql 临时表(一)核心特性拓展(二)操作全流程案例1. 复杂查询中的临时表应用2. 临时

MySQL 升级到8.4版本的完整流程及操作方法

《MySQL升级到8.4版本的完整流程及操作方法》本文详细说明了MySQL升级至8.4的完整流程,涵盖升级前准备(备份、兼容性检查)、支持路径(原地、逻辑导出、复制)、关键变更(空间索引、保留关键字... 目录一、升级前准备 (3.1 Before You Begin)二、升级路径 (3.2 Upgrade

Spring Boot 中的默认异常处理机制及执行流程

《SpringBoot中的默认异常处理机制及执行流程》SpringBoot内置BasicErrorController,自动处理异常并生成HTML/JSON响应,支持自定义错误路径、配置及扩展,如... 目录Spring Boot 异常处理机制详解默认错误页面功能自动异常转换机制错误属性配置选项默认错误处理

Spring Boot从main方法到内嵌Tomcat的全过程(自动化流程)

《SpringBoot从main方法到内嵌Tomcat的全过程(自动化流程)》SpringBoot启动始于main方法,创建SpringApplication实例,初始化上下文,准备环境,刷新容器并... 目录1. 入口:main方法2. SpringApplication初始化2.1 构造阶段3. 运行阶

Linux之platform平台设备驱动详解

《Linux之platform平台设备驱动详解》Linux设备驱动模型中,Platform总线作为虚拟总线统一管理无物理总线依赖的嵌入式设备,通过platform_driver和platform_de... 目录platform驱动注册platform设备注册设备树Platform驱动和设备的关系总结在 l

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

Ubuntu 24.04启用root图形登录的操作流程

《Ubuntu24.04启用root图形登录的操作流程》Ubuntu默认禁用root账户的图形与SSH登录,这是为了安全,但在某些场景你可能需要直接用root登录GNOME桌面,本文以Ubuntu2... 目录一、前言二、准备工作三、设置 root 密码四、启用图形界面 root 登录1. 修改 GDM 配

Spring Security中用户名和密码的验证完整流程

《SpringSecurity中用户名和密码的验证完整流程》本文给大家介绍SpringSecurity中用户名和密码的验证完整流程,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 首先创建了一个UsernamePasswordAuthenticationTChina编程oken对象,这是S