linux环境下遍历PCI设备
2015-08-14 17:13
537 查看
终于有时间写博客啦,让我把想写的都来说清楚!在网上找相关资料发现比较少,所以完成后迫不及待分享给大家,希望能带给大家帮助,欢迎批评指正!
PCI是Peripheral
PCI作為处理器系統的局部总线,主要目的为了连接外部设备,而不是作为处理器的系統总线连接cache和Main
Memory
host主桥作为连接处理器和PCI的介质,可以直接推出一条PCI总线,该总线又可以通过一个PCI桥连接其他PCI设备或PCI桥,这点和树有些类似
每一个Host主桥管理一个PCI总线域
下面进入正题
![](http://img.blog.csdn.net/20150815150033568?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
vendor
ID代表生产厂商看,0x8086代表Intel
Device
ID是这个厂商生产的具体设备
我们遍历PCI设备就是找到这个配置空间并打印出他的venderID和Device
ID即可
x86处理器定义了两个IO端口寄存器,用来访问PCI设备的配置空间,即CONFIG_ADDRESS和CONFIG_DATA
改变当前进程I/O端口的权能级别。对于允许8514兼容的X服务器在Linux上运行,这一系统调用必不可少。X服务器要求访问所有 65536个I/O端口,ioperm调用不能满足这种需求。另外,为了获取不受限制的I/O端口访问权,以较高级别的I/O权能级运行将允许进程禁止中断。这可能导致系统的崩毁,不推荐那样做。一般用户的I/O访问级是0。本系统调用只应用于i386平台。
用法:
#include <sys/io.h>
int iopl(int level);
ps:必须root权限执行
参数:
level:新的I/O访问级,范围是[0~3]。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EINVAL:参数无效,level大于3
ENOSYS:平台不支持这个系统调用
EPERM:调用进程没有权限使用iopl,要求CAP_SYS_RAWIO权能
void outl (unsigned int data, unsigned short int port);
static __iniine unsigned int inl(unsigned short int port);
inl(),outl(),是读写端口。
readl(),writel(),是读写内存。
下面来看代码吧(ps:仅适用于linux环境,运行通过,结果正确)
了解PCI总线
PCI是Peripheral
Component Interconnect(外设部件互连标准)的缩写,它是目前个人电脑中使用最为广泛的接口,几乎所有的主板产品上都带有这种插槽。PCI插槽也是主板带有最多数量的插槽类型,在目前流行的台式机主板上,ATX结构的主板一般带有5~6个PCI插槽,而小一点的MATX主板也都带有2~3个PCI插槽,可见其应用的广泛性。
PCI作為处理器系統的局部总线,主要目的为了连接外部设备,而不是作为处理器的系統总线连接cache和MainMemory
host主桥作为连接处理器和PCI的介质,可以直接推出一条PCI总线,该总线又可以通过一个PCI桥连接其他PCI设备或PCI桥,这点和树有些类似
每一个Host主桥管理一个PCI总线域
下面进入正题
PCI设备的配置空间
vendor
ID代表生产厂商看,0x8086代表Intel
Device
ID是这个厂商生产的具体设备
我们遍历PCI设备就是找到这个配置空间并打印出他的venderID和Device
ID即可
访问PCI设备的方式
x86处理器定义了两个IO端口寄存器,用来访问PCI设备的配置空间,即CONFIG_ADDRESS和CONFIG_DATA
CONFIG_ADDRESS地址是0xcf8,也就是说通过访问0xcf8就可以访问CONFIG_ADDRESS寄存器啦,CONFIG_ADDRESS寄存器用来存放PCI设备的ID号
CONFIG_DATA地址是0xcfc,CONFIG_DATA用来存放进行配置读写的数据
那这两个寄存器怎么用呢,简单来说就是把要找的PCI设备的相关信息按CONFIG_ADDRESS寄存器的格式写入0xcf8,然后再去CONFIG_DATA即0xcfc读出来就好了(当然是用宏定义来实现寄存器的地址啦)
下面来看CONFIG_ADDR寄存器的结构
![](http://img.blog.csdn.net/20150815150109351?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
enable位,即第31bit,置为1
bus
number 记录PCI设备总线号
device
number记录PCI设备设备号
function
number记录PCI设备功能号
register
number记录PCI设备寄存器号
访问过程:
当X86处理器对CONFIG_DATA寄存器进行I/O读写访问时,并且CONFIG_ADDRESS寄存器的Enable位为1时,Host主桥就把这个读写访问转为PCI配置读写总线事务发往PCI总线,PCI总线根据CONFIG_ADDRESS的ID号,将请求发送到相应的PCI设备寄存器
在这里好不容易搞懂CONFIG_DATA里到底放了些啥,就是说PCI总线根据CONFIG_ADDRESS的bus,device,functin number来寻找PCI设备,找到后再根据CONFIG_ADDRESS的register
number寄存器号也就是PCI设备配置空间的偏移量,取出相应的内容放到CONFIG_DATA寄存器,
如register number为0x00即代表PCI设备的vender ID和Device ID,但是在这里我们不需要读取他们的具体内容,只需要知道有就可以了
参考文献:王齐《PCIE体系结构导读》
在上代码之前还需要解释几个函数
1.iopl()系统调用(linux环境)
功能描述:改变当前进程I/O端口的权能级别。对于允许8514兼容的X服务器在Linux上运行,这一系统调用必不可少。X服务器要求访问所有 65536个I/O端口,ioperm调用不能满足这种需求。另外,为了获取不受限制的I/O端口访问权,以较高级别的I/O权能级运行将允许进程禁止中断。这可能导致系统的崩毁,不推荐那样做。一般用户的I/O访问级是0。本系统调用只应用于i386平台。
用法:
#include <sys/io.h>
int iopl(int level);
ps:必须root权限执行
参数:
level:新的I/O访问级,范围是[0~3]。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EINVAL:参数无效,level大于3
ENOSYS:平台不支持这个系统调用
EPERM:调用进程没有权限使用iopl,要求CAP_SYS_RAWIO权能
2.outl()和inl()
函数原型:void outl (unsigned int data, unsigned short int port);
static __iniine unsigned int inl(unsigned short int port);
inl(),outl(),是读写端口。
readl(),writel(),是读写内存。
下面来看代码吧(ps:仅适用于linux环境,运行通过,结果正确)
<span style="font-weight: normal;"><span style="font-family:Microsoft YaHei;font-size:14px;">#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/io.h> #define MAX_BUS 256 //bus bunber:0~255 #define MAX_DEV 32 //device number:0~31 #define MAX_FUN 8 //function number:0~7 #define BASE_ADDR 0x80000000 //基地址,enable=1 #define CONFIG_ADDR 0xcf8 #define CONFIG_DATA 0xcfc typedef unsigned int WORD; //4byte int main() { WORD bus,dev,fun; WORD addr,data; int ret=0; printf("bus#\tdev#\tfun#\t"); printf("\n"); ret=iopl(3); //set high power if(ret<0) { perror("iopl set high power error"); return -1; } for(bus=0;bus<MAX_BUS;++bus) { for(dev=0;dev<MAX_DEV;++dev) { for(fun=0;fun<MAX_FUN;++fun) { addr=BASE_ADDR|(bus<<16)|(dev<<11)|(fun<<8); //把number们分别左移到相应的位上去,register number默认设为0 outl(addr,CONFIG_ADDR); //put addr into CONFIG_ADDR data=inl(CONFIG_DATA); //read address from CONFIG_DATA if((data!=0xffffffff)&&(data!=0)) //如果vender ID和Device ID位全是1,代表没有该设备 printf("%2x\t%2x\t%2x\n",bus,dev,fun); //找到PCI设备后打印他的bus,dev,fun number } } } ret=iopl(0); //set low power if(ret<0) { perror("iopl set low power error"); return -1; } return 0; }</span></span>下一篇写PCIE设备的mmio访问
相关文章推荐
- hadoop(1):centos 安装 hadoop & hive
- linux学习笔记-----其他文件权限管理命令
- linux的Input子系统获取Device Name
- linux上运行jar文件以及打包jar文件的时候遇到的错误
- Linux脚本重定向
- Linux服务器同步时间
- linux C中va_list用法
- 查进程和杀进程命令 linux
- Java程序以后台方式在Linux上运行并且重定向LOG文件
- Linux 数据库学习---访问mysql的一般性原则
- linux文件查找
- 从零开始--(1)Linux虚拟机下使用yum安装gcc
- VS2015编译boost1.58 Linux平台编译
- 在linux下编译线程程序undefined reference to `pthread_create'
- 如何在Linux下写无线网卡的驱动【转】
- Linux专业名词
- centos下安装KVM虚拟机
- Linux中yum和apt-get用法及区别
- Centos6 Postfix+Dovecot+Postfixadmin+Roundcube 搭建Webmail邮件系统
- linux系统下无法创建mysql存储过程问题