一、配置空间介绍:
PCI设备有三个空间——内存地址空间、IO地址空间和配置空间。由于PCI支持即插即用,所以PCI设备不是占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。怎么配置呢?这就是配置空间的作用。
DW | Byte3 | Byte2 | Byte1 | Byte0 | Addr
---+---------------------------------------------------------+-----
0 | Device ID | Vendor ID | 00
---+---------------------------------------------------------+-----
1 | Status | Command | 04
---+---------------------------------------------------------+-----
2 | Class Code | Revision ID | 08
---+---------------------------------------------------------+-----
3 | BIST | Header Type | Latency Timer | Cache Line | 0C
---+---------------------------------------------------------+-----
4 | Base Address 0 | 10
---+---------------------------------------------------------+-----
5 | Base Address 1 | 14
---+---------------------------------------------------------+-----
6 | Base Address 2 | 18
---+---------------------------------------------------------+-----
7 | Base Address 3 | 1C
---+---------------------------------------------------------+-----
8 | Base Address 4 | 20
---+---------------------------------------------------------+-----
9 | Base Address 5 | 24
---+---------------------------------------------------------+-----
10 | CardBus CIS pointer | 28
---+---------------------------------------------------------+-----
11 | Subsystem Device ID | Subsystem Vendor ID | 2C
---+---------------------------------------------------------+-----
12 | Expansion ROM Base Address | 30
---+---------------------------------------------------------+-----
13 | Reserved(Capability List) | 34
---+---------------------------------------------------------+-----
14 | Reserved | 38
---+---------------------------------------------------------+-----
15 | Max_Lat | Min_Gnt | IRQ Pin | IRQ Line | 3C
-------------------------------------------------------------------
配置空间中最重要的有:
Vendor ID:厂商ID。知名的设备厂商的ID。FFFFh是一个非法厂商ID,可它来判断PCI设备是否存在。
Device ID:设备ID。某厂商生产的设备的ID。操作系统就是凭着 Vendor ID和Device ID 找到对应驱动程序的。
Class Code:类代码。共三字节,分别是类代码、子类代码、编程接口。类代码不仅用于区分设备类型,还是编程接口的规范,这就是为什么会有通用驱动程序。
IRQ Line:IRQ编号。PC机以前是靠两片8259芯片来管理16个硬件中断。现在为了支持对称多处理器,有了APIC(高级可编程中断控制器),它支持管理24个中断。
IRQ Pin:中断引脚。PCI有4个中断引脚,该寄存器表明该设备连接的是哪个引脚。
二、访问配置空间
可通过访问CF8h、CFCh端口来实现(《PCI Local Bus Specification》的3.2.2.3.2)。
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位端口。
现在有个难题——CF8h、CFCh端口是32位端口,可像TurboC之类的16位C语言编译器都不支持32位端口访问。怎么办?我们可以使用__emit__在程序中插入机器码。每次都__emit__一下肯定很麻烦,所以我们应该将它封装成函数。代码如下(注意66h是32位指令前缀):
/* 读32位端口 */ DWORD inpd(int portid) { DWORD dwRet; asm mov dx, portid; asm lea bx, dwRet; __emit__( 0x66,0x50, // push EAX 0x66,0xED, // in EAX,DX 0x66,0x89,0x07, // mov [BX],EAX 0x66,0x58); // pop EAX return dwRet; } /* 写32位端口 */ void outpd(int portid, DWORD dwVal) { asm mov dx, portid; asm lea bx, dwVal; __emit__( 0x66,0x50, // push EAX 0x66,0x8B,0x07, // mov EAX,[BX] 0x66,0xEF, // out DX,EAX 0x66,0x58); // pop EAX return; } |
三、枚举PCI设备
怎么枚举PCI设备呢?我们可以尝试所有的 bus/dev/func 组合,然后判断得到的厂商ID是否为FFFFh。
下面这个程序就是使用该方法枚举PCI设备的。同时为了便于分析数据,将每个设备的配置空间信息保存到文件,这样可以慢慢分析。
/* */ #include
#define PDI_DEVICE_SHIFT 3 #define PDI_FUNCTION_SHIFT 0 #define MK_PDI(bus,dev,func)(WORD)((bus&PDI_BUS_MAX)<<<PDI_DEVICE_SHIFT | (func&PDI_FUNCTION_MAX) )< pre>
/* 写32位端口 */
printf("/n"); /* 枚举PCI设备 */ /* 判断设备是否存在。FFFFh是非法厂商ID */ /* Vendor/Device */ /* Class Code */ /* IRQ/intPin */ printf("/n"); /* 写文件 */ /* Write */ return0; |
对于我的电脑的枚举结果是:
Bus# | Device# | Func# | Vendor | Device | Class | IRQ | IntPin | 类代码的说明 |
0 | 0 | 0 | 1106 | 3189 | 60000 | 0 | 0 | Host bridge |
0 | 1 | 0 | 1106 | B168 | 60400 | 0 | 0 | PCI-to-PCI bridge(实际上是PCI/AGP桥,AGP可看成一种特殊的PCI设备) |
0 | 9 | 0 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 1 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 2 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 3 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 4 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 5 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 6 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 9 | 7 | 14F1 | 2013 | 78000 | 11 | 1 | Simple communication controllers |
0 | 10 | 0 | 1106 | 3038 | 0C0300 | 11 | 1 | USB controller: Universal Host Controller Specification |
0 | 10 | 1 | 1106 | 3038 | 0C0300 | 5 | 2 | USB controller: Universal Host Controller Specification |
0 | 10 | 2 | 1106 | 3038 | 0C0300 | 5 | 3 | USB controller: Universal Host Controller Specification |
0 | 10 | 3 | 1106 | 3104 | 0C0320 | 11 | 4 | USB2 controller: Intel Enhanced Host Controller Interface |
0 | 11 | 0 | 1106 | 3177 | 60100 | 0 | 0 | ISA bridge |
0 | 11 | 1 | 1106 | 571 | 01018A | 255 | 1 | IDE controller |
0 | 11 | 5 | 1106 | 3059 | 40100 | 5 | 3 | Audio device |
0 | 12 | 0 | 1106 | 3065 | 20000 | 11 | 1 | Ethernet controller |
1 | 0 | 0 | 10DE | 110 | 30000 | 11 | 1 | VGA-compatible controller |
总线编号为0的都是主板上固有的芯片(主要是南桥),非主板设备的典型是——显卡。
WindowsXP的设备管理器中也可以看到 PCI信息。启动“设备管理器”,最好将查看方式设为“依连接查看设备(V)”。找到我的显卡,双击查看属性。切换到“详细信息”页,定位组合框为“硬件 Id”。可看到其中一行为“ PCI/VEN_10DE&DEV_0110&CC_030000”,表示厂商ID为“10DE”、设备ID为“0110”、类代码为“030000”,与程序得到的结果一致。