x86(32位)处理器 有三种地址空间:
1、Memory Address Space,CPU直接访问的内存空间0~4G(32位);
2、I/O Address Space ,io端口,0~0xFFFF(64K),通过in、out 指令直接访问,
3、PCI Configuration Address Space(PCI配置空间) 0~0xFFFFFF (16M)。
PCI配置空间访问有4种方式:pci_bios、pci_conf1、pci_conf2、pci_mmcfg
PCIconf1 PCI conf2 其实也是用in、out指令,该方式只能访问256B。
在reactos0.17中有完整的代码
当0xCF8-CFB作为单独的Byte使用时,往0xCFB的bit0写1意味着使用PCIconf1,写0使用PCI conf2,conf2已经废弃不用 WritePortByte(0xCFB, 0x01);
然后往0xCF8-CFB写入地址0x80000000,如果读回来的还是0x80000000说明系统中有PCI总线,
pci_mmcfg应该是MMIO方式,
1)通过主桥(Host Bridge)中的 IO 映射地址端口(Address Port )和资料端口(Data Port)进行索引来间接访问 PCI 配置空间。地址端口(Address Port )位于 IO 地址 CF8h-CFBh,是一个大小为32 bits的暂存器,
2)Direct Memory Access (DMA)
PCIe则引入了一种新的Configuration Address Space访问方式:将其直接映射到了Memory Address Space当中。
简化代码,做一些基本功能测试
typedef struct _PCI_TYPE_HEADER {
USHORT vendorID, deviceID;
USHORT command, status; //
UCHAR revID,ProgrammingInterface,SubClassCode,BaseClassCode; // // 其中 BaseClassCode 将 PCI 设备分类为显卡、网卡、PCI 桥等设备;
//SubClassCode 对这些设备进一步细分;
//而 Interface 定义编程接口。
UCHAR CacheLineSize, LatencyTimer, HeaderType, bist;
ULONG BaseAdder0, BaseAdder1;
union {
struct {
ULONG BaseAdder2;
ULONG BaseAdder3;
ULONG BaseAdder4;
ULONG BaseAdder5;
ULONG CardBusClsBponiter;
USHORT SubsysVendorID,SubSysID;
ULONG ExpRomBaseAddress0;
ULONG CapabilityPointer0;//(CapPointer0+Reserved)
ULONG Reserved;
UCHAR interLine0, interPin0, MinGnt, MaxLat;
}TYPE_0_HEADER;
struct {
UCHAR PrimaryBus, secondaryBus, subBus, SecLatTimer;
UCHAR ioBase, ioLimit;
USHORT SecondaryStatus;
USHORT memBase,memLimit;
USHORT prefMemBase,PreMemLimit;
ULONG PrefBase32,PrefLimit32;
USHORT iobase16,ioLimit16;
ULONG CapPointer1;//(CapPointer0+Reserved)
ULONG ExpRomBaseAddress1;
UCHAR interLine1, interPin1;
USHORT bridgeControl;
}TYPE_1_HEADER;
char CardBusCls[192];
};
} PCI_TYPE_HEADER, *PPCI_TYPE_HEADER;
#define PCI_CONF1_ADDRESS(bus, dev, fn) \
(0x80000000 | (bus << 16) | (dev << 11) | (fn << 8))
#define PCI_CONFIG 0xCF8
#define PCI_DATA 0xCFC
DWORD read_pci_ulong(DWORD baseaddr, DWORD reg) {
WritePortDword(PCI_CONFIG, baseaddr | ((reg & 0x3F) << 2));
return ReadPortDword(PCI_DATA);
}
void pci_scan() {
//domain(总线域)、bus(总线号)、dev(设备) func函数号、reg(寄存器号)
int bus, dev, func, reg;
DWORD dwAddr;
PCI_TYPE_HEADER pth;
// pci 配置空间的具体到代码中的访问有4种方式:pci_bios、pci_conf1、pci_conf2、pci_mmcfg。最优的方式是 mmcfg
// I/O 访问的方式只可以访问配置空间的前256字节,而使用 mmcfg 的方式则可以完全支持 PCIE 的扩展寄存器即4K字节的配置
//前64位是header 后192位是CapabilityPointer,这是个偏移值
// WritePortByte((PUCHAR) 0xCFB, 0x01);
for (bus = 0; bus < 256; ++bus) {
for (dev = 0; dev < 32; ++dev) {
for (func = 0; func < 8; ++func) {
dwAddr = PCI_CONF1_ADDRESS(bus, dev, func);
*((PULONG) &pth) = read_pci_ulong(dwAddr, 0);
if ((pth.deviceID != 0xFFFF) && (pth.vendorID != 0)) {
//ID前面已经读取,前16为header
for (reg = 1; reg < 16; reg++) {
*(PULONG) (((ULONG) &pth) + reg * 4) = read_pci_ulong(
dwAddr, reg);
}
DbgPrint("b=%x, d=%x,f=%x;", bus, dev, func);
DbgPrint("did=%x,vid=%x,bcc=%x,scc=%x;", pth.deviceID,
pth.vendorID, pth.BaseClassCode, pth.SubClassCode);
DbgPrint("ht=%x ba=%x\n", pth.HeaderType, pth.BaseAdder0);
}
//printf("-\t-\t-\t-\n");
}
}
}
return;
}