PCI总线:一种高性能32位或者64位的多路复用地址或者数据行的总线。
作用:高度集成外围控制器、外围插件和处理器/内存系统之间的互连机制。
BDF码:总线号,设备号,功能号
作用:描述PCI设备物理连接情况。一个系统可以有256个PCI总线,每个总线上可以有32个设备,每个设备可以具有8个功能(每个功能作为一个PCI设备)。当这三个数据确定的时候,就可以在系统中唯一确定一个PCI 设备。
8~10:功能位:有时候,一个pci设备对应多个功能.将每个功能单元分离出来,对应一个独立的pci device
11~15位:设备号:对应该pci总线上的设备序号
16~23位:总线号:根总线的总线号为0.每遍历到下层总线,总线号+1。(下层总线的资源是它上层总线资源的子集,上层总线资源是下层总线资源的父集。)
bus | dev | func | |
---|---|---|---|
占 | 16-23位 | 11-15位 | 8-10位 |
共 | 8位 | 5位 | 3位 |
总 | 256条 | 32个 | 7个 |
左移 | 8位 | 3位 | 0 |
最大 | 0xFF | 0x1F | 0x7 |
mask | 0xFF00 | 0xF8 | 0x0007 |
PCI设备有三个空间——内存地址空间、IO地址空间和配置空间。
pci配置空间
PCI配置空间是一块容量为256字节并具有特定记录结构或模型的地址空间,通过配置空间,我们可以了解该PCI设备的一些配置情况,进而控制该设备,除主总线桥以外的所有PCI设备都必须事先配置空间.
配置空间的前64个字节叫头标区,头标区又分成两个部分,第一部分为前16个字节,在各种类型的设备中定义都是一样的,其他字节随各设备支持的功能不同而有所不同,位于偏移0EH的投标类型字段规定了是何种布局,目前有三种头标类型,头标类型1用于PCI-PCI桥,头标类型2用于PCI-CARDBUS桥,头标类型0用于其他PCI设备,下为头标类型0的头标区布局。
头标区中有5个字段涉及设备的识别。
供应商识别字段(Vendor ID)
偏移:00H。该字段用以标明设备的制造者。一个有效的供应商标识由PCI SIG来分配,以保证它的唯一性。0FFFFH是该字段的无效值。
设备识别字段(Device ID)
偏移:02H。用以标明特定的设备,具体代码由供应商来分配。
版本识别字段(Revision ID)
偏移:08H。用来指定一个设备特有的版本识别代码,其值由供应商提供,可以是0。
头标类型字段(Header Type)
偏移:0EH。该字段有两个作用,一是用来表示配置空间头标区第二部分的布局类型;二是用以指定设备是否包含多功能。位7用来标识一个多功能设备,位7为0表明是单功能设备,位7为1表明是多功能设备。位0-位6表明头标区类型。
分类代码字段(Class Code)
偏移:09H。标识设备的总体功能和特定的寄存器级编程接口。该字节分三部分,每部分占一个字节,第一部分是基本分类代码,位于偏移0BH,第二部分叫子分类代码,位于偏移0AH处,第三部分用于标识一个特定的寄存器级编程接口。
io口访问pci设备
可通过访问CF8h、CFCh端口来实现:
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位端口。
在dos下申请相关的接口就可以得到io口,通过cf8和cfc的模式进行读取遍历pci设备。
#include <stdio.h>
typedef unsigned long DWORD;
typedef unsigned int WORD;
#define MK_PDI(bus,dev,func) (WORD)((bus<<8)|(dev<<3)|(func))
#define MK_PCIaddr(bus,dev,func) (DWORD)(0xf8000000L|(DWORD)MK_PDI(bus,dev,func)<<8)
#define PCI_CONFIG_ADDRESS 0xCF8
#define PCI_CONFIG_DATA 0xCFC
DWORD inpd(int inport)
{
DWORD data;
asm mov dx,inport;
asm lea bx,data;
__emit__(
0x66,0x50,
0x66,0xED,
0x66,0x89,0x07,
0x66,0x58);
return data;
}
void outpd(int outport,DWORD addr)
{
asm mov dx,outport;
asm lea bx,addr;
__emit__(
0x66,0x50,
0x66,0x8B,0x07,
0x66,0xEF,
0x66,0x58);
}
DWORD GetData(DWORD addr)
{
DWORD data;
outpd(PCI_CONFIG_ADDRESS,addr);
data = inpd(PCI_CONFIG_DATA);
return data;
}
int main()
{
int bus,dev,func;
DWORD addr,addr1,addr2,addr3;
DWORD data,data1,data2,data3;
printf("Bus#\tDev#\tFunc#");
printf("\n");
for (bus = 0; bus <= 0x63; ++bus)
{
for (dev = 0; dev <= 0x1F; ++dev)
{
for (func = 0; func <= 0x7; ++func)
{
addr = MK_PCIaddr(bus,dev,func);
data = GetData(addr);
if((WORD)data!=0xFFFF) //FFFFh是一个非法厂商ID,可它来判断PCI设备是否存在。
{
printf("%2.2x\t%2.2x\t%2.2x\t",bus,dev,func);
printf("\n");
}
}
}
}
return 0;
}
【14】PCIe架构下memory空间、IO空间、PCIe配置空间简介