PCIe总线-Linux内核PCIe设备枚举流程分析(十三)

news2024/9/20 16:36:11

1.简介

当系统启动时或者有新的PCIe设备接入时,PCIe主机会扫描PCIe总线上的PCIe设备,读取设备配置空间信息,建立设备的拓扑关系,然后为设备分配资源(如内存空间、I/O空间、中断、总线编号等),最后根据设备的类型匹配驱动。下面以Linux内核为例,介绍PCIe主机枚举PCIe设备的流程。

2.pci_host_probe

Linux内核中,PCIe RC驱动调用pci_host_probe函数枚举PCIe总线上的所有设备。pci_host_probe的执行流程如下图所示,总共有5部分,具体如下:

  1. 调用pci_scan_root_bus_bridge函数扫描总线上的桥和设备。
    1. 首先注册Host主桥。主桥上有root bus,root bus的编号通常是0。然后设置msi_controller和msi_domain,用于MSI/MSI-X中断。最后将root bus的数据结构挂到pci_root_buses上,便于统一管理。
    2. 调用pci_scan_child_bus函数扫描设备,后面详细介绍。
  2. 确定连接到PCI总线上的PCI桥(包括标准PCI桥和PCI到PCIe桥)所需的地址空间大小,并分配相应的内存和I/O资源。最后,调整桥设备的子总线号,以确保正确的总线层次结构和设备访问路径。
  3. 给总线上的设备和桥分配资源,即设置配置空间的BAR、BASE、Limit等寄存器,后面详细介绍。
  4. 遍历PCIe总线上设备和桥,设置MPS、MRRS,可以参考PCIe Linux MRRS和MPS参数设置策略。
  5. 遍历PCIe总线上设备和桥,添加设备,调用PCIe总线提供的匹配方法匹配PCIe设备驱动。

pci_host_probe

3.pci_scan_child_bus

Linux内核调用pci_scan_child_bus函数,从root bus开始枚举PCIe总线上的所有设备。整个枚举流程分为三步,具体如下:

  1. 第一步:首先从root bus开始扫描。PCIe设备和功能号组合总共有256种,因此需要全部遍历一遍,devfn高5位是设备号,低3位是功能号,每次循坏扫描一个设备,默认扫描Fun0,如果设备是多功能设备,则会扫描其他Function。扫描设备的单个Function使用pci_scan_single_device函数,后面详细介绍。
  2. 第二步:若bus下面挂的桥被BIOS配置过,则扫描桥下面的设备,如此递归,直到扫面完整个总线,若桥没有被配置过或需要重新配置,则先跳过,在第三步重新配置。对于被BIOS配置过的桥,其Primary、Secondary、Subordinate已经设置,内核会创建pci_bus,为其预留bus编号,同时将bus资源管理起来。
  3. 第三步:遍历总线上的所有桥,设置所有没有配置的桥,即设置桥的Primary、Secondary、Subordinate,同时创建pci_bus,为其预留bus编号,同时将bus资源管理起来。

pci_scan_child_bus

4.pci_scan_single_device

内核调用pci_scan_single_device函数扫描设备的单个Function,先扫描Function,再添加Function。执行的流程如下:

  1. 内核通过读取Function的VID来判断设备是否存在,超时时间为60000毫秒。若读取到的VID是0xffffffff、0x00000000、0x0000ffff、0xffff0000,则表示没有设备,若读取到的是0x1,说明返回了CRS的completion包,设备还没准备好,延时循环读取PCI_VENDOR_ID(等待设备准备好),若读到CRS的completion包,则延时翻倍。
  2. 扫描到Function,则分配pci_dev数据结构,每个Function对对应一个pci_dev数据结构。
  3. 初始化Function(设备)。
    1. 设置Function的bus_typepci_bus_type,用于匹配和初始化PCIe设备驱动。
    2. 读取Function的port类型,通过PCI Express Capability registers获取,主要是确定Function是upstream还是downstream ports。
    3. 获取Function的配置空间大小。通过读偏移地址为256处的值,若读到有效的扩展空间头则为4096字节,否则为256字节。
    4. 解析非桥设备的PCIe配置空间头。具体有PCI_INTERRUPT_PINPCI_BASE_ADDRESSPCI_SUBSYSTEM_VENDOR_IDPCI_SUBSYSTEM_IDPCI_BASE_ADDRESS会读取Function的BAR,详细的后面说明。
    5. 解析桥设备的PCIe配置空间头。具体有PCI_INTERRUPT_PINPCI_BASE_ADDRESSPCI_EXP_SLTCAPPCI_CAP_ID_SSVIDPCI_SSVID_VENDOR_ID和桥的地址空间。
  4. 配置、初始化和管理Function。主要是配置MPS、Extended Tags、ordering、LTR等,初始化MSI、PM、VPD、IOV、ATS、AER等,将pci_dev挂到pci_busdevices链表,最后建立MSI IRQ domain。

pci_scan_single_device

4.1.解析BAR

内核使用pci_read_bases函数解析Function的BAR和ROM,包括桥的BAR0和BAR1,非桥设备的BAR0-BAR5。BAR的使用方法参考3.3节PCIe总线-配置空间介绍(三)。pci_read_bases函数的执行流程如下所示,主要有:

  1. 先读BAR,读出的全为F,表示BAR不正常,意味着设备可能已经被移除,不再存在于总线上,或者设备已经进入了一种不能响应读操作的错误或低功耗状态。
  2. 向BAR写入0xFFFFFFFF,向ROM写入0xFFFF8000,然后再度BAR和ROM,以确定BAR和ROM的属性和大小。
  3. 将第一次读的BAR和ROM的默认值写回,恢复默认值。
  4. 根据第一次读取BAR的值,解析BAR的类型和属性。具体有IO空间IORESOURCE_IO,32位非预取存储器空间IORESOURCE_MEM、64位非预取存储器空间IORESOURCE_MEM_64(虽然BAR是64位,但实际上RC只会给BAR配置32位PCIe总线地址),预取存储器空间IORESOURCE_PREFETCH。内核会根据解析的BAR信息分配PCIe总线地址。
  5. 如果是64位BAR,则还需要解析高32位的BAR,步骤和第4步一样。
  6. 计算BAR所需地址空间大小。
  7. 将PCIe总线地址region转换成存储域地址resource,将存储域地址resource转换成PCIe总线地址Region,两者的起始地址必须相等。

pci_read_bases

4.2.解析桥windows

内核使用pci_read_bridge_windows函数解析桥设备的PCI_IO_BASEPCI_PREF_MEMORY_BASEPCI_PREF_BASE_UPPER32。若PCI_IO_BASE寄存器存在,则设置bridge->io_window = 1。若PCI_PREF_MEMORY_BASE寄存器存在,则设置bridge->pref_window = 1。若支持64位预取地址,且PCI_PREF_BASE_UPPER32寄存器存在,则设置bridge->pref_64_window = 1PCI_MEMORY_BASE寄存器必须实现,这里可不用解析,后面直接分配地址。

[drivers/pci/probe.c]
static void pci_read_bridge_windows(struct pci_dev *bridge)
{
	u16 io;
	u32 pmem, tmp;

	pci_read_config_word(bridge, PCI_IO_BASE, &io);
	if (!io) {
		pci_write_config_word(bridge, PCI_IO_BASE, 0xe0f0);
		pci_read_config_word(bridge, PCI_IO_BASE, &io);
		pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
	}
	if (io)
		bridge->io_window = 1;

	/*
	 * DECchip 21050 pass 2 errata: the bridge may miss an address
	 * disconnect boundary by one PCI data phase.  Workaround: do not
	 * use prefetching on this device.
	 */
	if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)
		return;

	pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
	if (!pmem) {
		pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,
					       0xffe0fff0);
		pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
		pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
	}
	if (!pmem)
		return;

	bridge->pref_window = 1;

	if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {

		/*
		 * Bridge claims to have a 64-bit prefetchable memory
		 * window; verify that the upper bits are actually
		 * writable.
		 */
		pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &pmem);
		pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
				       0xffffffff);
		pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
		pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, pmem);
		if (tmp)
			bridge->pref_64_window = 1;
	}
}

5.分配资源(地址)

内核使用pci_bus_assign_resources函数给PCIe桥和非桥设备分配资源。详细的流程如下所示。针对每一条子总线,递归调用__pci_bus_assign_resources分配资源。一条子总线上的设备和桥分配资源的步骤如下:

  1. 遍历子总线上的所有设备,给设备需要的资源排序。
  2. 遍历子总线上设备需要的资源,调用pci_assign_resource函数分配资源,最终会调用到allocate_resource函数,先查找资源,再请求资源。这里的资源是PCIe总线地址。
  3. 分配好资源后,调用pci_update_resource函数将资源配置到设备的BAR或ROM中,同时回读BAR和ROM进行校验。将PCIe总线地址设置到BAR或者ROM寄存器当中。
  4. 如果是桥设备,则调用pci_setup_bridge初始化桥。桥的IORESOURCE_IOIORESOURCE_MEMIORESOURCE_PREFETCH资源范围,是桥下面所有设备相同资源类型之和。

pci_bus_assign_resources

参考资料

  1. PCIEXPRESS体系结构导读
  2. PCI Express technology 3.0
  3. PCI Express® Base Specification Revision 5.0 Version 1.0
  4. Rockchip RK3588 TRM
  5. Linux kernel 5.10

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全AI大模型训练从入门到精通

前言 2022年下半年,国内安全圈内开始完chatGPT,当时在安全圈内小火了一把。大家纷纷注册去体验一把,希望chatGPT能帮助解决日常安服渗透问题。当时以为仅此而已,谁知年后大火,随后以chatGPT为代表的大语言模型&#x…

【老课推荐】基于LangChain和知识图谱的大模型医疗问答机器人项目

在当今数据驱动和人工智能主导的时代,大模型和知识图谱的结合是一个重要的研究和应用方向。大模型实战课程通过48课时,分为六个主要章节,涵盖了从基本概念到高级应用的多方面内容。学员将通过本课程学习如何使用LangChain和OpenAI进行开发&am…

Spring Boot:医疗排班系统开发的技术革新

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…

数据分析:Python语言相关性对角矩阵计算

文章目录 介绍加载R包导入数据计算连续型变量相关系数展示显著性结果图总结介绍 下三角相关系数矩阵是指相关系数矩阵中,仅展示主对角线以下部分(不包括主对角线)的值。在相关系数矩阵中,主对角线上的元素都是1(因为任何变量与自身的相关系数都是完美的1),而上三角和下…

Java笔试面试题AI答之单元测试JUnit(2)

文章目录 7. 为什么JUnit只报告单次测试中的第一次失败?8. Java中,assert是一个关键字。 这不会与JUnit的assert()方法冲突吗?9. 解释如何测试静态方法?一、直接调用测试二、隔离依赖三、使用Mock框架四、重…

助贷行业的三大严峻挑战:贷款中介公司转型债务重组业务

大家是否察觉到一种趋势?现如今,众多贷款辅助服务机构与专注于债务再构的公司之间形成了紧密的“联动”。有的选择将获取的贷款需求转介给债务重组方,有的则直接下场,动用自身资本参与债务重组业务。这一现象背后,究竟…

每日一练:合并区间

一、题目要求 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入:in…

四.海量数据实时分析-Doris数据导入导出

数据导入 1.概述 Apache Doris 提供多种数据导入方案,可以针对不同的数据源进行选择不同的数据导入方式。 数据源导入方式对象存储(s3),HDFS使用 Broker 导入数据本地文件Stream Load, MySQL LoadKafka订阅 Kafka 数据Mysql、PostgreSQL&a…

表格多列情况下,loading不显示问题

问题描述: 用element plus 做得表格,如下图,列数较多,且部分表格内容显示比较复杂,数据量中等的情况下,有一个switch 按钮,切换部分列的显示和隐藏,会发现,切换为显示的时…

单线程 TCP/IP 服务器和客户端的实现

单线程 TCP/IP 服务器和客户端的实现 文章目录 单线程 TCP/IP 服务器和客户端的实现通信流程服务端客户端 代码实现服务端客户端 运行结果 通信流程 服务端 socket:创建监听的文件描述符(socket) fd;bind:fd 和自身的 ip 和端口绑定&#x…

【Transformer】Positional Encoding

文章目录 为什么需要位置编码?预备知识三角函数求和公式旋转矩阵逆时针旋转顺时针旋转 原始Transformer中的位置编码论文中的介绍具体计算过程为什么是线性变换? 大模型常用的旋转位置编码RoPE基本原理Llama3中的代码实现 参考资料 为什么需要位置编码&a…

DPDK基础入门(五):报文转发

网络处理模块划分 Packet Input: 接收数据包,将其引入处理流程。Pre-processing: 对数据包进行初步处理,例如基本的检查和标记。Input Classification: 细化数据包的分类,例如基于协议或流进行分流。Ingress Queuing: 将数据包放入队列中进行…

【信息学奥赛题】

目录 一、计算机组成与工作原理 二、计算机信息表示 三、计算机软件系统 四、计算机网络基础 五、多媒体知识 六、数据结构 七、程序语言知识 八、知识性问题 一、计算机组成与工作原理 1.下列不属于冯诺依曼计算机模型的核心思想是(D&#xff…

Spring源码(3)Aware接口、初始化和销毁方法、@Scope、@Primary

1、目标 本文的主要目标是学习Spring源码中Aware接口、初始化和销毁方法、Scope注解、Primary注解的使用 2、Aware接口 Component public class MyBeanAware implements BeanNameAware, ApplicationContextAware {Overridepublic void setBeanName(String name) {System.out…

Linux系统本地化部署Dify并安装Ollama运行llava大语言模型详细教程

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

屏保壁纸 芝麻时钟比屏保壁纸更好看的桌面动态屏保 大气美观

屏保壁纸 芝麻时钟比屏保壁纸更好看的桌面动态屏保 大气美观,今天小编给大家带来一款非常大气美观的桌面时钟屏保,比屏保壁纸更好看,更美观的桌面屏保软件。非常有个性化哦,我们看看这种屏保主题,是不是让你眼前一亮呢…

20240908 每日AI必读资讯

新AI编程工具爆火:手机2分钟创建一个APP! - AI初创公司Replit推出的智能体——Replit Agent。开发环境、编写代码、安装软件包、配置数据库、部署等等,统统自动化! - 操作方式也是极其简单,只需一个提出Prompt的动作…

HBuilderx 安装 compile-node-sass编译工具

在使用HBuilderx工具,利用uni-app框架开发前端过程中,应用 “.scss”扩展名的的样式文件,scss作为css的预编译文件,在实际开发中是需要编译的,所以需要安装插件 compile-node-sass。 本人在CSDN下载插件“compile-node…

2.软件生命周期及流程(包含笔试/面试题)

一、软件生命周期 1.什么是软件的生命周期? 软件生命周期就是软件从开始研发到最终被废弃不用的一整个过程。 二、软件生命周期模型 1.瀑布型生命周期模型(基本不用这个模型) 最早期的模型,流程是从上而下的,如同瀑布流…

【机器人工具箱Robotics Toolbox开发笔记(二)】Matlab中机器人工具箱的下载与安装

Matlab机器人工具箱(Robotics Toolbox)可从Peter Corke教授提供的网站上免费下载。网址为:http://www.petercorke.com/Robotics_Toolbox.html。 图1 网站所提供的机器人工具箱版本 在Downloading the Toolbox栏目中单击here按钮进入下载页面,然后在该页面中填写国家、组织…