1. 程式人生 > >讀取PCI配置空間資料並操作其對映的實體記憶體

讀取PCI配置空間資料並操作其對映的實體記憶體

PC機在啟動的時候,都會看到一個PCI裝置清單,可以看到機器中的所有PCI裝置,其實搜尋PCI裝置的程式並不難編,本文通過一個例項說明如何遍歷PCI裝置。

1、瞭解PCI裝置

       PCI的含義是外設部件互連(Peripheral Component Interconnect),PCI區域性匯流排(Local Bus)是1991年由Intel定義的,現在PCI區域性匯流排已經成為了PC機中不可缺少的外圍裝置匯流排,幾乎所有的外部裝置都連線到PCI區域性總線上, 我們說的PCI裝置,實際上就是指連線在PCI區域性總線上的裝置。

2、瞭解PCI配置空間

       學習PCI程式設計,不瞭解PCI的配置空間是不可能的,配置空間是一塊容量為256位元組並具有特定記錄結構或模型的地址空間,通過配置空間,我們可以瞭解該PCI裝置的一些配置情況,進而控制該裝置,除主匯流排橋以外的所有PCI裝置都必須事先配置空間,本節僅就一些配置空間的共有的規定作一些說明,更加具體和詳細的資訊請參閱其他書籍及相應的晶片手冊。
       配置空間的前64個位元組叫頭標區,頭標區又分成兩個部分,第一部分為前16個位元組,在各種型別的裝置中定義都是一樣的,其他位元組隨各裝置支援的功能不同而有所不同,位於偏移0EH的投標型別欄位規定了是何種佈局,目前有三種頭標型別,頭標型別1用於PCI-PCI橋,頭標型別2用於PCI-CARDBUS 橋,頭標型別0用於其他PCI裝置,下圖為頭標型別0的頭標區佈局。
		DW |    Byte3    |    Byte2    |    Byte1    |     Byte0     | Addr
		---+---------------------------------------------------------+-----
		 0 |     Device ID     |     Vendor ID      | 00
		---+---------------------------------------------------------+-----
		 1 |      Status     |      Command      | 04
		---+---------------------------------------------------------+-----
		 2 |        Class Code        | Revision ID | 08
		---+---------------------------------------------------------+-----
		 3 |   BIST  | Header Type | Latency Timer | Cache Line  | 0C
		---+---------------------------------------------------------+-----
		 4 |           Base Address 0           | 10
		---+---------------------------------------------------------+-----
		 5 |           Base Address 1           | 14
		---+---------------------------------------------------------+-----
		 6 |           Base Address 2           | 18
		---+---------------------------------------------------------+-----
		 7 |           Base Address 3           | 1C
		---+---------------------------------------------------------+-----
		 8 |           Base Address 4           | 20
		---+---------------------------------------------------------+-----
		 9 |           Base Address 5           | 24
		---+---------------------------------------------------------+-----
		10 |          CardBus CIS pointer          | 28
		---+---------------------------------------------------------+-----
		11 |  Subsystem Device ID  |   Subsystem Vendor ID   | 2C
		---+---------------------------------------------------------+-----
		12 |        Expansion ROM Base Address        | 30
		---+---------------------------------------------------------+-----
		13 |        Reserved(Capability List)         | 34
		---+---------------------------------------------------------+-----
		14 |            Reserved             | 38
		---+---------------------------------------------------------+-----
		15 |  Max_Lat  |  Min_Gnt  |  IRQ Pin  |  IRQ Line  | 3C
		-------------------------------------------------------------------
       頭標區中有5個欄位涉及裝置的識別。
  1. 供應商識別字段(Vendor ID)該欄位用一標明裝置的製造者。一個有效的供應商標識由PCI SIG來分配,以保證它的唯一性。0FFFFH是該欄位的無效值。
  2. 裝置識別字段(Device ID)用以標明特定的裝置,具體程式碼由供應商來分配。
  3. 版本識別字段(Revision ID)用來指定一個裝置特有的版本識別程式碼,其值由供應商提供,可以是0。
  4. 頭標型別欄位(Header Type)該欄位有兩個作用,一是用來表示配置空間頭標區第二部分的佈局型別;二是用以指定裝置是否包含多功能。位7用來標識一個多功能裝置,位7為0表明是單功能裝置,位7為1表明是多功能裝置。位0-位6表明頭標區型別。
  5.  分類程式碼欄位(Class Code)標識裝置的總體功能和特定的暫存器級程式設計介面。該位元組分三部分,每部分佔一個位元組,第一部分是基本分類程式碼,位於偏移0BH,第二部分叫子分類程式碼,位於偏移0AH處,第三部分用於標識一個特定的暫存器級程式設計介面(如果有的話)。

3、配置空間暫存器的讀寫

       x86的CPU只有記憶體和I/O兩種空間,沒有專用的配置空間,PCI協議規定利用特定的I/O空間操作驅動PCI橋路轉換成配置空間的操作。目前存在兩種轉換機制,即配置機制1#和配置機制2#。配置機制2#在新的設計中將不再被採用,新的設計應使用配置機制1#來產生配置空間的物理操作。這種機制使用了兩個特定的32位I/O空間,即CF8h和CFCh。這兩個空間對應於PCI橋路的兩個暫存器,當橋路看到CPU在區域性匯流排對這兩個I/O空間進行雙字 操作時,就將該I/O操作轉變為PCI匯流排的配置操作。暫存器CF8h用於產生配置空間的地址(CONFIG-ADDRESS),暫存器CFCh用於儲存 配置空間的讀寫資料(CONFIG-DATA)。
將要訪問配置空間暫存器的匯流排號、裝置號、功能號和暫存器號以一個雙字的格式寫到配置地址埠 (CF8H-CFBH),接著執行配置資料埠(CFCH)的讀和寫,向配置資料口寫資料即向配置空間寫資料,從配置資料口讀資料即從配置空間讀資料。
配置地址埠(CF8H)的格式定義如下:
        暫存器號:選擇配置空間中的一個雙字(32位)
        功能號:  選擇多功能裝置中的某一個功能,有八種功能,0--7
        裝置號:  在一條給定的總線上選擇32個裝置中的一個。0--31
        匯流排號:  從系統中的256條匯流排中選擇一條,0--255
        儘管理論上可以有256條匯流排,但實際上PC機上PCI插槽的匯流排號都是1,有些工控機的匯流排號是2或者3,所以我們只需要查詢0--4號匯流排就足夠了。PCI規範規定,功能0是必須實現的,所以,如果功能0的頭標型別欄位的位7為0,表明這是一個單功能裝置,則沒有必要再去查其他功能,否則要查詢所有其他功能。

4、讀取PCI裝置

       至此,我們掌握的有關PCI的知識已經足夠我們遍歷PCI裝置了,其實便利方法非常簡單就是按照匯流排號、裝置號、功能號的順序依次羅列所有的可能性,讀取配置空間頭標區的供應商程式碼、及裝置程式碼,進而找到所有PCI裝置。下面是一個例子,關於讀取指定PCI裝置的指定配置空間資料的方法,同時可以根據讀取的BAR值操作實體記憶體。
#include <sys/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define PCI_CONFIG_ADDR(bus, dev, fn, reg) (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))

void usage()
{
    printf("Usage: readpci [-bdfrth]\n\
            -a   addr  :   specify bar address default 0\n\
            -b   bus   :   specify   PCI   bus   number(default   0)\n\
            -d   dev   :   device   number(default   0)\n\
            -f   fn    :   function   number(default   0)\n\
            -r   reg   :   register   address(must   be   multiple   of   4,   default   0)\n\
            -p   port  :   specify port number default 0\n\
            -v   value :   write a integer value into the address\n\
            -h         :   print   this   help   text\n ");
    exit(-1);
}

int operaMem(int bar, int offset, int modValue, int isWrite)
{
	int i;
	int fd;
	char* mem;
	//open /dev/mem with read and write mode
	if((fd = open ("/dev/mem", O_RDWR)) < 0)
	{
		perror ("open error\n");
		return -1;
	}

	//map physical memory 0-10 bytes
	mem = mmap (0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bar);
	if (mem == MAP_FAILED)
	{
		perror ("mmap error:\n");
		return 1;
	}

	int value = *((int *)(mem + offset));
    printf("The value at 0x%x is 0x%x\n", bar + offset, value);
    if(isWrite == 1)
    {
        printf("Write value 0x%x at 0x%x\n", modValue, bar + offset);
        memcpy(mem + offset, &modValue, 4);
        printf("Reread the value at 0x%x is 0x%x\n", bar + offset, *((int *)(mem + offset)));
    }
    
	munmap (mem, 4096); //destroy map memory
	close (fd);         //close file
	return 0;
}

int parseInt(char *str)
{
    int value = 0;

    char tmpChar;
    while((tmpChar = *str) != '\0')
    {
        if(tmpChar >= 'A' && tmpChar <= 'F')
        {
            tmpChar = tmpChar - 'A' + 10;
        }
        else if(tmpChar >= 'a' && tmpChar <= 'f')
        {
            tmpChar = tmpChar - 'a' + 10;
        }
        else
        {
            tmpChar -= '0';
        }
        value = value * 16 + tmpChar;
        str += 1;
    }
    return value;
}

int main(int argc, char **argv)
{
    unsigned long val = 0;
    char options[] = "a:b:d:f:r:v:p:h";
    int addr = 0, bus = 0, dev = 0, fn = 0, reg = 0, port = 0;
    int opt, value = 0, isWrite = 0, isReadBar = 0;
    while((opt = getopt(argc, argv, options)) != -1)
    {
        switch(opt)
        {
            case 'a':
                addr = parseInt(optarg);
                break;
            case 'b':
                bus = atoi(optarg);
                break;
            case 'd':
                dev = atoi(optarg);
                break;
            case 'f':
                fn = atoi(optarg);
                break;
            case 'r':
                reg = parseInt(optarg);
                break;
            case 'p':
                port = atoi(optarg);
                isReadBar = 1;
                break;
            case 'v':
                value = parseInt(optarg);
                isWrite = 1;
                break;
            case 'h':
            default:
                usage();
                break;
        }
    }

    iopl(3);
    if(isWrite == 0)
    {
        if(isReadBar == 0)
        {
            int i;
            for(i = 0; i < 256; i += 4)
            {
                outl(PCI_CONFIG_ADDR(bus, dev, fn, i), 0xCF8);
                val = inl(0xCFC);
                printf("PCI:Bus %d, DEV %d, FUNC %d, REG %x, Value is %x\n ", bus, dev, fn, i, val);
            }
        }
        else
        {
            outl(PCI_CONFIG_ADDR(bus, dev, fn, 0x10), 0xCF8);
            val = inl(0xCFC) & 0xfffffff0;
            printf("The base address value is 0x%x\n", val);
            int pointAddr = val + port * 0x1000;
            printf("The offset address value is 0x%x\n", pointAddr + addr);
            operaMem(pointAddr, addr, 0, 0);
        }
    }
    else
    {
        outl(PCI_CONFIG_ADDR(bus, dev, fn, 0x10), 0xCF8);
        val = inl(0xCFC) & 0xfffffff0;
        printf("The base address value is 0x%x\n", val);
        int pointAddr = val + port * 0x1000;
        printf("The offset address value is 0x%x\n", pointAddr + addr);
        operaMem(pointAddr, addr, value, 1);
    }
    return 0;
}

來自:http://blog.csdn.net/zsf8701/article/details/7817661

相關推薦

讀取PCI配置空間資料操作對映實體記憶體

PC機在啟動的時候,都會看到一個PCI裝置清單,可以看到機器中的所有PCI裝置,其實搜尋PCI裝置的程式並不難編,本文通過一個例項說明如何遍歷PCI裝置。 1、瞭解PCI裝置        PCI的含義是外設部件互連(Peripheral Component Interconnect),PCI區域性匯流排(L

Python學習_我該怎麼讀取檔案中的資料操作檔案

資料儲存在檔案中使用很方便,可我該怎麼讀取檔案呢? jiangxue.txt檔案中儲存以下內容: “ 千山鳥飛絕,萬徑人蹤滅。 孤舟蓑笠翁,獨釣寒江雪。 ” 1、讀取檔案 假設我在檔案中儲

使用JavaScript讀取所選文本復制到剪貼板

tip 相同 不為 ocl 執行命令 eight .com form html 詢問大多數開發人員如何將用戶選擇的內容復制到剪貼板,他們會提到Flash的需求(因此 Zeroclipboard等腳本的流行 )。然而,這種想法的調整現在歸功於最近瀏覽器對JavaScript中

從db2資料庫讀取blob型別圖片資料在前端頁面顯示

最近要寫一個需求,從db2中讀取圖片資料,blob型別,然後在前端頁面顯示,採用的是前後端分離的開發模式,使用下面這種方式,可以忽略前端,前端不需要動程式碼,就可直接顯示。。 直接上程式碼: sql:只給大家擷取關鍵部分,select   xmlgroup(pe.Pict

wdmWin10下讀取PCI配置資訊

WDM下HalGetBusData不能用了。加上感覺png方式太麻煩。自己修改了驅動開發技術詳解上的程式碼直接在驅動下獲取資訊 #include "Driver.h" NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNI

wdmWin10下遍歷PCI配置空間

圖右邊是引用驅動開發技術詳解書中的程式碼:3環只增加了個死迴圈 驅動沒變 #include <windows.h> #include <stdio.h> //使用CTL_CODE必須加入winioctl.h #include <winioctl.h> #include "

SpringBoot:讀取核心配置檔案application生成靜態工具類

SpringBoot:讀取核心配置檔案application並生成靜態工具類       通常在SpringBoot專案中,如果需要使用配置檔案中的配置屬性,在使用情況少的情況下,可以參下文章《Sprin

QT讀取本地txt影象資料在介面上顯示該影象

int pixel[180*1500]; int i=0; char filenametxt[150] = "inputimg.txt"; ifstream infile(filenametxt,ios::in); //測試檔案是否成功開啟 if(

python爬取資料存入mongodb

其實很早就想知道如何將爬取到的資料存入資料庫,並且實現前後臺的互動功能,昨天剛剛看了一集關於爬資料並存資料的視訊,今天,在這裡總結一下~ 以下為最終所需要爬取的資訊: 由於需要爬取所有的二手商品資訊,所以以下內容也要爬取到: 1.先寫一個py檔案,用於爬取上述圖片類目導

python讀取excel指定列資料寫入到新的excel

#encoding=utf-8 import xlrd from xlwt import * #------------------讀資料-------------------------------

PostgreSql對空間資料操作函式

  GIS開發對資料的操作,主要是對空間資料的操作。PostgreSql提供了一些處理空間資料的函式,操作起來是比較方便的。下面就記錄一下,最近做專案裡面用到的一些,也是比較常用的。 空間資料  

jsp 讀取table表格中資料,封裝成JSON陣列,用AJAX方式傳到後臺

//讀取table中的資料,並裝成陣列物件 //定義一個數組 var billData = []; //獲取選中的複選框 $(":checkbox[name='bill']").each(function(){ var bi

NodeMCU刷Arduino韌體讀取紅外測溫資料上傳到OneNet

#include <Arduino.h> #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> #include <ESP8266HTTPClient.h> #defin

C#讀取Excel表格中資料返回datatable

在軟體開發的過程中,經常用到從excel表格中讀取資料作為資料來源,以下整理了一個有效的讀取excel表格的方法。 DataTable GetDataTable(string tableName,string leftTopCel,string rightbutCel)

使用spark讀取es中的資料進行資料清洗,使用fp-growth演算法進行加工

最近學了spark,用fg-growth演算法進行資料的關聯排序 object HelloPFg { def main(args: Array[String]) { val conf = new SparkConf().setAppName("Spark M

演算法 -- 猴子選大王的四種方法,時間與記憶體消耗的分析和對比&PHP

本篇利用PHP對“猴子選大王”問題,給出了四種方法,並對其進行了時間消耗的分析與對比。 題目:n個猴子要選出一個大王,隨機給出一個數m,當猴子報數為m的時候,則被淘汰,剩餘的最後一個猴子即為大王。  一、演算法解釋及程式碼展示 方法一:圍圈報數 n 個猴子圍成一圈從 1

按遞增次序輸出帶頭節點單鏈表的資料元素釋放空間

#include "stdafx.h" #include<stdio.h> #include<malloc.h> #include<stdlib.h> typed

使用python讀取mysql資料庫進行資料操作

(一)環境的配置 使用python呼叫mysql資料庫要引進一些庫。 目前我使用的python版本是python3.6。引進庫為pymysql 其他對應的庫可以有以下選擇:  mysqldb,oursql, PyMySQL, myconnpy 等,參考如下連結:  http://de

python讀取sql裡面的指定資料列,轉換成列表使用

程式碼如下: import pyodbc import pandas as pd import numpy as np conn = pyodbc.connect(r'DRIVER={SQL Server Native Client 10.0};SERVER=.;DATABASE=資料庫名字

C#,WPF中使用多文字顯示資料資料進行關鍵字高亮等操作

需求:針對多文字資訊顯示,我們需要對其內容中的某些關鍵字或者某行進行高亮顯示,並用不同顏色顯示。 分析:在C#中,首先要進行多文字資訊顯示,可以RichTextBox(不要使用TextBox)控制元件,該控制元件由自動換行等功能,具體程式碼如下: /// <summary> /// 向