2011-10-26 74 views
6

我有一些我想模擬的硬件;我想知道我是否可以在這樣的低水平上做到這一點。硬件有許多寄存器,這是我在一個結構安排:如何模擬內存映射I/O

#include <stdint.h> 
struct MyControlStruct 
{ 
    uint32_t data_reg_1; 
    uint32_t data_reg_2; 
    uint32_t dummy[2]; // to make the following registers have certain addresses 
    uint32_t control_reg_1; 
    uint32_t control_reg_2; 
}; 
volatile struct MyControlStruct* MyDevice = (struct MyControlStruct*)0xDeadF00; 

所以,我想,以支持在Windows硬件訪問和linux的語法如下:

MyDevice->data_reg_1 = 42; 
MyDevice->data_reg_2 = 100; 
MyDevice->control_reg_1 = 1; 

當代碼的最後一行被執行,我想讓硬件模擬器「醒來」並做一些事情。我可以在Windows和/或Linux上實現這個嗎?我想過要捕捉「分段故障」信號,但不知道這是否可以在Windows上完成,或者根本不知道。

我看了一下手冊頁mmap;它似乎可以幫助,但我不明白我可以如何使用它。

當然,我可以通過定義諸如WriteToMyDevice之類的函數來抽象化硬件訪問,並且一切都很容易(也許),但是我想了解我是否可以以這種確切方式安排訪問我的硬件。

+0

您的「低級別」過高......在Windows和Linux硬件訪問都是從內核模式完成的。你需要考慮更多的實現細節 - 如何與硬件進行交流。例如,您可以編寫真實的驅動程序和驅動程序仿真。 –

+0

那麼,在用戶模式下使用預定義的內存地址是不可能的? – anatolyg

+0

您無法直接與使用直接內存訪問的Windows/Linux用戶模式硬件設備進行通話。這隻有在內核模式下才有可能。這就是爲什麼我建議你先考慮實施細節。沒有他們,你實際上不知道該效仿什麼。 –

回答

2

原則上,您可以編寫SIGSEGV的處理程序(不可移植),它可以捕獲和處理對不需要的頁面的訪問,並可以檢查指定的地址是否被訪問。

要在Linux下執行此操作,您需要使用sigaction系統調用SA_SIGINFO並使用信號處理程序的第三個參數ucontext_t*

這是非常不可移植的:對於不同的Unix(甚至可能是Linux內核的版本號可能很重要)以及更換處理器時,您將不得不針對不同的代碼進行編碼。

我聽說Linux內核在這種處理上並不是很快。

其他更好的內核(Hurd,Plan9)提供用戶級別的分頁,這應該有所幫助。

1

實際上,您的模擬器(非常粗暴)在純Linux用戶空間代碼上是可能的。

要構建仿真器,簡單地具有第二線程或進程(使用共享存儲器,或者一個文件mmap'd和inotify的)觀看該模擬存儲器映射設備

對於真正的硬件驅動存儲器,您將需要一點內核代碼,但這可能只是將實際硬件地址映射到具有適當權限的用戶空間中。實際上,這可以將現代多用戶操作環境降低到像舊的dos箱或簡單的微控制器那樣 - 這不是一個很好的實踐,但至少在安全性不受關注的情況下是可行的。

您可以考慮的另一件事是在虛擬機中運行代碼。

如果您要鍛鍊的代碼是您自己的代碼,那麼最好先以便攜的方式編寫代碼,將硬件訪問抽象爲可以爲每個平臺重新編寫的函數(即OS,硬件版本或物理/仿真)。如果這些技術是您需要爲其創建環境的其他人的現有代碼,那麼這些技術會更有用。你可以考慮的另一件事情(如果原始文件不是太緊密集成)使用特定函數的動態庫級別攔截,例如使用Linux上的LD_PRELOAD或Windows上的包裝DLL。或者就此而言,修補二進制文件。

1

我最初誤解了你的問題。你有一塊內存映射硬件,你希望你的仿真是二進制兼容的。在Windows上,您可以使用VirtualAlloc爲結構分配內存,並使其成爲警戒頁面,並使用SEH捕獲對其的訪問。