前言

最近楼主比较苦逼啊,主管布置了一道访问pci的作业,这个作业使用io方式还可以非常浪地将所有的东西都给读取出来,虽然不能读取出pci-e设备的所有信息,但是还是可以将256位的其他东西给读出来的。

本文将先从io访问模式进行对pci访问的设置,在这里我所使用的包含了dos和linux,这样可以看到这2个的系统代码的不同。

pci简介

PCI总线是一种高性能32位或者64位的多路复用地址或者数据行的总线。相当于现实生活中的公路,是所谓的信号通道。可以在这上面传输数据、控制信号等等。

作用:高度集成外围控制其、外围插件和处理器/内存系统之间的互连机制。

如果要访问PCI 设备,首先要确定PCI设备在系统中的物理连接情况。描述这个连接情况的数据是“总线号”、“设备号”和“功能号”。一个系统可以有256个PCI总线,每个总线上可以有32个设备,每个设备可以具有8个功能(每个功能作为一个PCI设备)。当这三个数据确定的时候,就可以在系统中唯一确定一个PCI 设备。

8~10:功能位. 有时候,一个pci设备对应多个功能.将每个功能单元分离出来,对应一个独立的pci device

11~15位:设备号 对应该pci总线上的设备序号

16~23位:总线号 根总线的总线号为0.每遍历到下层总线,总线号+1

PCI拓扑结构图

在上图的总线结构中,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.也就是说,下层总线的资源是它上层总线资源的子集。上层总线资源是下层总线资源的父集。

其实,每个PCI设备的资源地始地址都是由操作系统设置的.在x86上,都由bios设置好了.

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设备

在dos下申请相关的接口就可以得到io口,通过cf8和cfc的模式进行读取遍历pci设备。

  1. #include <stdio.h>
  2. typedef unsigned long DWORD;
  3. typedef unsigned int WORD;
  4. #define MK_PDI(bus,dev,func) (WORD)((bus<<8)|(dev<<3)|(func))
  5. #define MK_PCIaddr(bus,dev,func) (DWORD)(0xf8000000L|(DWORD)MK_PDI(bus,dev,func)<<8)
  6. #define PCI_CONFIG_ADDRESS 0xCF8
  7. #define PCI_CONFIG_DATA 0xCFC
  8. DWORD inpd(int inport)
  9. {
  10. DWORD data;
  11. asm mov dx,inport;
  12. asm lea bx,data;
  13. __emit__(
  14. 0x66,0x50,
  15. 0x66,0xED,
  16. 0x66,0x89,0x07,
  17. 0x66,0x58);
  18. return data;
  19. }
  20. void outpd(int outport,DWORD addr)
  21. {
  22. asm mov dx,outport;
  23. asm lea bx,addr;
  24. __emit__(
  25. 0x66,0x50,
  26. 0x66,0x8B,0x07,
  27. 0x66,0xEF,
  28. 0x66,0x58);
  29. }
  30. DWORD GetData(DWORD addr)
  31. {
  32. DWORD data;
  33. outpd(PCI_CONFIG_ADDRESS,addr);
  34. data = inpd(PCI_CONFIG_DATA);
  35. return data;
  36. }
  37. int main()
  38. {
  39. int bus,dev,func;
  40. DWORD addr,addr1,addr2,addr3;
  41. DWORD data,data1,data2,data3;
  42. printf("Bus#\tDev#\tFunc#");
  43. printf("\n");
  44. for (bus = 0; bus <= 0x63; ++bus)
  45. {
  46. for (dev = 0; dev <= 0x1F; ++dev)
  47. {
  48. for (func = 0; func <= 0x7; ++func)
  49. {
  50. addr = MK_PCIaddr(bus,dev,func);
  51. data = GetData(addr);
  52. if((WORD)data!=0xFFFF)
  53. {
  54. printf("%2.2x\t%2.2x\t%2.2x\t",bus,dev,func);
  55. printf("\n");
  56. }
  57. }
  58. }
  59. }
  60. return 0;
  61. }

在linux系统下就很简单了,直接看代码吧!

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<sys/io.h>
  5. #define PCI_MAX_BUS 255
  6. #define PCI_MAX_DEV 31
  7. #define PCI_MAX_FUN 7
  8. #define PCI_BASE_ADDR 0x80000000L
  9. #define CONFIG_ADDR 0xcf8
  10. #define CONFIG_DATA 0xcfc
  11. typedef unsigned long DWORD;
  12. typedef unsigned int WORD;
  13. typedef unsigned long DWORD;
  14. int main()
  15. {
  16. WORD bus,dev,fun;
  17. DWORD addr,data;
  18. int ret;
  19. printf("bus#\tdev#\tfun#\t");
  20. printf("\n");
  21. ret = iopl(3);
  22. if(ret < 0)
  23. {
  24. perror("iopl set error");
  25. return -1;
  26. }
  27. for(bus = 0; bus <= PCI_MAX_BUS; bus++)
  28. for(dev = 0; dev <= PCI_MAX_DEV; dev++)
  29. for(fun = 0; fun <= PCI_MAX_FUN; fun++)
  30. {
  31. addr = PCI_BASE_ADDR|(bus << 16)|(dev << 11)|(fun << 8);
  32. outl(addr,CONFIG_ADDR);
  33. data = inl(CONFIG_DATA);
  34. if((data != 0xffffffff)&&(data != 0))
  35. {
  36. printf("%2x\t%2x\t%2x",bus,dev,fun);
  37. printf("\n");
  38. }
  39. }
  40. ret = iopl(0);
  41. if(ret < 0){
  42. perror("iopl set error");
  43. return -1;
  44. }
  45. return 0;
  46. }

后记

今天暂时到这里,下一章将介绍什么是mmio,如何实现的!欢迎关注!

c语言-遍历pci设备(1)io访问的更多相关文章

  1. c语言-遍历pci设备(2)mmio访问

    前言 今天其实我在公司也没有做什么,但是昨天就把pcie遍历的mmio形式做了出来,赞扬公司的台湾服务器,至少我可以使用google来去搜索我想要的资料和答案,有一位大神在台湾的论坛上发布了一片博文, ...

  2. 【DPDK】谈谈DPDK如何实现bypass内核的原理 其一 PCI设备与UIO驱动

    [前言] 随着网络的高速发展,对网络的性能要求也越来越高,DPDK框架是目前的一种加速网络IO的解决方案之一,也是最为流行的一套方案.DPDK通过bypass内核协议栈与内核驱动,将驱动的工作从内核态 ...

  3. 3.1 PCI设备BAR空间的初始化

    在PCI Agent设备进行数据传送之前,系统软件需要初始化PCI Agent设备的BAR0~5寄存器和PCI桥的Base.Limit寄存器.系统软件使用DFS算法对PCI总线进行遍历时,完成这些寄存 ...

  4. 008 PCI设备BAR空间的初始化

    一.PCI设备BAR空间的初始化 在PCI Agent设备进行数据传送之前,系统软件需要初始化PCI Agent设备的BAR0~5寄存器和PCI桥的Base.Limit寄存器.系统软件使用DFS算法对 ...

  5. Linux驱动设计——内存与IO访问

    名词解释 内存空间与IO空间 内存空间是计算机系统里面非系统内存区域的地址空间,现在的通用X86体系提供32位地址,寻址4G字节的内存空间,但一般的计算机只安装256M字节或者更少的内存,剩下的高位内 ...

  6. PCI 设备详解二

    上篇文章主要从硬件的角度分析了PCI设备的特性以及各种寄存器,那么本节就结合LInux源代码分析下内核中PCI设备的各种数据结构以及相互之间的联系和工作机制 2016-10-09 注:一下代码参考LI ...

  7. PCI 设备详解一

    2016-10-09 其实之前是简单学习过PCI设备的相关知识,但是总感觉 自己的理解很函数,很多东西说不清楚,正好今天接着写这篇文章自己重新梳理一下,文章想要分为三部分,首先介绍PCI设备硬件相关的 ...

  8. 系统虚拟化学习笔记——PCI设备

    内容摘自<系统虚拟化:原理与实现> PCI 总线架构 PCI总线是典型的树结构.把北桥中host-PCI桥看做根,总线中其他PCI-PCI桥,PCI-ISA桥(ISA总线转PCI总线桥)等 ...

  9. linux PCI设备初始化过程

    linux PCI设备初始化过程 start_kernel->rest_init 这个函数会启动一个核心线程0, 核心线程然后调用init -> do_basic_setup. 然后我们开 ...

随机推荐

  1. bootstrap手风琴折叠

    <!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title> ...

  2. RxSwift学习笔记5:Binder

    使用 Binder 创建观察者 //Observable序列(每隔1秒钟发出一个索引数) let scheduleObservable = Observable<Int>.interval ...

  3. 前端开发 - JavaScript 词法分析

    JavaScript代码运行前有一个类似编译的过程即词法分析,词法分析主要有三个步骤: 1.分析函数的参数 2.分析函数的变量声明 3.分析函数的函数声明表达式 具体步骤如下: 函数在运行的瞬间,生成 ...

  4. F - Cookies Piles

    Description The kids in my son's kindergarten made Christmas cookies with their teacher, and piled t ...

  5. .Net Core Web应用发布至IIS后报“An error occurred while starting the application”错误

    An error occurred while starting the application. .NET Core X64 v4.1.1.0    |   Microsoft.AspNetCore ...

  6. 【DirectX】 AudioVideoPlayback 中的事件BUG

    当访问 Video 中的 Audio 属性时,会造成 Video 的所有事件失效.经过反汇查看源码,原来在访问Audio属性时,Audio会通过当前Video对象创建一个新实例.而这个新实例会覆盖掉当 ...

  7. cesiumjs

    官网 https://cesium.com/ https://cesiumjs.org/Cesium/ 论坛 http://cesium.coinidea.com/ 中文网 http://cesium ...

  8. 【转】javascript深入理解js闭包

    原文:http://www.jb51.net/article/24101.htm 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 一.变量的作 ...

  9. 《jQuery基础教程(第四版)》学习笔记

    本书代码参考:Learning jQuery Code Listing Browser 原书: jQuery基础教程 目录: 第2章 选择元素 1. 使用$()函数 2. 选择符 3. DOM遍历方法 ...

  10. CentOS 7修改yum源为阿里源

    1.备份本地源 1 # mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo_bak 2.获取阿里yum源配置 ...