• c语言-遍历pci设备(1)io访问


    前言

    最近楼主比较苦逼啊,主管布置了一道访问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设备。

    #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#	Dev#	Func#");
    printf("
    ");
    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	%2.2x	%2.2x	",bus,dev,func);
    printf("
    "); 
    }
    }
    }
    }
    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#	dev#	fun#	");
    printf("
    ");
    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	%2x	%2x",bus,dev,fun);
    printf("
    ");
    } 
    }
    ret = iopl(0);
    if(ret < 0){
    perror("iopl set error");
    return -1;
    }
    return 0;
    
    }
    

    后记

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

  • 相关阅读:
    DNX SDK版本 “dnx-clr-win-x86.1.0.0-beta5”无法安装
    【循序渐进MVC】第一回——一物多用Project.json依赖关系之dependencies节点
    ASP.NET页面中去除VIEWSTATE视图状态乱码
    git入门操作命令(转载)
    论火车票订单系统中并发问题和锁机制的探讨(转载)
    如何取消MSSQL自带智能提示步骤,使用第三方智能提示插件
    查看Windows服务器登录日志
    浅谈分布式计算系统和集群系统的区别
    数据库设计优化经验谈(转载)
    程序性能优化之防止装箱将拆装箱最小化
  • 原文地址:https://www.cnblogs.com/samuelwnb/p/4659936.html
Copyright © 2020-2023  润新知