歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C語言-遍歷pci設備

C語言-遍歷pci設備

日期:2017/3/1 9:27:18   编辑:Linux編程

目錄

  • 前言
  • pci簡介
  • 後記

前言

最近樓主比較苦逼啊,主管布置了一道訪問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#\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;

} 

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2015-07/120302p2.htm

Copyright © Linux教程網 All Rights Reserved