最近楼主比较苦逼啊,主管布置了一道访问pci的做业,这个做业使用io方式还能够很是浪地将全部的东西都给读取出来,虽然不能读取出pci-e设备的全部信息,可是仍是能够将256位的其余东西给读出来的。linux
本文将先从io访问模式进行对pci访问的设置,在这里我所使用的包含了dos和linux,这样能够看到这2个的系统代码的不一样。ios
PCI总线是一种高性能32位或者64位的多路复用地址或者数据行的总线。至关于现实生活中的公路,是所谓的信号通道。能够在这上面传输数据、控制信号等等。编程
做用:高度集成外围控制其、外围插件和处理器/内存系统之间的互连机制。c#
若是要访问PCI 设备,首先要肯定PCI设备在系统中的物理链接状况。描述这个链接状况的数据是“总线号”、“设备号”和“功能号”。一个系统能够有256个PCI总线,每一个总线上能够有32个设备,每一个设备能够具备8个功能(每一个功能做为一个PCI设备)。当这三个数据肯定的时候,就能够在系统中惟一肯定一个PCI 设备。布局
8~10:功能位. 有时候,一个pci设备对应多个功能.将每一个功能单元分离出来,对应一个独立的pci device
11~15位:设备号 对应该pci总线上的设备序号
16~23位:总线号 根总线的总线号为0.每遍历到下层总线,总线号+1性能
在上图的总线结构中,ethernet设备和pci-pci bridge的同类型资源空间必需要是pci bus0的一个子集操作系统
例如,pci bus 0的I/O端口资源是0x00CC~0x01CC. Ethernet设备的I/O范围的是0x00CC~0x0xE0.那么pci-pci bridge的I/O端口范围就必需要在0x0xE0~0x01CC之间.插件
一样,SCSI和VIDEO同类型资源必需要是pci_bus1的子集.pci bus1上有一个pci桥,对应的资源也就是它所连桥上的资源.即pci_bus->self.也就是说,下层总线的资源是它上层总线资源的子集。上层总线资源是下层总线资源的父集。code
其实,每一个PCI设备的资源地始地址都是由操做系统设置的.在x86上,都由bios设置好了.blog
PCI配置空间是一块容量为256字节并具备特定记录结构或模型的地址空间,经过配置空间,咱们能够了解该PCI设备的一些配置状况,进而控制该设备,除主总线桥之外的全部PCI设备都必须事先配置空间.
配置空间的前64个字节叫头标区,头标区又分红两个部分,第一部分为前16个字节,在各类类型的设备中定义都是同样的,其余字节随各设备支持的功能不一样而有所不一样,位于偏移0EH的投标类型字段规定了是何种布局,目前有三种头标类型,头标类型1用于PCI-PCI桥,头标类型2用于PCI-CARDBUS桥,头标类型0用于其余PCI设备,下图为头标类型0的头标区布局。
头标区中有5个字段涉及设备的识别。
偏移:00H。该字段用以标明设备的制造者。一个有效的供应商标识由PCI SIG来分配,以保证它的惟一性。0FFFFH是该字段的无效值。
偏移:02H。用以标明特定的设备,具体代码由供应商来分配。
偏移:08H。用来指定一个设备特有的版本识别代码,其值由供应商提供,能够是0。
偏移:0EH。该字段有两个做用,一是用来表示配置空间头标区第二部分的布局类型;二是用以指定设备是否包含多功能。位7用来标识一个多功能设备,位7为0代表是单功能设备,位7为1代表是多功能设备。位0-位6代表头标区类型。
偏移:09H。标识设备的整体功能和特定的寄存器级编程接口。该字节分三部分,每部分占一个字节,第一部分是基本分类代码,位于偏移0BH,第二部分叫子分类代码,位于偏移0AH处,第三部分用于标识一个特定的寄存器级编程接口。
在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) { printf("%2.2x\t%2.2x\t%2.2x\t",bus,dev,func); printf("\n"); } } } } return 0; }
在linux系统下就很简单了,直接看代码吧!
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/io.h> #define PCI_MAX_BUS 255 #define PCI_MAX_DEV 31 #define PCI_MAX_FUN 7 #define PCI_BASE_ADDR 0x80000000L #define CONFIG_ADDR 0xcf8 #define CONFIG_DATA 0xcfc typedef unsigned long DWORD; typedef unsigned int WORD; typedef unsigned long DWORD; int main() { WORD bus,dev,fun; DWORD addr,data; int ret; printf("bus#\tdev#\tfun#\t"); printf("\n"); ret = iopl(3); if(ret < 0) { perror("iopl set error"); return -1; } for(bus = 0; bus <= PCI_MAX_BUS; bus++) for(dev = 0; dev <= PCI_MAX_DEV; dev++) for(fun = 0; fun <= PCI_MAX_FUN; fun++) { addr = PCI_BASE_ADDR|(bus << 16)|(dev << 11)|(fun << 8); outl(addr,CONFIG_ADDR); data = inl(CONFIG_DATA); if((data != 0xffffffff)&&(data != 0)) { printf("%2x\t%2x\t%2x",bus,dev,fun); printf("\n"); } } ret = iopl(0); if(ret < 0){ perror("iopl set error"); return -1; } return 0; }
今天暂时到这里,下一章将介绍什么是mmio,如何实现的!欢迎关注!