讀取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 -------------------------------------------------------------------
- 供應商識別字段(Vendor ID)該欄位用一標明裝置的製造者。一個有效的供應商標識由PCI SIG來分配,以保證它的唯一性。0FFFFH是該欄位的無效值。
- 裝置識別字段(Device ID)用以標明特定的裝置,具體程式碼由供應商來分配。
- 版本識別字段(Revision ID)用來指定一個裝置特有的版本識別程式碼,其值由供應商提供,可以是0。
- 頭標型別欄位(Header Type)該欄位有兩個作用,一是用來表示配置空間頭標區第二部分的佈局型別;二是用以指定裝置是否包含多功能。位7用來標識一個多功能裝置,位7為0表明是單功能裝置,位7為1表明是多功能裝置。位0-位6表明頭標區型別。
- 分類程式碼欄位(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> /// 向