PCI/PCIe基础——配置空间

简介

PCI/PCIe设备有本身的独立地址空间,这部分空间会映射到整个系统的地址空间。linux

映射地址在BIOS/UEFI下指定(若是有的话,对于使用非BIOS启动的OS,不清楚),它有两种类型,一种是MMIO,一种是IO。对于MMIO的访问,跟访问内存的方式同样,它从称为PCIEXBAR的基地址开始,有很大的一段空间,这个PCIEXBAR的值根据不一样的平台可能不一样,大体可能值有0xC0000000、0xE0000000等,关于这个值是怎么使用的后面的章节会讲到;对于IO,它是一种比较老的访问PCI/PCIe设备的方式,并且占有的空间相比MMIO很是小,好像只有64K的空间。函数

PCI/PCIe设备使用的空间也有两个部分,一部分称为配置空间(经过MMIO);另外一部分经过配置空间的BAR寄存器指定,是设备实现功能所须要用到的地址空间(有MMIO也有IO, 不过IO用的比较少了)。布局

 

PCI/PCIe配置空间的访问方式

PCI/PCIe设备的配置空间经过PCIEXBAR加上设备的Bus、Device、Fun号的转换来获得,BDF到地址的转换关系以下:spa

  
 
 
 
 
  
  
           
  
  
  1. /**
  2. Macro that converts PCI Bus, PCI Device, PCI Function and PCI Register to an
  3. address that can be passed to the PCI Library functions.
  4. @param Bus PCI Bus number. Range 0..255.
  5. @param Device PCI Device number. Range 0..31.
  6. @param Function PCI Function number. Range 0..7.
  7. @param Register PCI Register number. Range 0..255 for PCI. Range 0..4095
  8. for PCI Express.
  9. @return The encoded PCI address.
  10. **/
  11. #define PCI_LIB_ADDRESS(Bus,Device,Function,Register) \
  12. (((Register) & 0xfff) | (((Function) & 0x07) << 12) | (((Device) & 0x1f) << 15) | (((Bus) & 0xff) << 20))

其中的Register是具体要访问的寄存器。3d

这是最经常使用的一种方式,经过将B/D/F转换成MMIO的地址,以后就能够经过MMIO的方式来访问,下面是一个例子:code

  
 
 
 
 
  
  
           
  
  
  1. UINT8
  2. EFIAPI
  3. PciExpressRead8 (
  4. IN UINTN Address
  5. )
  6. {
  7. ASSERT_INVALID_PCI_ADDRESS (Address);
  8. return MmioRead8 ((UINTN) GetPciExpressBaseAddress () + Address);
  9. }

不过这里有个问题,经过PCI_LIB_ADDRESS获得的并非实际的系统地址空间,它算是一个偏移,还须要加上一个基地址(就是这个经过函数GetPciExpressBaseAddress()获得的)。在UEFI中这个基地址被设置成一个PCD变量:PcdPciExpressBaseAddress。它的值根据不一样平台可能会不一样。blog

不太重点不是它的值,重点是如何设置这个值,由于只有设置了这个值才能使用上面说的方式来读写PCI/PCIe配置空间。内存

而PCIEXBAR也是要写到PCI设备的配置空间中的,它会被写到B0/D0/F0/R060h这个寄存器(不一样平台可能不一样)。ci

这里就遇到了一个问题,该经过什么方式来写这个值呢?资源

实际上,须要注意几点:

1. 上述提供的访问PCI/PCIe配置空间的方式是PCIe的方式;

2. 早期在没有PCIe的时候,要访问配置空间时,使用的是两个IO端口,CFCh和CF8h,经过往一个端口指定寄存器,另外一个端口写值的方式为指定寄存器赋值。

因此咱们要注意,有两个配置空间的方式:

1. 传统方式,写IO端口CFCh和CF8h。只能访问PCI/PCIe设备的开始256个字节(由于PCI设备的配置空间原本就只有256个字节);

2. PCIe的方式,就是上面提到的方式,它能够方位4K个字节的配置空间。

 

PCI配置空间

因为PCI/PCIe设备分为Bridge和Agent两种,因此配置空间也有两种类型:

其中Agent的配置空间类型称为Type 00h:

简单介绍其中的几个寄存器的意义:

Vendor ID,Device ID:标记了一个设备的生产厂商和具体的设备,好比Intel的设备Vendor ID一般是0x8086,Device ID就须要厂家自定义了,总之可以识别到具体是哪一个设备就能够了。

Status:设备状态字,具体每一个BIT的意义见下图:

Command:设备状态字:

Base Address Registers:决定PCI/PCIe设备空间映射到系统空间具体位置的寄存器,映射方式有两种,分别是IO和Memory映射:

处理器系统资源分为IO资源和MMIO资源两种,所以PCI/PCIe空间地址对应也有两种。

下面是Bridge的配置空间,它的类型被称为Type 01h:

Type 01h中也有Vendor ID,Device ID,Status,Command等寄存器。

另外须要注意的是这里的BAR计算获得的系统空间是该桥下挂的全部设备的系统空间的总和。

另外Subordinate Bus Number、Secondary Bus Number和Primary Bus Number,这些寄存器共同肯定了该桥上行和下行的全部Bus号。

 

PCIe配置空间

PCIe是在PCI基础上发展的协议,PCIe也有上述的PCI配置空间,而且在此基础之上进行了扩展,其扩展形式是经过一种称为Capability的寄存器块来完成的。

PCI配置空间的大小是256个字节,即0x00~0xFF,而PCIe的配置空间扩大到了0x00~0xFFF,下图是具体的布局。

在原来的配置空间中,有一个寄存器指定了第一个Capability的位置,而第一个Capability又指定下一个Capability,构成了一串Capability,具体以下图所示:

各个不一样的Capability的做用不一样,且不一样的设备有不一样的Capability,在这里不一一介绍了。

 

其它说明

处理器系统中会为全部的PCI/PCIe设备留足足够的空间,可是几乎没有系统会满配,因此不少的配置空间其实是空的,此时若是去访问,就会获得全FF,表示设备不存在。

下面是linux下访问PCI/PCIe配置空间的一个例子:

注:以上是一个Intel网卡的PCI/PCIe配置空间,这是虚拟机下的结果,真实机器上可能有所不一样。

相关文章
相关标签/搜索