PCIe枚举源码分析

news2024/10/6 4:11:42

枚举的过程也就是RC的系统软件通过配置空间访问来确定以及扫描整个总线拓扑的过程。
PCIe的拓扑结构如下:
在这里插入图片描述

• Root Complex是树的根,它一般实现了一个主桥设备(host bridge), 一条内部PCIe总线(BUS 0),以及通过若干个PCI bridge扩展出一些root port。host bridge可以完成CPU地址到PCI域地址的转换,pci bridge用于系统的扩展,没有地址转换功能;
• Switch是转接器设备,目的是扩展PCIe总线。switch中有一个upstream port和若干个downstream port, 每一个端口都相当于一个pci bridge
• PCIe ep device是叶子节点设备,比如pcie网卡,显卡等

对CPU来说,最开始仅仅知道Bus0的存在,Bus0下面都有什么设备,PCIE树是怎么样的是不知道的。因此首先从Bus0,Dev0开始,先去读Dev0中Fun0的DID&VID(一定是从Fun0开始),看其是否返回0,如果不为0则表示设备存在,继续下一步。若返回FFFF,则Dev0中没有Fun0(任何设备的第一种功能一定是0),因此该设备不存在,继续探查Bus0,Dev1,Fun0

PCIe枚举过程一般分为三步:
1.创建根节点
2.注册初始化根节点以及枚举根节点下所有设备
3.为根节点下设备分配资源

1、创建根节点:
在dw_pcie_host_init中通过devm_pci_alloc_host_bridge()分配host bridge结构体
pci_register_host_bridge()注册上一步的host bridge,主要是为host bridge数据结构注册对应的设备,创建了一个根总线pci_bus, 也为该pci_bus数据结构注册一个设备并填充初始化的数据。
1、注册了bridge对应的Device,其目录为/sys/devices/pciNNNN:NN,NNNN:NN表示Domain Number、Bus Number:
在这里插入图片描述

2、创建了根总线的pci_bus对象
在这里插入图片描述

3、将根总线的Device注册到sysfs中:
在这里插入图片描述

它们对应的目录位于/sys/devices/pciNNNN:NN/pci_bus/XXXX:XX/…/ZZZZ:ZZ。

pci数据结构

1、pci_host_bridge
    Host Bridge连接CPU和PCI系统
2、pci_bus
    pci_bus包括节点信息、父总线pci_bus、设备链表、设备操作函数等信息
3、pci_bus_type
    与pci_bus不同,该结构体是设备总线驱动模型里的总线
4、pci_driver
    pci设备驱动
5、pci_dev
    描述PCI设备,比如PCI-to-PCI桥设备等

2、枚举根节点下所有设备:
暂时无法在飞书文档外展示此内容

枚举流程关键函数调用关系

+-> dw_pcie_host_init
    +-> pci_host_probe
        +-> pci_scan_root_bus_bridge
            +-> pci_scan_child_bus()
                +-> pci_scan_child_bus_extend()
                    +-> for dev range(0, 256) //枚举device
                           pci_scan_slot()枚举单个设备,里面循环的读取每个逻辑function的id
                                +-> pci_scan_single_device()
                                    +-> pci_scan_device()
                                        +-> pci_bus_read_dev_vendor_id()
                                        +-> pci_alloc_dev()
                                        +-> pci_setup_device()
                                    +-> pci_device_add()//将pci_dev填充数据添加到pci总线设备列表中,并注册相应的ko
                    +-> for each pci bridge //枚举bridge
                        +-> pci_scan_bridge_extend()

pci_scan_slot(): 一条pcie总线最多32个设备,每个设备最多8个function, 所以这里pci_scan_child_bus枚举了所有的pcie function, 调用了pci_scan_device 256次尝试获取对应的vendor id和device id, pci_scan_slot调用pci_scan_single_device()配置当前总线下的所有pci设备。
drivers/pci/probe.c +2826
[图片]

drivers/pci/probe.c +2622
[图片]

同时,在扫描完所有的device之后还将aspm进行了初始化
pci_scan_slot(bus, devfn)
±> pci_scan_single_device(bus, devfn)
±> pci_scan_device(bus, devfn)
±> pci_alloc_dev(bus)
±> pci_setup_device(dev)
±> pci_device_add(dev, bus)

pci_scan_single_device(): 进一步调用 pci_scan_device() 和 pci_add_device() 。pci_scan_device 先去通过配置空间访问接口读取设备的vendor id, 如果60s没读到,说明没有找到该设备。 如果找到该设备,则通过 pci_alloc_dev 创建 pci_dev 数据结构,并对pci的配置空间进行一些配置。pci_add_device 软件将 pci dev 添加到设备list中。
[图片]

pci_scan_device()
±> pci_bus_generic_read_dev_vendor_id()
±> pci_bus_wait_crs()
±> pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)
具体获取vendor id和device id的时候, 通过pci_bus_read_config_dword函数读取0x00地址,因为每次读一个DW(32bit)所以传入PCI_VENDOR_ID可以把vendor id和device id读出来
[图片]

[图片]

扫描完bus上pcie device信息之后通过pci_setup_device() 获取 pci 设备信息,中断号,BAR地址 和 大小 (使用 pci_read_bases 就是往BAR地址写1来计算的),并保存到pci_dev->resources中。

pci_setup_device函数会将能够读取到vendor id和device id的设备填充信息,包括class,memory,IO-space address以及IRQ line等,通过往bar中写入全“1”,再读出来确定bar的大小也是在该函数中调用的
[图片]

在这里插入图片描述
![[图片]](https://img-blog.csdnimg.cn/63e6b56a54154ff591b4e51e3a3e54c4.png

[图片]

从配置空间header中读取irq_line到dev中
![[图片](https://img-blog.csdnimg.cn/cd3c76640dd84f09ad3745d96ffe2a2a.png)

至此,现在我们已经扫描完了host bridge下的bus和dev了,还有bridge没有扫描,
pci_scan_bridge_extend() 就是用于扫描 pci桥和 pci桥下的所有设备, 这个函数会被调用2次,第一次是处理 BIOS 已经配置好的pci桥, 这个是为了兼容各个架构所做的妥协。通过2次调用 pci_scan_bridge_extend 函数,完成所有的pci桥的处理
3、为根节点下设备分配资源
pcie枚举完成后,pci总线号已经分配,pcie ecam的映射、 pcie设备信息、BAR的个数及大小等也已经ready, 但此时并没有给各个pci device的BAR, pci bridge的mem, I/O, prefetch mem的base/limit寄存器分配资源。
这时就需要走到pcie的资源分配流程,整个资源分配的过程就是从系统的总资源里给每个pci device的bar分配资源,给每个pci桥的base, limit的寄存器分配资源。
pcie资源分配的入口在pci_bus_assign_resources()
[图片]

[图片]

如果定义了宏pci_has_flag,则会使用现有配置,不进行对齐操作直接对资源进行分配
在调用pci_bus_assign_resources()之前,先调用pci_bus_size_bridges()
pci_bus_size_bridges(): 用深度优先递归确定各级pci桥上base/limit的大小,会记录在pci_dev->resource[PCI_BRIDGE_RESOURCES]中。
资源分配流程关键函数调用关系

+-> pci_bus_size_bridges(bus);
+-> pci_bus_assign_resources(bus);
  +-> __pci_bus_assign_resources
    +-> pbus_assign_resources_sorted
          /* pci_dev->resource[]里记录有想申请的资源的大小, 
          * 把这些资源按对齐的要求排序
          * 比如资源A要求1K地址对齐,资源B要求32地址对齐
          * 那么资源A排在资源B前面, 优先分配资源A
          */
          list_for_each_entry(dev, &bus->devices, bus_list)
                  __dev_sort_resources(dev, &head);
          // 分配资源
          +-> __assign_resources_sorted
          +-> assign_requested_resources_sorted(head, &local_fail_head);
              +-> pci_assign_resource
                  +-> ret = _pci_assign_resource(dev, resno, size, align);
                            // 分配地址空间
                      +-> __pci_assign_resource
                          +-> pci_bus_alloc_resource
                              +-> pci_bus_alloc_from_region
                                  /* Ok, try it out.. */
                                  +-> ret = allocate_resource(r, res, size, ...);
                                      +-> err = find_resource(root, new, size,...);
                                          +-> __find_resource
                                              
                                              // 从资源链表中分配地址空间
                                              // 设置pci_dev->resource[]
                                              new->start = alloc.start;
                                              new->end = alloc.end;
                  // 把对应的PCI地址写入BAR
                  +-> pci_update_resource(dev, resno);
                      +-> pci_std_update_resource
                          /* 把CPU地址转换为PCI地址: PCI地址 = CPU地址 - offset 
                           * 写入BAR
                           */
                          pcibios_resource_to_bus(dev->bus, &region, res);
                          new = region.start;
                          reg = PCI_BASE_ADDRESS_0 + 4 * resno;
                          pci_write_config_dword(dev, reg, new);

pcie的资源枚举过程可以概况如下:

  1. 获取上游pci 桥设备所管理的系统资源范围

  2. 使用DFS对所有的pci ep device进行bar资源的分配 (drivers/pci/setup-res.c +25)
    [图片]

  3. 使用DFS对当前pci桥设备的base和limit的值,并对这些寄存器进行更新(drivers/pci/setup-bus.c +665)
    ![[图片](https://img-blog.csdnimg.cn/665d19769575410c80dcc25e7fb2d1a9.png)

根据当前bridge windows可分为三种情况:
drivers/pci/setup-bus.c +574
[图片]

drivers/pci/setup-bus.c +611
[图片]

drivers/pci/setup-bus.c +630
[图片]

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

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

相关文章

性能测试监控指标及分析调优指南

目录 一、哪些因素会成为系统的瓶颈 二、哪些指标做为衡量系统的性能 三、性能测试注意的问题 四、定位性能问题的时候,可以使用自下而上的策略分析排查 五、优化性能问题的时候,可以使用自上而下的策略进行优化 一、哪些因素会成为系统的瓶颈 CPU&…

Vercel 部署的项目发现APIkeys过期了怎么办

好不容易部署的Vercel,发现APIkeys过期了显示,查了查资料发现只要更新下新的apikeys,然后再重新部署下就好了。 重新设置APIkeys 1.1. 进去 Vercel 项目内部控制台,点击顶部的 Settings 按钮; 1.2 点击环境变量Enviorn…

K8S系列文章之 开源的堡垒机 jumpserver

一、jumpserver作为一款开源的堡垒机,不管是企业还是个人,我觉得都是比较合适的,而且使用也比较简单。 二、这里记录一下安装和使用过程。 1、安装,直接docker不是就行 version: 3 services:xbd-mysql:image: mysql:8.0.19restart…

Spring Data JPA源码

导读: 什么是Spring Data JPA? 要解释这个问题,我们先将Spring Data JPA拆成两个部分,即Sping Data和JPA。 从这两个部分来解释。 Spring Data是什么? 摘自: https://spring.io/projects/spring-data Spring Data’s mission is to provide a familiar and cons…

Nginx可视化NginxWebUI

Nginx可视化Web Github:https://github.com/cym1102/nginxWebUI 支持window、linux 安装方式支持docker、window直接运行 jar包cmd运行:port可自行替换 java -jar -Dfile.encodingUTF-8 D:/软件/Nginx-Ui/nginxWebUI-3.6.3.jar --server.port8380 --project.hom…

nvm下载安装配置

一、作用 nvm是node版本管理的工具,具有管理、下载、切换node版本等能力。经常不同项目需要依赖不同版本的node,此时nvm就能有效的解决node版本切换的问题。 二、nvm下载安装配置 (1)安装包地址 https://github.com/coreybutl…

Net强大混淆和代码保护 LogicNP Crypto Obfuscator

.Net 的强大混淆和代码保护确实有效! .Net 汇编代码保护和混淆 自动异常报告 优化和性能改进 更小和简化的部署 您希望您的混淆器... 使用高级混淆技术确保对您的代码和知识产权提供最佳保护。使用智能规则和自动排除 避免常见混淆问题。 拥有简单的用户…

P2824 [HEOI2016/TJOI2016] 排序

题目 思路 直接模拟排序肯定会TLE,所以我们想一种离线的方法:01排序 利用二分答案check一下d,设序列中大于等于d的数为1,小于d的数为0 完成后再进行排序:这样升序排列就是将0放前面1放后面,降序排列则相反…

Linux下C/C++的gdb工具与Python的pdb工具常见用法之对比

1、gdb和pdb分别是什么? 1.1、gdb GDB(GNU Debugger)是一个功能强大的命令行调试工具,由GNU项目开发,用于调试C、C等编程语言的程序。它在多个操作系统中都可以使用,包括Linux、MacOS和Windows&#xff0…

C#与C/C++交互(1)——需要了解的基础知识

【前言】 C#中用于实现调用C/C的方案是P/Invoke(Platform Invoke),让托管代码可以调用库中的函数。类似的功能,JAVA中叫JNI,Python中叫Ctypes。 常见的代码用法如下: [DllImport("Test.dll", E…

关于游戏的笔记

关于搭建秦时明月2一键端,并且开启秘境神秘商人东海寻仙幻化 1.该游戏下主要的目录 gm端 服务框架 服务端 2.修改对应的文件 C:\qs\Q2Server\server\conf_common\ManagerAddress.xmlC:\qs\Q2Server\server\conf_manager\GateServer.xml修改ip 3.启动gm startup…

SpringCloud(32):Nacos配置管理应用于分布式系统

1 从单体架构到微服务 1.1 单体架构 Web应用程序发展的早期,大部分web工程师将所有的功能模块打包到一起并放在一个web容器中运行,所有功能 模块使用同一个数据库,同时,它还提供API或者UI访问的web模块等。 尽管也是模块化逻辑…

事务,不只ACID | 京东物流技术团队

1. 什么是事务? 应用在运行时可能会发生数据库、硬件的故障,应用与数据库的网络连接断开或多个客户端端并发修改数据导致预期之外的数据覆盖问题,为了提高应用的可靠性和数据的一致性,事务应运而生。 从概念上讲,事务…

ML之特征工程进阶

术语表 术语 释义 sklearn fraternization 特征工程 Feature scaling 特征缩放 Feature Retrieval 特征检索 NLP 全称: Natural Language Processing 自然语言处理 Corpus 语料库 特征工程概述 定义 特征工程并非是一个问题,而是关于特征的一系列问题…

这应该是最全的,Fiddler手机App抓包详解,看完还不会来找我...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 什么是抓包&#…

Centos7配置网卡信息及固定IP

找到网卡配置文件 Centos7之后的网卡配置文件统一放在/etc/sysconfig/network-scripts,在这个目录会找到以ifcfg开头的,和本机网卡数量对应的配置文件,如下: 执行该命令,进入该目录: cd /etc/sysconfig/network-scripts 再执行该命令 ll …

DAY02_Spring—第三方资源配置管理Spring容器Spring注解开发Spring整合Mybatis和Junit

目录 一 第三方资源配置管理1 管理DataSource连接池对象问题导入1.1 管理Druid连接池1.2 管理c3p0连接池 2 加载properties属性文件问题导入2.1 基本用法2.2 配置不加载系统属性2.3 加载properties文件写法 二 Spring容器1 Spring核心容器介绍问题导入1.1 创建容器1.2 获取bean…

Killing LeetCode [83] 删除排序链表中的重复元素

Description 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 Intro Ref Link:https://leetcode.cn/problems/remove-duplicates-from-sorted-list/ Difficulty:Easy Tag&am…

生信学院|08月18日《基于Flow Simulation的冷链运输产品案例》

课程主题:基于Flow Simulation的冷链运输产品案例 课程时间:2023年08月18日 14:00-14:30 主讲人:江流洋 生信科技 CAE专家 1、达索仿真方案介绍 2、项目介绍 3、案例分析 请安装腾讯会议客户端或APP,微信扫描海报中的二维码…

消息队列常见问题(1)-如何保障不丢消息

目录 1. 为什么消息队列会丢消息? 2. 怎么保障消息可靠传递? 2.1 生产者不丢消息 2.2 服务端不丢消息 2.3 消费者不丢消息 3. 消息丢失如何快速止损? 3.1 完善监控 3.2 完善止损工具 1. 为什么消息队列会丢消息? 现在主流…