早期的PCI时期,系统为每个PCI设备分配的内存大小仅有256个Bytes。到后来的PCIE时期,随着设备性能增强,PCIE设备的配置空间扩展至4K个Bytes。在这里需要注意:
PCIE一共支持256条Bus,32个Dev,8个Fun。因此在满负载的情况下,共需内存大小 = 4k * 256 *32*8 = 256K Bytes = 256M,这个256M的内存空间是为PCIE设备准备的空间系统不可用,这也是你的内存条实际可用的总是会小于标称的主要原因之一。
PCIE设备发展向前兼容PCI,每个设备的配置空间的前256个Byte是PCI空间,后(4k-256)个Byte的空间是PCIE扩展空间,这是二者的主要区别,另外一个区别就要引出下面的一个问题:
PCI/PCIE设备配置空间的访问方式----IO访问 & 内存访问
X86系统中,对PCIE设备配置空间的地址映射一般有两种方式:内存映射和IO映射。因此开发者也可以通过内存访问或者IO访问来访问其配置空间
PCIE设备的访问离不开其Bus,Dev,Fun的编号方式,如下图寄存器所示,Bit[23:16]用来存放Bus号,共8Bit,因此解释了上述表述为何一共有256条Bus,Bit[15:11]存放Dev,共5bit可存32个Dev,Bit[10:8]存放Bus,共3Bit可存8个Fun。这也就也是了为何上述PCIE设备数一共是256个Bus,32个Dev和8个Fun。
原文链接: https://blog.csdn.net/qq_34765960/article/details/114094270
PCI总线是拓扑结构,PCI总线从0开始,不超过256,Device不超过32,Function不超过8。如下图,挂在总线0,即Bus 0上的为根(root)设备,下面还挂设备的则为桥(Bridge),不再挂设备的即为设备(Device)。挂在桥下的设备总线号必然大于桥的总线号,下图中,PCI桥片1为Bus 0,PCI设备11为bus 1,PCI设备31为Bus 3。所以PCI桥片1的从属总线是1-3。
挂在PCI总线上的所有桥或设备都有特定的编号,即为Bus,Device,Function,不会重复。CPU对于挂在root上的设备都有固定定义,查看datasheet即可。
PCIE的配置空间:
每个PCIE设备都有自己的独立的一段配置空间,该部分空间是这个设备的(可能是一段e2prom),系统会给这个设备分配一段内存空间,CPU访问这段内存空间即访问对此设备的配置空间。设备在出厂时,配置空间是有默认初始值的。
PCI设备的配置空间如下图
PCIE设备的配置空间大小以及 PCI和PCIE的配置空间有何区别联系 :
早期的PCI时期,系统为每个PCI设备分配的内存大小仅有256个Bytes。到后来的PCIE时期,随着设备性能增强,PCIE设备的配置空间扩展至4K个Bytes。在这里需要注意: PCIE一共支持256条Bus,32个Dev,8个Fun。 因此在满负载的情况下,共需内存大小 = 4k * 256 *32*8 = 256K Bytes = 256M,这个256M的内存空间是为PCIE设备准备的空间系统不可用,这也是你的内存条实际可用的总是会小于标称的主要原因之一。 PCIE设备发展向前兼容PCI,每个设备的配置空间的前256个Byte是PCI空间,后(4k-256)个Byte的空间是PCIE扩展空间,这是二者的主要区别。
: https://blog.csdn.net/szhb5251/article/details/105961892
PCI/PCIE设备配置空间的访问方式----IO访问 & 内存访问
X86系统中,对PCIE设备配置空间的地址映射一般有两种方式: 内存映射和IO映射 。因此开发者也可以通过内存访问或者IO访问来访问其配置空间。
PCIE设备的访问离不开其Bus,Dev,Fun的编号方式,如下图寄存器所示, Bit[23:16]用来存放Bus号,共8Bit,因此解释了上述表述为何一共有256条Bus,Bit[15:11]存放Dev,共5bit可存32个Dev,Bit[10:8]存放Bus,共3Bit可存8个Fun。 这也就也是了为何上述PCIE设备数一共是256个Bus,32个Dev和8个Fun。
CF8h: CONFIG_ADDRESS。PCI配置空间地址端口。
CFCh: CONFIG_DATA。PCI配置空间数据端口。
CONFIG_ADDRESS寄存器格式:
31 位:Enabled位。
23:16 位:总线编号。
15:11 位:设备编号。
10: 8 位:功能编号。
7: 2 位:配置空间寄存器编号。
1: 0 位:恒为“00”。这是因为CF8h、CFCh端口是32位端口。
如上,在CONFIG_ADDRESS端口(CF8h)填入BDF,即可以在CONFIG_DATA(CFCh)上写入或读出PCI配置空间的内容。
原文链接: https://blog.csdn.net/u013253075/article/details/80835831
无论是采用IO还是内存的方式来访问配置空间,都离不开上面的图,下面具体介绍:
1、 IO访问// CF8h/CFCH Method/ 使用PCI兼容性配置访问机制
IO应该是Intel X86架构的独有产物了,简单可理解为一段存储空间,用户通过IN/OUT指令来访问(内存的话,需要用MOVE指令来进行访问),部分设备可以映射到IO空间中,开发者通过IO端口访问这个设备,比如现在介绍的这个: 通过CF8 / CFC端口。用户可通过这组端口来对PCIE的前256个Byte进行访问,一个指定地址,一个指定数据。代码如下:
1 /*Access PCI Config Space in IO method*/
2 Address = BIT31|((BUS & 0XFF)<< 16)|((DEV & 0x1F)<<11)|((Fun & 0x7) << 8);
3 IoWrite32(0xCF8, address); //将要读取的地址写入到CF8
4 Date32 = IoRead32(0xcfc); //从CFC端口读出address的数据
以上的代码适用于PCI和PCIe寄存器的访问,对于PCIe而言,I/O方式只能访问前256个寄存器。
Bit31代表enable bit 。一定要置起来,否则不起作用。
原文链接: https://blog.csdn.net/szhb5251/article/details/105961892 MMIO访问的方式是错误的。!!!!!
使用PCI兼容性配置访问机制: 根据协议,x86上使用了两个I/O端口寄存器(OUT 和 IN 在汇编中是端口读写操作指令。端口是主机与外设进行数据交换使用的,分为数据端口,状态端口和控制端口三种。PC机给每一个端口分配了一个地址,所有端口成线性排列,形成一个独立于内存空间的I/O地址空间,一般用十六进制表示。8086中,端口地址的范围是0000H-FFFFH。 )CONFIG_ADDRESS(物理地址0CF8H)和CONFIG_DATA(0CFCH),来访问PCI配置空间。
其中Bit 31位是使能位,写1。
总线号0-255,系统中最多有256个总线。
设备号0-31,每个总线最多挂载32个设备。
功能号0-7,每个设备对多8个function。
寄存器号:待访问配置空间寄存器的编号。
原文链接: https://blog.csdn.net/mao0514/article/details/26072229
PCI设备的总线号必须被填写到IO地址CF8h的[23:16] bits
PCI设备的设备号必须被填写到IO地址CF8h的[15:11] bits
PCI设备的功能号必须被填写到IO地址CF8h的[10:8] bits
需要访问的寄存器双字地址必须被填写到IO地址CF8h的[7:2] bits
CF8h的最高位为配置位,该位必须设置为1
对于写操作,将设备的特定信息组合成一个双字(4bytes)后,写到CFCh地址
对于读操作,将设备的特定信息组合成一个双字后,把数据从CFCh读回来
原文链接: https://blog.csdn.net/geter_CS/article/details/110671519 !!!!!!!!!MMIO 正确的 !!!!!!!!!!!!!
注: IO访问仅能读取到前256个Byte,256Byte后(4k-256byte )的空间需要用内存访问
2、 内存访问// Memory Mapped Method/ 使用PCI Express增强配置访问机制
使用前述 PCI兼容配置访问机制只能访问到功能的4KB配置空间中的 PCI兼容配置寄存器区(256B),PCIE增强配置访问机制可以访问到整个4KB配置空间。两者访问方法的不同之处在于处理器向 Host-PCI桥表达自己配置访问意图的方式。映射于存储器的配置空间: PCIE增强的配置访问机制在系统存储器空间里划出一片256MB区域,将总容量为256MB的全部配置寄存器一对一地映射于这片存储器空间。 处理器访问256MB配置空间的地址含义: 原文链接: https://blog.csdn.net/geter_CS/article/details/110671519
这个其实与IO访问大同小异,配置空间全部映射到内存中,用户在确定设备地址后,即可通过内存读写的方式进行访问,如下代码:
1 /*Write date*/
2 MmioWrite32(PcieBaseAdd + Bus<<20 + Dev<<15 + Fun<<12 + offset, date); //PcieBaseAdd为PCIE在内存中的基地址
3 /*Read date*/
4 Value = MmioRead32(PcieBaseAdd + Bus<<20 + Dev<<15 + Fun<<12 + offset);
配置空间主要有两种,开发者也是搞清楚这两种即可,一是Type0:设备空间 二是Type1: Bridge空间, 桥设备空间如下图所示:下面介绍其中主要内容以及作用:(端点设备空间布局与Bridge空间类似,会简单一些,没有一堆Bus Num设备寄存器, CPU一般通过判断Header确定该设备是什么设备 )
通常我们会用CF8和CFC端口的方式访问PCIE基本配置空间。该方式支持配置机制1或者配置机制2,访问时借助in/out指令。配置机制2只在某些特定的主板上被使用。 新的设计应使用配置机制1来产生配置空间的物理操作。 这种机制使用了两个特定的32位I/O空间,即CF8h和CFCh。 这两个空间对应于PCI桥路的两个寄存器,当桥路看到CPU在局部总线对这两个I/O空间进行双字操作时,就将该I/O操作转变为PCI总线的配置操作。 寄存器CF8h用于产生配置空间的地址(CONFIG-ADDRESS),寄存器CFCh用于保存配置空间的读写数据(CONFIG-DATA)。 将要访问配置空间寄存器的总线号、设备号、功能号和寄存器号以一个双字的格式写到配置地址端口 (CF8H-CFBH),接着执行配置数据端口 (CFCH)的读和写,向配置数据口写数据即向配置空间写数据,从配置数据口读数据即从配置空间读数据。
PCI-E的配置空间大小为4096字节,如下图所示。 其中前256字节是与PCI兼容的配置寄存器。配置机制1仅仅能訪问[0-255]偏移之间的寄存器,也就是标准的PCI配置空间的寄存器。对于扩展PCIE配置空间的寄存器[256-4095],仅仅能使用MMIO内存映射方式訪问。
原文链接: https://blog.csdn.net/weixin_35051623/article/details/112150194
MMIO是另外一种读写PCIE配置空间的方式。MMIO即内存映射I/O,它是PCI规范一部分,I/O设备被放置在内存空间。从处理器角度看,内存映射I/O后系统设备访问起来和内存一样。这样访问PCI-E设备就可以使用读写内存一样的汇编指令完成,简化了程序设计的难度和接口的复杂性。 PCIE配置空间的寄存器被编址到系统的物理内存空间,在支持ACPI规范x86系统上,操作系统通过读 MCFG 表获得系统中 全部设备PCI配置空间的基地址 。操作系统在引导时,枚举PCI设备,从而知道每一个PCI设备的Bus, Device, Function号。有了 BDF 这三个编号,加上从MCFG中得到的基地址,就能够计算出给定设备的PCI配置空间在物理内存空间的地址。
软件访问指定PCIe设备的配置寄存器时,必须正确计算该寄存器映射到内存的具体地址,那么怎么计算呢,参考上图我们可以知道, busNo=0,deviceNo=0,funcNo=0的地址刚好是BAR,一条总线占用的最大空间计算如下:
SIZE_PER_BUS = 4K * 32 * 8 = 1024K = 1M = 100000h
SIZE_PER_DEVICE = 4K * 8 = 8000h
SIZE_PER_FUNC = 4K = 1000h
访问总线号为busNo,设备号为DevNo,功能号为funcNo的offset寄存器的计算公式是:
Memory Address = PCIe Configuration Register Base Address Register (BAR)
+ busNo * SIZE_PER_BUS
+ devNo * SIZE_PER_DEVICE
+ funcNo * SIZE_PER_FUNC
+ offset
————————————————
For example, to access the following configuration register:
• PCI Express Configuration Register F0000000h
• Bus Number 15h
• Device Number 00h
• Function Number 05h
• Register Offset 84h
Memory Address = F0000000h + 15h * 100000h + 00h * 8000h + 05h * 1000h + 84h
= F1505084h
现在我们可以从已知的busNo,devNo,funcNo和offset来计算映射后的内存地址,那么反过来,给定的内存地址,我们想知道这个地址的busNo, devNo, funcNo和offset信息,可以吗?当然可以,计算公式如下:
busNo = (Memory Address - BAR) / SIZE_PER_BUS;
devNo = (Memory Address - BAR - busNo * SIZE_PER_BUS) / SIZE_PER_DEVICE;
funcNo = (Memory Address - BAR - busNo * SIZE_PER_BUS
- devNo * SIZE_PER_DEVICE) / SIZE _PER_FUNC;
offset = Memory Address - BAR - busNo * SIZE_PER_BUS - devNo * SIZE_PER_DEVICE
- funcNo * SIZE_PER_FUNC;
又或offset = Memory Address & 0x0FFFh
原文链接:https://blog.csdn.net/mao0514/article/details/26072229
在X86系统中, 通过RW软件能够读取(ACPI)MCFG的基地址。 Memroy方式的翻译官就是 MCFG (memory mapped configuration space base address description table/ 内存映射配置空间基地址描述表。 可以通过cat /proc/iomem查看PCI MMCONFIG得到在memory空间的映射)其实就是bus 0 dev 0 function 0 的BASE地址。 这就是所谓的PCIe的 ECAM 方式 ,可以访问PCIe全部4K配置空间。
————————————————
版权声明:本文为CSDN博主「linjiasen」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: https://blog.csdn.net/linjiasen/article/details/87944672
注释: https://blog.csdn.net/huangkangying/article/details/50570612
https://blog.csdn.net/mao0514/article/details/26072229 memory地址计算方法
https://blog.csdn.net/xingqingly/article/details/45695739
https://zhuanlan.zhihu.com/p/34047690
http://developer.amd.com/wordpress/media/2012/10/pci%20-%20pci%20express%20configuration%20space%20access.pdf
————————————————
版权声明:本文为CSDN博主「linjiasen」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: https://blog.csdn.net/linjiasen/article/details/87944672
统一编址和独立编址导致IO空间和Memory空间分离,X86采用独立编址的方式,将memory操作与外设IO操作分开了,才有了memory空间和IO空间的区分。X86平台CPU内部对内存和外设寄存器访问的指令也是不同的。
BAR(base address registers)就是为了把设备的内部各种资源映射到IO空间(IO BAR)或者memory 空间(memory BAR)。
PCI桥设备
PCI桥在PCI设备树中起到呈上起下的作用。一个PCI-to-PCI桥它的配置空间如下:
注意其中的三组绿色的BUS Number和多组黄色的BASE/Limit对,它决定了桥和桥下面的PCI设备子树相应/被分配的Bus和各种资源大小和位置。这些值都是由PCI枚举程序来设置的。
Capabilities结构
PCI-X和PCIe总线规范要求其设备必须支持Capabilities结构。在PCI总线的基本配置空间中,包含一个Capabilities Pointer寄存器,该寄存器存放Capabilities结构链表的头指针。在一个PCIe设备中,可能含有多个Capability结构,这些寄存器组成一个链表,其结构如图:
PCIe的各种特性如Max Payload、Complete Timeout(CTO)等等都通过这个链表链接在一起,Capabilities ID由PCIe spec规定。链表的好处是如果你不关心这个Capabilities(或不知道怎么处理),直接跳过,处理关心的即可,兼容性比较好。另外扩展性也强,新加的功能不会固定放在某个位置,淘汰的功能删掉即好。
PCIe规范在PCI规范的基础上,将配置空间扩展到4KB。原来的CF8/CFC方法仍然可以访问所有PCIe设备配置空间的头255B,但是该方法访问不了剩下的(4K-255)配置空间。怎么办呢?Intel提供了另外一种PCIe配置空间访问方法:通过将配置空间映射到 Memory map IO(MMIO)空间,对PCIe配置空间可以像对内存一样进行读写访问了。如图:
这样再加上PCI板子上的RAM或者ROM,整个PCIe Device空间如下图:
MMIO这段空间有256MB,因为按照PCIe规范,支持最多256个buses,每个Bus支持最多32个PCI devices,每个device支持最多8个function,也就是说:占用内存的最大值为:256 * 32 * 8 * 4K = 256MB。在台式机上我们很多时候觉得占用256MB空间太浪费(造成4G以下memory可用空间变少,虽然实际memory可以映射到4G以上,但对32位OS影响很大),PCI Bus也没有那么多,所以可以设置成最低64MB,即最多64个Bus。那么这个256MB的MMIO空间在在哪里呢?我们以Intel的Haswell平台为例:
其中PCIEXBAR就是这个MMIO的起始位置,在4G下面占据64MB/128MB/256MB空间(4G以上部分不在本文范围内,我们今后会详细介绍固件中的内存布局),其具体位置可以由平台进行设置, 设置寄存器一般在Root complex(下文简称RC)中。 如果大家忘记RC,可以参考前文硬件部分的典型PCIe框图。
RC是PCIe体系结构的一个重要组成部件,也是一个较为混乱的概念。RC的提出与x86处理器系统密切相关,PCIe总线规范中涉及的RC也以x86处理器为例进行说明,而且一些在PCIe总线规范中出现的最新功能也在Intel的x86处理器系统中率先实现。事实上,只有x86处理器才存在PCIe总线规范定义的“标准RC”,而在多数处理器系统,并不含有在PCIe总线规范中涉及的,与RC相关的全部概念。
在x86处理器系统中,RC内部集成了一些PCI设备、RCRB(RC Register Block)和Event Collector等组成部件。其中RCRB由一系列的寄存器组成的大杂烩,而仅存在于x86处理器中;而Event Collector用来处理来自PCIe设备的错误消息报文和PME消息报文。RCRB的访问基地址一般在LPC设备寄存器上设置。
如果将RC中的RCRB、内置的PCI设备和Event Collector去除,该RC的主要功能与PCI总线中的Host Bridge类似,其主要作用是完成存储器域到PCI总线域的地址转换。但是随着虚拟化技术的引入,尤其是引入MR-IOV技术之后,RC的实现变得异常复杂。
BAR(Base Address Registers—— 基地址寄存器 )空间
现在我们来看看在配置空间里具体有些什么。我们以一个一般的type 0(非Bridge)设备为例:
其中Device ID和Vendor ID是区分不同设备的关键,OS和UEFI在很多时候就是通过匹配他们来找到不同的设备驱动(Class Code有时也起一定作用)。为了保证其唯一性,Vendor ID应当向PCI特别兴趣小组(PCI SIG)申请而得到。
我们重点来了解一下这些Base Address Registers(BAR)。BAR是PCI配置空间中从0x10 到 0x24的6个register,用来定义PCI需要的配置空间大小以及配置PCI设备占用的地址空间。
每个PCI设备在BAR中描述自己需要占用多少地址空间,UEFI通过所有设备的这些信息构建一张完整的关系图,描述系统中资源的分配情况,然后在合理的将地址空间配置给每个PCI设备。
BAR在bit0来表示该设备是映射到memory还是IO,bar的bit0是readonly的,也就是说,设备寄存器是映射到memory还是IO是由设备制造商决定的,其他人无法修改。
下图是BAR寄存器的结构,分别是Memory和IO:
AR通过将某些位设置为只读,且0来表示需要的地址空间大小,比如一个PCI设备需要占用1MB的地址空间,那么这个BAR就需要实现高12bit是可读写的,而20-4bit是只读且位0。地址空间大小的计算方法如下:
a.向BAR寄存器写全1
b.读回寄存器里面的值,然后clear 上图中特殊编码的值,(IO 中bit0,bit1, memory中bit0-3)。
c.对读回来的值去反,加一就得到了该设备需要占用的地址内存空间。
这样我们就可以在构建一张大表,用于记录所有PCI设备所需要的空间。这也是PCI枚举的主要任务之一。另外别忘记设置Command寄存器enable这些BARs。
UEFI对PCI/PCIe的支持
UEFI对于PCI总线的支持包括以下三个方面:
1) 提供分配PCI设备资源的协议(Protocol)。
2) 提供访问PCI设备的协议(Protocol)。
3) 提供PCI枚举器,枚举PCI总线上的设备以及分配设备所需的资源。
4) 提供各种Lib,方便驱动程序访问PCI/PCIe配置空间或者MMIO/IO空间。
PCI驱动
UEFI BIOS提供了两个主要的模块来支持PCI总线,一个是PCI Host Bridge控制器驱动,另一个是PCI总线驱动。
PCI Host Bridge控制器驱动是跟特定的平台硬件绑定的。根据系统实际I/O空间和memory map,为PCI设备指定I/O空间和Memory空间的范围,并且产生PCI Host Bridge Resource Allocation 协议(Protocol)供PCI总线驱动使用。 该驱动还对HostBridge控制器下所有RootBridge设备产生句柄(Handle),该句柄上安装了PciRootBridgeIoProtocol。PCI总线驱动则利用PciRootBridgeIo Protocol枚举系统中所有PCI设备,发现并获得PCI设备的Option Rom,并且调用PCI Host Bridge Resource Allocation 协议(Protocol)分配PCI设备资源。PCI Host Bridge Resource Allocation协议的实现是跟特定的芯和平台相结合的,毕竟只有平台所有者才知道资源从哪里来和有多少。每一个PCI HostBridge Controller下面可以接一个或者多个PCI root bridges,PCI Root Bridge会产生PCI local Bus。正如我们前文举得例子,如Intel志强第三代四路服务器,共四颗CPU,每个CPU都被划分了共享但区隔的Bus, PCI I/O, PCI Memory范围,其构成可以表示成如下图:
其他情况可见上文。PCI设备驱动不会使用PCI Root Bridge I/O协议访问PCI设备,而是会使用PCI总线驱动为PCI设备产生的PCI IO Protocol来访问PCI设备的IO/MEMORY空间和配置空间。PCI Root Bridge I/O协议(Protocol)是安装在RootBridge设备的句柄上(handle),同时在该handle上也会有表明RootBridge设备的DevicePath协议(Protocol),如下图所示:
PCI总线驱动在BDS阶段会枚举整个PCI设备树并分配资源(BUS,MMIO和IO等),它还会在不同的枚举点调用Notify event通知平台,平台的Hook可以挂接在这些点上做些特殊的动作。
原文链接: https://blog.csdn.net/u013253075/article/details/80835831
PCIE配置空间集中类型,都包含什么内容?
配置空间主要有两种,一是Type0:设备空间 二是Type1: Bridge空间。 桥设备空间如下图所示:下面介绍其中主要内容以及作用:(端点设备空间布局与Bridge空间类似,会简单一些,没有一堆Bus Num设备寄存器 ,CPU一般通过判断Header确定该设备是什么设备 )
DID&VID: 设备及厂商ID,出场固定且每个设备都不应,枚举设备时通常判断此VID来判断设备是否存在。
Class Code: 该寄存器是只读的,通常用来表示该设备类型。用法如下 : ClassClde Register共3个byte,位于配置空间的[0A:08],分别表示BaseClass, Sub-Class, InterFace;举 个简单的例子--使用RW工具查看笔者自己笔记本的Pcie Memory设备的Config空间,如下图:
可以看到该设备的ClassCode Register的值是0x058000,对照PCIE Spec查看,可以看到:
Add[0A] = 05 Add[09] = 80 Add[08] = 00
Add[0A] = 05表明该设备是一个Memory的Controller,具体内存子类是其他类型,不属于RAM&Flash,如下图: 而Add[08]表示该设备不同的接口,含有多种接口的才会表示出来,如下显示设备的会含有不同的Interface:
HeaderType: 标明Config空间的类型,同时也标明了Config空间的layout,共有三种:00H--普通端点设备,01H--Bridge设备,02H--CardBus bridges(此外,若该寄存器的Bit7为0.则表明该设备是一个单功能设备 )
BAR0 & BAR1: 设备空间的基地址
Subordinate Bus Number: 从属Bus号,即该Bus下最大的Bus号
Secondary Bus Number: 该Bridge下接的Bus号
Primary Bus Number: 该Bridge上接的Bus号
Memory Limit: 此设备所分配的内存大小
Memroy Base: 此设备在内从中分配的基地址
原文链接 https://blog.csdn.net/szhb5251/article/details/105961892