【PCIE-3】---PCIE设备的枚举扫描(经典好文)
前面两个小节大致总结了下PCIE的基本知识,算是扫盲篇吧。本文主要总结PCIE设备的枚举扫描过程,此部分才是PCIE模块的重点,无论是在BIOS下还是系统驱动下都会用到。
按照国际惯例,先列问题:
1. 系统如何判断PCIE设备是否在位?
2. 设备中的配置空间的数据一开始就有嘛?谁写的?
3. Bus号,Dev号,和Fun号与硬件有关系嘛?P
4. Bridge和Device的区别?
5. Device和Function的区别?
6. 枚举的流程是怎么样的?
系统如何判断PCIE设备是否在位
BIOS代码在枚举设备的时候,会去读每个设备的VID,如果读到的是不是FFFF,则表示此位置有设备存在,若为FFFF,则表示设备不存在。DID&VID是出厂时就固定在PCIE设备配置空间中的数据,表示每个不同的设备,同理,我们在BIOS中也可通过判断这个ID值来寻找指定的PCIE设备。
设备中的配置空间的数据一开始就有嘛?谁写的?
每个设备在出厂时,其配置空间中的值都有一些default值,枚举该设备时为每个设备分配BDS号和内存资源时,会再写入部分值。
Bus号,Dev号,和Fun号与硬件有关系嘛?硬件链接与BDS号的分配是否有关?
这个问题很关键,我想应该大部分的同学都会弄得不是很清楚,笔者一直以为BDS号是在枚举过程中全部由软件分配的,但实际应该不是这样的。与多个同事讨论后,基本断定:Dev和Fun号应该是和硬件设计有关系,也就是硬件就已经固定,软件的枚举过程是更多的是在分配Bus号。为什么这么说呢?这里要着重讲解下:
PCI是总线结构,而PCIE已经是点对点机构,这个点对点结构很重要,待会重点讲到。首先先来看下一个典型的Pcie系统框图如下:

从CPU出来,有一个Root Compex(RC),从PCIE设备断端来看的,我更倾向于简单的把这个RC理解为一个HostBridge,这样可能大家都会理解的更加清楚。OK,从RC出来,最典型也是最简单的结构就是一个Root Port和一个Endpoint设备直接组成一个点对点设备,重点来了哈,这里面我们仅仅挂载了一个端点设备,但当一个root port仅和一个EndPoint设备相连时,该EndPoint设备就需要一条Bus,我和同事讨论了下,大致原因是从CPU出来的每个port都是一个Bridge,有一个Bridge就要对应一条Bus,即使该Bus下仅有一个Dev,也需要给他单独分配一个Bus号。
上面介绍了一个Root Port和一个EndPont设备相连时的情况,这是最典型的PCIE点对点结构,现在讨论一个Port下挂载多个设备时要怎么做? 这种情况下一般就需要Switch了,Switch物理上一般是一个芯片,我更倾向于把他理解为一个分线器,RootPort对应一条Bus,该Bus由于Switch的存在可以挂载多个dev,也就是对应多个Dev的情况。
那么问题中所说的每个设备的Dev和Fun是由硬件固定的是怎么回事呢?那加入这两个固定,对于到软件枚举又如何对应呢?情况大致是这样:
a. Dev和Fun硬件固定,这个是由硬件决定,为何这样定,后续我做实验后贴数据上来,现在我们就这样认为;
b. 上面已经介绍了PCIE点对点结构,从一个Port出来即使仅有一个设备,也需要给该设备重新分配一条Bus。那么假设我插入了两个设备,在没有挂接到switch上时,这两个设备正如上面所说,组成了root port - endpoint dev的两个设备对,每个设备在枚举时,都会被重新分配一条Bus号,这种情况不会冲突。
c. 假如这两个设备接在了同一个Switch上,原本应该是Switch属于某个Bus下一个分线器,分别挂接来不同的设备,亦有不同的Dev和Fun。那么在这种情况下,如果Dev和Fun一致,枚举软件自动会把这Switch的两个port再次当成一个与Root Port类似的port。每个Port也可当作一个Bridge来使用,由于两个设备Dev和Fun一样,所以会再给这两个设备分配 两个新的Bus号,以表示区别,其他的设备则按原计划枚举扫描。大概是这样的过程。
这个说法不知道会不会颠覆部分人的认识,后续我会找个实际的PCIE设备插入不同设备来进行验证,但就目前查阅的资料来看,应该是此过程!
Bridge和Device的区别?
PCIE设备树的实现,离不开Bridge,我个人更倾向于简单一点去看带着Bridge和Device,或许可以这样说:Bridge是一条Bus的管理者,当你看到一个Bridge的时候,也会对应的有一条Bus。那Device就简单的多了,简单的一个终端设备。不过目前在我看来Bridge某种意义上也可以算的上一种Device
因为在枚举扫描的时候,接下来扫到的这个是什么?你假设它为Bus1,Dev0,Fun0,然后发现有设备,去读了配置空间的Header和ClassCode后才知道这是一个Bridge还是device,但实际上你已经假设它为一个Device了,不是嘛?哈哈
这里对桥和设备的判断是通过HeaderType和ClassCode来判断的,如下:
待添加:
Device和Function的区别?
这个问题困扰了笔者很久的时间,其实真正弄懂了以后发现,可以不用钻的太深,最大的疑惑是: Dev到底对应的是不是一个设备,Fun是不是这个设备的具体的一个功能?请教了好多人,每个人给的回复都不一样。其实笔者后来总结部分情况下是这样的,对于你插入的某个设备,其Function号对应的就是它不同的功能。
枚举的流程是怎么样的?
又是一长串的知识扫盲,以上问题都是困扰了笔者许久的问题,因此单独列出来,作下说明,希望能解答和我有同样疑惑的你们。接下来就要进入正式的枚举过程了,如下图是枚举之后的两个图,希望各位同学对着这两个图,来看这个过程,你就会知道枚举到底是怎么回事了?

1. 对CPU来说,最开始仅仅知道Bus0的存在,Bus0下面都有什么设备,PCIE树是怎么样的一概不知。因此首先从Bus0,Dev0(桥A)开始,先去读Dev0中Fun0的DID&VID(一定是从Fun0开始),看其是否返回0,如果不为0则表示设备存在,继续下一步。若返回FFFF,则Dev0中没有Fun0(任何设备的第一种功能一定是0),因此该设备不存在,继续探查Bus0,Dev1,Fun0
2. Bus0,Dev0,Fun0存在,读其Header寄存器的值为“1”,则表示这是一个PCI To PCI Bridge(桥B),每一个bridge对应一个Bus,那么接下来就开始填写这个Dev0,Fun0的配置空间,将其总线号寄存器设置如下:
Primary Bus Number = 0; 以下简称为PBN
Secondary Bus Nymber = 1; 以下简称为SBN
Subordinate Bus Number = 1; 以下简称为SuBN
至此,桥B知道了他管理的下游的一条Bus,编号为1,下游最远(也就是Bus号最大的那个Bus是1)
3. 更新HostBridge的SuBN = 1
4. PCIE设备扫描遵循深度优先法则,因此在进行Bus0其他Dev扫描之前,必须首先扫面Bus1
5. 软件读取Bus1,Dev0,Fun0的DID&VID,若返回不为FFFF,则该设备存在。在此查看Header寄存器值为1,表名Bus1,Dev0,Fun0依旧是一个PCI to PCI Bridge,。且Bit7是0.说明桥C是单功能设备
6. 至此开始设置桥C的总线号寄存器
Primary Bus Number = 1;
Secondary Bus Nymber = 2;
Subordinate Bus Number = 2;
然后再更新桥A,桥B的 SuBN = 2
7. 继续延续深度优先法则,开始扫描桥C下管理的Bus2总线,首先读取Dev0,Fun0的DID&VID,会发现结果与之前一直,Bus2,Dev0,Fun0设备存在,其Header寄存器值为1,表明又是一个PCI to PCI Bridge(桥D),bit7为0,说明桥D也是单功能设备。
8. 设置桥D的总线号寄存器
Primary Bus Number = 2;
Secondary Bus Nymber = 3;
Subordinate Bus Number = 3;
然后再更新桥A,桥B,桥C的 SuBN = 3
9. 继续深度优先法则,扫描桥D下的Dev0,Fun0, 发现DID&VID存在,则表示Bus3,Dev0,Fun0存在,查看其Header寄存器的值为0,表名是一台端点设备,不是在是Bridge,且Bit7是一台多功能,这就对应到某个具体设备的多个功能了,Bridge的话就仅有一个功能--桥接。
10. 然后接着扫描Bus3, Dev0, Fun1-7,会发现仅有Fun1存在。
至此基于深度扫描法则的一条支路扫描完毕,其实到这个地方大家应该就能发现,深度扫描的意思大概是什么了,简而言之就是只要有Bridge就往下一直走,走到最后不是Bridge的那个点,PCIE设备树的结构也是在这样的基础上完成的。
11. Bus3扫面完毕后,枚举软件返回到上层Bus,也就是Bus2上的其他设备。之前已经扫描国Bus2 Dev0,Fun0,并且属于这个分支的都已经扫描完毕,接下来要扫描Bus2,Dev1,Fun0. 同样DID&VID存在,且Heade寄存器表明该设备是一个Bridge设备(桥E)。
12. 设置桥E的总线号寄存器
Primary Bus Number = 2;
Secondary Bus Nymber = 4;
Subordinate Bus Number = 4;
然后再更新桥A,桥B SuBN = 4
这个地方要注意了,桥E是挂载Bus2下载设备,因此PBN = 2没有问题,但是这个SBN = 4,SuBN = 4相比大家应该也不会有任何问题吧,截止到桥E(一个桥代表有一个Bus,编号分到Bus4),该分支下最远的Bus应该就是到Bus4,所以这样设置,但是再更新上游桥的SuBN就已经没有桥C的了,这是为什么呢? --- 因为桥C和E是不同的支路。而每次更新SuBN,我们仅仅更新同一条支路上的所有Bridge上的SuBN的值。
13. Bus4(桥E)也分配好了,接下来扫描Bus4下的设备,首先就是Dev0,Fun0,其Header寄存器值为0表示是一台端点设备,bit7为0表示一台单功能设备,OK,已经到头,且此Dev0没有更多Fun,此条支路到此结束。
其他的支路扫描与上述过程类似,在此不再赘述。截至此,基本描述完PCIE设备的枚举过程,过程还是比较简单的,就是要结合很多硬件设计的知识,以及其这样设计的初衷和原理,对此的了解会更加深刻一些!
【PCIE-3】---PCIE设备的枚举扫描(经典好文)的更多相关文章
- USB协议-USB设备的枚举过程
USB主机在检测到USB设备插入后,就要对设备进行枚举了.为什么要枚举?枚举就是从设备读取各种描述符信息,这样主机就可以根据这些信息来加载合适的驱动程序,从而知道设备是什么样的设备,如何进行通信等. ...
- ABBYY FineReader 14扫描和保存文档
在ABBYY FineReader 14中您可以使用扫描"新建任务"窗口选项卡上的内置任务创建各种格式的数字文档.本文介绍使用FineReader 14扫描和保存文档的方法. 1. ...
- ABBYY FineReader 15扫描和保存文档详解
通过使用ABBYY FineReader 15 OCR文字识别软件的扫描和保存文档功能,用户可使用扫描仪或数码照相机获得图像文档,然后再转换为各种数字格式文档. 在"新任务窗口"中 ...
- PCIE笔记--PCIe错误定义与分类
转载地址:http://blog.chinaaet.com/justlxy/p/5100057782 前面的文章提到过,PCI总线中定义两个边带信号(PERR#和SERR#)来处理总线错误.其中PER ...
- Android 6.0 Kotlin 蓝牙BLE扫描(改为指定时间没有发现新设备后停止扫描使用interface)
package com.arci.myapplication import android.os.Bundleimport android.support.design.widget.Snackbar ...
- 【转】经典网文:追MM与设计模式
设计模式做为程序员的“内功心法”,越来越受到.net 社区的重视,这种变化是很可喜的,Java社区走在了我们的前面,但这种状况也许有一天会发生改变. 从追MM谈Java的23种设计模式1.FACT ...
- 移动设备如何打开RMS加密的文档
关键字:RMS. AZure RMS.IPhone.Android.Office365.Sharepoint.Exchange 最近总是碰到要求用苹果手机及安卓手机阅读RMS加密文档的需求,经过查找相 ...
- 关于CQRS(老外经典好文)
CQRS means Command Query Responsibility Segregation. Many people think that CQRS is an entire archit ...
- 经典好文:android和iOS平台的崩溃捕获和收集
通过崩溃捕获和收集,可以收集到已发布应用(游戏)的异常,以便开发人员发现和修改bug,对于提高软件质量有着极大的帮助.本文介绍了iOS和android平台下崩溃捕获和收集的原理及步骤,不过如果是个人开 ...
随机推荐
- C# 16 进制字符串转 int
最近在写硬件,发现有一些测试是做 16 进制的字符串,需要把他转换为整形才可以处理. 本文告诉大家如何从 16 进制转整形 如果输入的是 0xaa 这时转换 int 不能使用 Parse 不然会出现异 ...
- H3C 通配符掩码
- 北京信息科技大学第十一届程序设计竞赛E-- kotori和素因子(深搜)
链接:https://ac.nowcoder.com/acm/contest/940/E 题目描述 kotori拿到了一些正整数.她决定从每个正整数取出一个素因子.但是,kotori有强迫症,她不允许 ...
- PowerShell 通过 WMI 获取系统信息
本文告诉大家如何通过 WMI 使用 Win32_OperatingSystem 获取设备厂商 通过下面代码可以获取 系统版本和系统是专业版还是教育版 Get-WmiObject Win32_Opera ...
- 关于redux和react-redux使用combinereducers之后的问题
最近用react写项目的时候,开始复习之前学过的redux,记录一下一些坑,以防忘记 我现在的redux目录下有这么些东西 首先是index.js import { createStore } fro ...
- 今天IT告告诉我,我电脑上的java jdk属性收费滴!需卸载
敲着代码,IT突然跑来说,你电脑的Jdk版本属于收费版,目前需要卸载!啊哦...手贱!每次有更新我都更新了,Java要收费老早之前耳闻了,但是俺很少做java,一般都在.Net,所以忽略鸟.. 于是G ...
- Qt串行化的输入和输出(使用QDataStream读写QByteArray,对QIODevice直接起作用)
参考https://lug.ustc.edu.cn/sites/qtguide/ 今天看了一个介绍Qt串行化的介绍,感觉很受益,就记录了下来. 串行化(Serialization)是计算机科学中的一个 ...
- JSR303 数据检验
原文:https://blog.csdn.net/qq_28867949/article/category/7370730 一.JSR-303简介 JSR-303 是 JAVA EE 6 中的一项子规 ...
- Vijos1917 艾酱最喜欢的数字 [求绝对众数]
1.题意:第一行一个数字N,表示一共有多少个数字,第二行N个数字,保证其中至少有一个数字出现次数超过一半,任务是求出这个出现最多的数. 2.分析:本题是明显的求众数的问题,常规思路为开一个大数组,在读 ...
- Dubbo-本地测试直连
一.服务提供方 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http: ...