標籤

2008年8月29日 星期五

HOOK

1. test point: 代表類似 Task ID的意義;即做一件事前先丟值(byte)到Port 80.代表"準備要做這件事了". 其值只拿來當作ID;兩兩 tasks的 test point的比較沒有意義 !

2. hook address: 在 POST所做的 tasks 都由幾個要件組成:
a. ID(that is, test point)
b. hook routine (<-你指的 hook address吧...)
c. post routine

b代表:在 c之前要做的事情;hook的英文是"勾";想像有 routine在"這點"被"勾"進來做,做完了做 post routine.

3. flash:將 bios image "寫" 到 ROM中. 當 BIOS checksum fail 或是 有發現特定裝置存在時,BIOS會決定要做 "flash"的動作.

Bootblock中的 flow可分成兩部份: task and flash phase;若沒有需要 flash,則跑完 task table後就離開;若需要 flash則加跑 flash table的部份.

4. BIOS shadow當然在 memory ready/stable之後;shadow code當然放在 ROM中.
Bootblock 是開機後第一個會跑的. 跑完 BB才跑 BIOS entry point(通常 1st instruction is cli). 沒有所謂的 shadow bootblock. 至於BIOS shadow,可以搜尋本網站,有頗詳細的敘述.

鉤子是一種机制,用來截獲你發住指定窗口的各種消息包括鍵盤消息,mouse消息及日志等,你可以查看MSDN或<>中打破進程邊界一節中有詳細敘述.

hook是WINDOWS提供的一種消息處理机制,它使得程序員可以使用子過程來監視系統消息,並在消息到達目標過程前得到處理
使用HOOK 將會降低系統效率,因為它增加了系統處量消息的工作量。建議在必要時才使用HOOK,並在消息處理完成后立即移去該HOOK。
WINDOWS提供了几種不同類型的HOOKS;不同的HOOK可以處理不同的消息。例如,WH_MOUSE HOOK用來監視鼠標消息。
HOOK過程
為了攔截特定的消息,你可以使用SetWindowsHookEx函數在該類型的HOOK鏈中安裝你自己的HOOK函數。該函數語法如下:
public function MyHook(nCode,wParam,iParam) as long
‘加入代碼







運行机制

1、鉤子鏈表和鉤子子程:

每一個Hook都有一個與之相關聯的指針列表,稱之為鉤子鏈表,由系統來維護。這個列表的指針指向指定的,應用程序定義的,被Hook子程調用的回調函數,也就是該鉤子的各個處理子程。當與指定的Hook類型關聯的消息發生時,系統就把這個消息傳遞到Hook子程。一些Hook子程可以只監視消息,或者修改消息,或者停止消息的前進,避免這些消息傳遞到下一個Hook子程或者目的窗口。最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最后,也就是后加入的先獲得控制權。

Windows 並不要求鉤子子程的卸載順序一定得和安裝順序相反。每當有一個鉤子被卸載,Windows 便釋放其占用的內存,並更新整個Hook鏈表。如果程序安裝了鉤子,但是在尚未卸載鉤子之前就結束了,那麼系統會自動為它做卸載鉤子的操作。

鉤子子程是一個應用程序定義的回調函數(CALLBACK Function),不能定義成某個類的成員函數,只能定義為普通的C函數。用以監視系統或某一特定類型的事件,這些事件可以是與某一特定線程關聯的,也可以是系統中所有線程的事件。

鉤子子程必須按照以下的語法:

LRESULT CALLBACK HookProc
(
int nCode,
WPARAM wParam,
LPARAM lParam
);

HookProc是應用程序定義的名字。

nCode參數是Hook代碼,Hook子程使用這個參數來确定任務。這個參數的值依賴于Hook類型,每一種Hook都有自己的Hook代碼特征字符集。

wParam和lParam參數的值依賴于Hook代碼,但是它們的典型值是包含了關于發送或者接收消息的信息。

2、鉤子的安裝與釋放:

使用API函數SetWindowsHookEx()把一個應用程序定義的鉤子子程安裝到鉤子鏈表中。SetWindowsHookEx函數總是在Hook鏈的開頭安裝Hook子程。當指定類型的Hook監視的事件發生時,系統就調用與這個Hook關聯的Hook鏈的開頭的Hook子程。每一個Hook鏈中的Hook子程都決定是否把這個事件傳遞到下一個Hook子程。Hook子程傳遞事件到下一個Hook子程需要調用CallNextHookEx函數。

HHOOK SetWindowsHookEx(
int idHook, // 鉤子的類型,即它處理的消息類型
HOOKPROC lpfn, // 鉤子子程的地址指針。如果dwThreadId參數為0
// 或是一個由別的進程創建的線程的標識,
// lpfn必須指向DLL中的鉤子子程。
// 除此以外,lpfn可以指向當前進程的一段鉤子子程代碼。
// 鉤子函數的入口地址,當鉤子鉤到任何消息后便調用這個函數。
HINSTANCE hMod, // 應用程序實例的句柄。標識包含lpfn所指的子程的
DLL。
// 如果dwThreadId 標識當前進程創建的一個線程,
// 而且子程代碼位于當前進程,hMod必須為NULL。
// 可以很簡單的設定其為本應用程序的實例句柄。
DWORD dwThreadId // 與安裝的鉤子子程相關聯的線程的標識符。
// 如果為0,鉤子子程與所有的線程關聯,即為全局鉤子。
);

函數成功則返回鉤子子程的句柄,失敗返回NULL。

以上所說的鉤子子程與線程相關聯是指在一鉤子鏈表中發給該線程的消息同時發送給鉤子子程,且被鉤子子程先處理。

在鉤子子程中調用得到控制權的鉤子函數在完成對消息的處理后,如果想要該消息繼續傳遞,那麼它必須調用另外一個SDK中的API函數CallNextHookEx來傳遞它,以執行鉤子鏈表所指的下一個鉤子子程。這個函數成功時返回鉤子鏈中下一個鉤子過程的返回值,返回值的類型依賴于鉤子的類型。這個函數的原型如下:

LRESULT CallNextHookEx
(
HHOOK hhk;
int nCode;
WPARAM wParam;
LPARAM lParam;
);

hhk為當前鉤子的句柄,由SetWindowsHookEx()函數返回。

NCode為傳給鉤子過程的事件代碼。

wParam和lParam 分別是傳給鉤子子程的wParam值,其具體含義與鉤子類型有關。


鉤子函數也可以通過直接返回TRUE來丟棄該消息,並阻止該消息的傳遞。否則的話,其他安裝了鉤子的應用程序將不會接收到鉤子的通知而且還有可能產生不正确的結果。

鉤子在使用完之后需要用UnHookWindowsHookEx()卸載,否則會造成麻煩。釋放鉤子比較簡單,UnHookWindowsHookEx()只有一個參數。函數原型如下:

UnHookWindowsHookEx
(
HHOOK hhk;
);

函數成功返回TRUE,否則返回FALSE。

3、一些運行机制:

在Win16環境中,DLL的全局數据對每個載入它的進程來說都是相同的;而在Win32環境中,情況卻發生了變化,DLL函數中的代碼所創建的任何對象(包括變量)都歸調用它的線程或進程所有。當進程在載入DLL時,操作系統自動把DLL地址映射到該進程的私有空間,也就是進程的虛擬地址空間,而且也復制該DLL的全局數据的一份拷貝到該進程空間。也就是說每個進程所擁有的相同的DLL的全局數据,它們的名稱相同,但其值卻並不一定是相同的,而且是互不干涉的。


因此,在Win32環境下要想在多個進程中共享數据,就必須進行必要的設置。在訪問同一個Dll的各進程之間共享存儲器是通過存儲器映射文件技朮實現的。也可以把這些需要共享的數据分離出來,放置在一個獨立的數据段里,並把該段的屬性設置為共享。必須給這些變量賦初值,否則編譯器會把沒有賦初始值的變量放在一個叫未被初始化的數据段中。

#pragma data_seg預處理指令用于設置共享數据段。例如:

#pragma data_seg("SharedDataName")
HHOOK hHook=NULL;
#pragma data_seg()

在#pragma data_seg("SharedDataName")和#pragma data_seg()之間的所有變量將被訪問該Dll的所有進程看到和共享。再加上一條指令#pragma comment(linker,"/section:.SharedDataName,rws"),那麼這個數据節中的數据可以在所有DLL的實例之間共享。所有對這些數据的操作都針對同一個實例的,而不是在每個進程的地址空間中都有一份。

當進程隱式或顯式調用一個動態庫里的函數時,系統都要把這個動態庫映射到這個進程的虛擬地址空間里(以下簡稱"地址空間")。這使得DLL成為進程的一部分,以這個進程的身份執行,使用這個進程的堆棧。

4、系統鉤子與線程鉤子:

SetWindowsHookEx()函數的最后一個參數決定了此鉤子是系統鉤子還是線程鉤子。


線程勾子用于監視指定線程的事件消息。線程勾子一般在當前線程或者當前線程派生的線程內。


系統勾子監視系統中的所有線程的事件消息。因為系統勾子會影響系統中所有的應用程序,所以勾子函數必須放在獨立的動態鏈接庫(DLL) 中。系統自動將包含"鉤子回調函數"的DLL映射到受鉤子函數影響的所有進程的地址空間中,即將這個DLL注入了那些進程。

几點說明:

(1)如果對于同一事件(如鼠標消息)既安裝了線程勾子又安裝了系統勾子,那麼系統會自動先調用線程勾子,然后調用系統勾子。

(2)對同一事件消息可安裝多個勾子處理過程,這些勾子處理過程形成了勾子鏈。當前勾子處理結束后應把勾子信息傳遞給下一個勾子函數。

(3)勾子特別是系統勾子會消耗消息處理時間,降低系統性能。只有在必要的時候才安裝勾子,在使用完畢后要及時卸載。


--------------------------------------------------------------------------------

鉤子類型

每一種類型的Hook可以使應用程序能夠監視不同類型的系統消息處理机制。下面描述所有可以利用的Hook類型。

1、WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks

WH_CALLWNDPROC和WH_CALLWNDPROCRET Hooks使你可以監視發送到窗口過程的消息。系統在消息發送到接收窗口過程之前調用WH_CALLWNDPROC Hook子程,並且在窗口過程處理完消息之后調用WH_CALLWNDPROCRET Hook子程。

WH_CALLWNDPROCRET Hook傳遞指針到CWPRETSTRUCT結构,再傳遞到Hook子程。

CWPRETSTRUCT結构包含了來自處理消息的窗口過程的返回值,同樣也包括了與這個消息關聯的消息參數。

2、WH_CBT Hook

在以下事件之前,系統都會調用WH_CBT Hook子程,這些事件包括:

1. 激活,建立,銷毀,最小化,最大化,移動,改變尺寸等窗口事件;

2. 完成系統指令;

3. 來自系統消息隊列中的移動鼠標,鍵盤事件;

4. 設置輸入焦點事件;

5. 同步系統消息隊列事件。


Hook子程的返回值确定系統是否允許或者防止這些操作中的一個。

3、WH_DEBUG Hook

在系統調用系統中與其他Hook關聯的Hook子程之前,系統會調用WH_DEBUG Hook子程。你可以使用這個Hook來決定是否允許系統調用與其他Hook關聯的Hook子程。

4、WH_FOREGROUNDIDLE Hook

當應用程序的前台線程處于空閑狀態時,可以使用WH_FOREGROUNDIDLE Hook執行低優先級的任務。當應用程序的前台線程大概要變成空閑狀態時,系統就會調用WH_FOREGROUNDIDLE Hook子程。

5、WH_GETMESSAGE Hook

應用程序使用WH_GETMESSAGE Hook來監視從GetMessage or PeekMessage函數返回的消息。你可以使用WH_GETMESSAGE Hook去監視鼠標和鍵盤輸入,以及其他發送到消息隊列中的消息。

6、WH_JOURNALPLAYBACK Hook

WH_JOURNALPLAYBACK Hook使應用程序可以插入消息到系統消息隊列。可以使用這個Hook回放通過使用WH_JOURNALRECORD Hook記錄下來的連續的鼠標和鍵盤事件。只要WH_JOURNALPLAYBACK Hook已經安裝,正常的鼠標和鍵盤事件就是無效的。

WH_JOURNALPLAYBACK Hook是全局Hook,它不能象線程特定Hook一樣使用。

WH_JOURNALPLAYBACK Hook返回超時值,這個值告訴系統在處理來自回放Hook當前消息之前需要等待多長時間(毫秒)。這就使Hook可以控制實時事件的回放。

WH_JOURNALPLAYBACK是system-wide local hooks,它們不會被注射到任何行程位址空間。

7、WH_JOURNALRECORD Hook

WH_JOURNALRECORD Hook用來監視和記錄輸入事件。典型的,可以使用這個Hook記錄連續的鼠標和鍵盤事件,然后通過使用WH_JOURNALPLAYBACK Hook來回放。

WH_JOURNALRECORD Hook是全局Hook,它不能象線程特定Hook一樣使用。

WH_JOURNALRECORD是system-wide local hooks,它們不會被注射到任何行程位址空間。

8、WH_KEYBOARD Hook

在應用程序中,WH_KEYBOARD Hook用來監視WM_KEYDOWN and WM_KEYUP消息,這些消息通過GetMessage or PeekMessage function返回。可以使用這個Hook來監視輸入到消息隊列中的鍵盤消息。

9、WH_KEYBOARD_LL Hook

WH_KEYBOARD_LL Hook監視輸入到線程消息隊列中的鍵盤消息。

10、WH_MOUSE Hook

WH_MOUSE Hook監視從GetMessage 或者 PeekMessage 函數返回的鼠標消息。使用這個Hook監視輸入到消息隊列中的鼠標消息。

11、WH_MOUSE_LL Hook

WH_MOUSE_LL Hook監視輸入到線程消息隊列中的鼠標消息。

12、WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks

WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我們可以監視菜單,滾動條,消息框,對話框消息並且發現用戶使用ALT+TAB or ALT+ESC 組合鍵切換窗口。WH_MSGFILTER Hook只能監視傳遞到菜單,滾動條,消息框的消息,以及傳遞到通過安裝了Hook子程的應用程序建立的對話框的消息。WH_SYSMSGFILTER Hook監視所有應用程序消息。


WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks使我們可以在模式循環期間過濾消息,這等价于在主消息循環中過濾消息。


通過調用CallMsgFilter function可以直接的調用WH_MSGFILTER Hook。通過使用這個函數,應用程序能夠在模式循環期間使用相同的代碼去過濾消息,如同在主消息循環里一樣。

13、WH_SHELL Hook

外殼應用程序可以使用WH_SHELL Hook去接收重要的通知。當外殼應用程序是激活的並且當頂層窗口建立或者銷毀時,系統調用WH_SHELL Hook子程。

WH_SHELL 共有5鐘情況:

1. 只要有個top-level、unowned 窗口被產生、起作用、或是被摧毀;

2. 當Taskbar需要重畫某個按鈕;

3. 當系統需要顯示關于Taskbar的一個程序的最小化形式;

4. 當目前的鍵盤布局狀態改變;

5. 當使用者按Ctrl+Esc去執行Task Manager(或相同級別的程序)。

按照慣例,外殼應用程序都不接收WH_SHELL消息。所以,在應用程序能夠接收WH_SHELL消息之前,應用程序必須調用SystemParametersInfo function注冊它自己。

參考資料:http://www.zhenwm.com/article.asp?id=1
end function
其中MyHook可以隨便命名,其它不能變。該函數必須放在模塊段。nCode指定HOOK類型。wParam,iParam的取值隨nCode不同而不同,它代表了某種類型的HOOK的某個特定的動作。

2008年8月28日 星期四

BIg real mode

http://www.programmer-club.com/pc2020v5/forum/showSameTitleN.asp?board_pc2020=assembly&id=5188&keyword=

我查了討論區有關Big Mode/Flat Mode/Protected Mode/Big Real Mode/Flat Memort Model 以及相關的Intel 說明,但是對於這個Mode 還是不太懂, 我看到的資料都是說 protect mode 跟 Big Mode (我統一這個說法以免混淆)不一樣,但是不一樣的地方在哪裡?先說說我目前了解的地方:

80386 開始的保護模式PM提出了虛擬記憶體VM的概念(80286就有,只是386開始落實),VM最主要的概念是讓OS以為有很多用不完的記憶體,為了達成這個理想提出了利用分段(Segment)及分頁的方式,原本真實模式RM下是利用段暫存器*10h+偏移位址方式來表示出記憶體位址,而這個方式可以看作是 base address + limit 的方式,簡單說就是原本方式 F000:FFFF=F000*10h + FFFF = FFFFF 可以看作是Base address=F0000 他的區段大小的限制在FFFF(64k),所以F0000~FFFFF是他的區段。

除了這個觀念外另一個觀念就是RM的時候因為暫存器16 bit 所以沒辦法定址到全部的位址因此才使用上述的這種方式,但是到了386年代暫存器已經32 bit,所以一般暫存器就可以存取4G的位址而不需要用到"段暫存器"。 因為可以存取到4G而不需要段暫存器,所以在80386的保護模式的虛擬記憶體的分段概念中段暫存器就不是拿來當作存放Base Address用途,而是拿來當段描述的選擇器Segment Selector(或稱之為Index也可以看作是Offset)。

另外就是保護模式要保護什麼?他就是要保護程式彼此之間不會去存取到對方的程式段或是資料段以免發生問題,因此在386 PM模式裡面他去描述了像是一些存取權限/優先權(Ring 0~3)...等的東西,因為要描述這些東西所以他使用了64 bit的資料結構去描述 base address+Limit+優先權+存取權限,簡單說就是每一筆的描述都是64 bit(也有人說8 Bytes)大家稱這個描述叫做段描述符(Segment Descriptor),因為有很多筆所以程式設計師必須在實際的記憶體區塊去分配一個陣列放這些描述的資料,而這個陣列是大家都看得到的(PUBLIC),所以稱之為GDT(Gobal Descriptor Table),而前面說的段暫存器就是用來選這個陣列裡面的那一筆資料,那另外一個問題出現了,程式設計師分配了一會記憶體區塊來描述這些段的資訊那這個Table到底在哪裡? 因此386加入了一個暫存器叫做GDTR指向GDT的入口(GDT的Base Address),你可以利用LGDT指令把GDTR指向GDT... 因為保護所以每個任務都會有屬於自己的記憶體區段,所以每個任務也會有各自的段描述,稱之為LDT(Local Descriptor Table),LDT也是陣列,裡面也有段描述...為了統一處理,所以GDT裡面除了段描述SD0/SD1...SD N 之外還有描述LDT 0/LDT1...LDT N在哪個記憶體區段的資料(這邊要特別強調,GDT分配到一塊記憶體的區段,LDT也是,但是放在GDT裡面的LDT 0只是用來指向LDT是放那一塊記憶體的描述),那他們也設計了一個暫存器叫做LDTR來當作Index(選GDT陣列中的LDT描述),那至於你要選哪一個Table就是看段暫存器的設定,段暫存器 bit 2 =0 就是選GDT,=1就是LDT, bit 1:0=RPL ,其他的bit15:3才是當作Index。

GDT (假設放在00028000)
SD0
SD1
SD2
LDT0 (假設放在0003A000)
LDT0 ------------------------------> SD0
LDT1 SD1
LDT2 SD2

總結 :
1.RM 因為暫存器只有16 bit 所以使用Segment*10h+Offset方式,但是這個方式可看作Base Address+Limit概念

2.PM 模式提出VM概念:利用Segment 跟 Paging,他有3種定址方式: 邏輯位址(Logical Address)、線性位址(Linear Address)、和實體位址(Physical Address)。
a.所謂的「實體位址」,就是指系統的記憶體的真正位址,它的範圍由 00000000H 到 FFFFFFFFH,共有 4GB。
b.在沒有使用分頁(Paging)功能的時候,線性位址是直接對映到實體位址的,也就是說,線性位址就等同於實體位址。不過,在開啟分頁分能之後,一個線性位址可能沒有相對映的實體位址(因為它所對映的記憶體可能被 swap 到硬碟裡了)。
c.邏輯位址則和實際模式類似,仍然是 segment:offset 的形式,只不過現在 offset 的大小改成 32 bit 而已,而 segment 仍然是 16 bit。另外就是段暫存器當作是Segment Selector。

3.段暫存器用來當Index 是bit 15:3 所以是2^13 ,給ㄧ個段描述最大可以4G,所以看作是有2^13 * 4G這樣多的記憶體可以用

4.分段方式可以分成兩種:
a. Flat segmentation model : 所有區段對應整個32 bit實體空間,最少要兩個區段,1個程式段,1個資料段,描述在GDT,當Base=0 Limit=4G時可以存取整個實體位址
b.Multi-Segmentation Model:每個程式有自己的LDT,LDT由Segment Selector組成

5.澄清專有名詞: Big Mode / Flat Mode : BIOS 用來存取4G方式 (旗標BIOS Inside pp.3-17)

Flat Memory Model :應該指的是Flat segmentation model (Paper Author: Steve Gorman Title: Programming with the Intel architecture in the flat memory model)

Virtual Protected Mode/ Protected Mode : 虛擬記憶體概念的保護模式

Flat segmentation model : 分段方式的其中一種 (Assembly Language for Intel-Based Computer 4th edition,KIP R.IRVINE,全華科技)

問題:
1.一般我們看到的 xxxx:zzzzzzzzh 表示方式是指Segment Selecotr: Offset 嗎?

2.DOS底下也可以用平坦區段模式(Flat Segmentation Mode)存取4G,那他跟Big Mode有什麼不同?

3.PM模式中的分段方式裡面的平坦區段模式就可以存取4G實體位址,那麼跟Big Mode有什麼不同

4.進入保護模式PM方式是設定GDT/LDT,設定CR0,而Big Mode也是一模一樣的方式,他們有什麼不一樣?

5.Big Mode為什麼設定好Flat Segmentation Mode後要從PM切換回去RM,並且開啟A20 ?

6.RM底下開啟A20不是只能存取1M + 64k - 16 嗎? 為什麼他切進去PM設定好之後切換回來就可以存取4G ?

7.當Big Mode切換回RM之後定址方式是 Segment:Offset 還是 0: Offset 還是 Segment Selector:Offset(這個應該不可能因為RM底下沒有GDT)???

A:
在 Real Mode 下,改變 Selector 只會更新 Base Address。唯有在 Far Jump 時,CS 的 Limit 會被重載入!至於 A20 ,是由 Chipset 或 KBC 來控制 CPU 的 A20 Mask,所以無關 PM 或 RM。如果 A20 未開,就如同 CPU 的 A20 address line 不會動作一樣,那麼你可以想像會有什麼結果了!不過現在 AMD K8 似乎已沒有 A20M pin,這個古老的東西存在的目的只在相容性而已,說不定未來 Intel 也會拿掉這個東西!(286 我並不了解,也不知是否有特殊限制)
Hidden=Segment Base Address+Segment Limit+ Attri+...
分成固定與非固定兩部份: Segment Base Address是非固定,其他為固定

Real Mode底下
MOV AX,F000
MOV DS,AX ; 此時非固定部分會更新成,目前DS*10

PM Mode底下
MOV BX,Selector
MOV DS,BX ;此時固定與非固定部分都會更新

為什麼要做Short JMP?
因為 CPU 不只一條指令管線(instruction pipeline), 所以會預先提取指令(instruction fetching)並解碼(decoding), 但同樣的指令在 Protected Mode 和 Real Mode 中的解碼結果是不同的, 所以要將指令管線中在 Real Mode 解碼的部分清掉, 怎麼清呢? 執行會產生"程序控制權轉移"的指令即可, 如 jmp,jxx,call,ret,int,iret 等指令.

每個段都有自己的隱含暫存器,CPU定址是利用隱含暫存器的Base Addr + Offset 而不是Seg*10h+Offset (80286以後就不是這樣定址了) 在80386裡面隱含暫存器是64 bit,它分成幾部份:段暫存器Base Addr+ 段暫存器Limit+段暫存器屬性

有人說A20打開就可以存取0~4G了,這是對的嗎? 這個問題要考慮CPU定址方式跟位址線。當A20=0,就算你能定址0~4G,你也只能存取偶數位址。當A20=1 ,而CPU的隱含暫存器如果沒有設定成【Base Addr】=00000000,【Limit】跟【段暫存器屬性】改變成FFFFF * 4K=4GB你也沒辦法定址0~4G,所以答案告訴我們都是CPU在決定一切,A20=1只是必要的一個過程而不是打開就可以存取0~4G。

而這個答案也告訴我們只要你有辦法設定【隱含暫存器】不管那個CPU模式你都能去存取0~4G。真實模式下沒辦法設定【隱含暫存器】,唯有進入保護模式才能設定Big Real Mode 這個模式指的就是改變CPU狀態讓我們可以在真實模式底下存取0~4G的記憶體位址。

這方式步驟就是在真實模式設定好GDT,然後進入保護模式中設定好【隱含暫存器】,接著切換回去真實模式並且開啟A20。

1.找ㄧ塊記憶體區塊放置GDT Table(我們要改變的段描述也在那裡面,設定Limit=4G)
2.載入段描述表(用LGDT指令會將GDT位址放入, GDTR暫存器)
3.設定控制暫存器CR0讓CPU進入保護模式
4.做一個Short JMP,清空CPU Prefetch,因為 CPU 不只一條指令管線(instruction pipeline) , 所以會預先提取指令(instruction fetching)並解碼(decoding) ,但同樣的指令在 Protected Mode 和 Real Mode 中的解碼結果是不同的,所以要將指令管線中在 Real Mode 解碼的部分清掉,怎麼清呢? 執行會產生"程序控制權轉移"的指令即可, 如 jmp,jxx,call,ret,int,iret 等指令。
5.執行CLI指令關閉所有中斷請求以免錯誤(保護模式內段暫存器是Selector,如呼叫中斷會發生錯誤,有解決方式但超過討論範圍)
6.載入索引給你將來會使用到的段暫存器,讓CPU能夠自己去將段描述載入【隱含暫存器】
7.設定CR0,關閉保護模式,回保護模式(因為目的已經達到)
8.做一個FAR JMP ,清空CPU Prefetch ,並且給定正確的CS:IP
9.真實模式下開啟A20 位址線。

;;In Real Mode:;設定一些相關設定
LGDT ;;把GDT 放到LDTR
EnablePMMode ;;改變CR0進入保護模式
JMP Flush1 ;;丟棄已經存於CPU預先存取佇列中的指令
Flush1: ;; 這裡以經是保護模式MOV DS,BX ;;
裝Selector,給你要用的段暫存器DS/ES/FS…等
........
........
DisablePMMode ;; 改變CR0關閉保護模式
DB 0EAH ;;Far JMP回Real Mode,
DW OFFSET Flush2 ;;你程式碼的段內偏移位址
DW F000H ; ;你程式碼的段位址

Flush2: ;; 這裡以經是真實模式

XOR AX,AX
MOV DS,AX ;;DS=0,以後就可以用DS:OFFSET方式存取4G
.......

EnableA20 ;;開啟A20, by Harrison 2006.09.27


====================================================================
我想請問一下你所謂的"RESET CPU"是指HW RESET嗎? 因為我最近在看BOOTBLOCK跟POST 的Code,理論上CPU Reset 一定是去FFFF_FFF0讀第一行指令,然後看讀取的第一條指令要做什麼,但是我最近發現第一條指令是Short JMP 到BootBlock,而不是Far JMP(Far JMP才會跳到1M的地方,因為會改變CS跟IP,造成隱含暫存器的Base Addr=F0000h),所以我覺得很奇怪,感覺BIOS從開機後執行都是在A20 Enable且存取的都是4G那邊的位址,一直到了他去初始化一些HW後才回歸到1M那邊,另外A20 Disable 跟Warm Boot真的有影響嗎?
據我所知,如果你是Phoenix BIOS,你去看第一條指令,他是跳到BoolBlock執行,你可以用反組譯工具或是用Debug工具去FFFF_FFF0看,他是E9 XX XX, 'E9'是Short Jmp

FFFF_FFF0 CPU RESET來這邊讀,如果有裝BootBlock,他會做short jmp 'E9' to bookblock(真正放BootBlock程式碼的地方)

FFFE_XXXX 假設這邊是實際上 BootBlock 進入點的地方,這邊會去判斷一些事情
如果F000:FFF0 是E9,代表沒有Shadow 過
如果F000:FFF0 是EA,代表已經Shadow 過,也就可能是Warm Boot

F000:FFF0 CPU RESET一定不會讀這邊,不管是Warm Boot還是Cold Boot(因為CPU RESET一定是去FFFF_FFF0,去4G而不是1M,雖然都是同一個地方同資料,但是對CPU來說不一樣),如果已經從FFFF_FFF0 JMP到某個地方去開始執行BootBlock後,再來讀取這邊的資料的話,你會讀到跟FFFF_FFF0一樣的資料,也就是E9 xx xx,另外當POST過程中如果Shadow 相容性的資料後,這邊的資料會變成 EA F000:E05B,簡單說就是:
Shadow 前: F000:FFF0讀到的資料跟FFFF_FFF0一樣, E9 XX XX
Shadow 後: F000:FFF0讀到的資料會變成EA F000:E05B

F000:E05B BIOS Entry Point (BIOS POST Test),這邊決定的是Cold Boot或是Warm Boot(註:以BIOS觀點,因為HW的Cold/Warm Boot跟BIOS觀點不同)
P.S CPU RESET後的狀態

CS:F000 <--注意,80286之後不用CS定址
EIP:0000FFF0 <--16 bit只能看到FFF0,因為Segment Limit=64k
隱含暫存器的Base Addr=FFFF0000 <--真正被拿來定址的暫存器
隱含暫存器的Segment Limit=64k

所以第一條指令定址的方式是 Base Addr+IP=FFFF000+FFF0=FFFFFFF0 <--4G的地方

如果第一條指令是FAR JMP,則會改變CS跟IP(也就是隱含暫存器內的Base Addr會重新計算,計算方式是 Base Addr=目前CS*10h=F0000h, IP=跳躍的位址, 所以會定址方式 Base Addr+IP也就是F0000+IP,可以看做F000:IP <--1M的地方)

如果第一條指令是Short JMP,理論上Base Addr不會改變(因為Short JMP只會改變IP不會改變CS,因此Base Addr不會重新計算),所以是FFFF0000+IP <--保持這個狀態直到做了FAR JMP/FAR CALL才會改變,也就是我們說的跳到1M的地方.

因此有人說CPU讀取的第一條指令一定是FAR JMP,做了FAR JMP後就會跳到1M的地方執行,這種說法沒錯,但是這種情況是沒有BootBlock下才這樣。

忘記說明一下最近K完SPEC後發現底下的表示法的不同,這是南北橋SPEC裡面的表示法:FFFF_FFF0 <--代表4G那邊的位址,以前我一直以為FFFF:FFF0 ,我很豬頭吧! 呵呵!

跟FFFFFFF0 表示方式一樣

F000:FFF0 <--代表F000*10h + FFF0

我自己說明一下免得大家搞糊塗我所表示的地方...^^

2008年8月27日 星期三

NVRAM

1. CMOS 不是固定128BYTE,通常都會70h&71h port 這一對IO PORT可以存取你說的128BYTES,72h & 73h 這對PORT可以存取額外的128 bytes 若你的南橋有支援的話74h & 75h 還有128BYTES可用
2. NVRAM 就是非揮發性記憶體,也就是可以不依靠電源就可以永久記住 CMOS其實並不算因為只要主機板的小電池沒電他就無法保持資料 所以FLASHROM=NVRAM
3. DMI table 固定資料的部份會寫在FLASHROM,這是你刷新BIOS就寫進去了但是有些動態資料就是POST的過程中算出來的,而不管是動態或靜態資料都會在POST的過程中,存到F000:0000 的區段內
4. 有任何變更FLASHROM的時候通常為BIOS刷新
5. NVRAM可以放一大堆東西,例如PASSWORD或是存放一些s3要回存的資料

1. BIOS是Flash ROM也可以稱之為NVRAM,因為他有存放一些資料,例如Boot code/DMITable/ESCD/PASSWORD....等

2. 在南橋晶片組裡面有 128/256 Byte/...等的 "SRAM ",就靠那顆2.8V的鋰電池,讓它變成NVRAM(可能稱不上是NVRAM).

3.除了上述的地方之外還是有可能有其他的方有NVRAM...所以BIOS的Flash part除了放程式碼/OPROM/...開機會用到的東西之外,還多放了一些像是DMI tabl/ESCD/PASSWRD..等的東西,這樣子說法對吧?如果上述說法是正確的,也就是說一開始的時候BIOS是放在EEPROM,CMOS與RTC放在一個晶片裡面,後來因為技術進步,所以使用NVRAM取代EEPROM,因為可以存放一些資料速度也比較快,而CMOS則被南僑晶片整合進去...以上是我看到資料與大大說明後的心得,如有誤請指正!

2008年8月26日 星期二

暫存




ACPI SPEC中 EC INTERFACE兩種




SHARED PRIVATE

COMPAL用


SHAREDSMI - SMI」SCI共用一組COMMAND跟DATA的ADDRESS


Q:到底是BIOS還是OS











EC工作原理 INDEX IO PORT DATA IO PORT

主動向OS做請求使用INTERRUPT ACPI定義為SMI SCI兩種INT













PRIVATE INTERFACE

SMI接手的是BIOS SCI是OS



DOS下INT皆會產生SCI EC需要獨立的SCI跟OS做溝通



EC使用INT跟OS溝通

EC用獨立的SCI跟OS作溝通

WINDOWS下INT皆會產生SCI



EC 只能有256BYTE的RAM SPACE

但系統可以有muiltiple的EC存在

但EC的RAM SPACE最多只能到256BYTE

SMSBUS 整理

SMbus 最早是由 Intel 公司提出來的. 現在由 SBS 管理維護這一個規格. 此規格是用 PhilipsI2C 簡化而來. SMbus 是由兩條訊號所組成的一種匯流排. 是為了在系統上較慢速的裝置及電源管理裝置之間的溝通使用. 使系統可取得這些裝置的製造廠商,型號,一些控制資訊,錯誤訊息及狀態.
這兩條訊號為 SMBCLK 和 SMBDATA. 這和 I2C 上的 Clock(SCL) 和 Data(SDA) 是一樣的.











( 取材自 Philips I2C )
上圖為一 SMbus 的架構圖. 不同的裝置都接在同一 Bus 上. 在 SMbus 上只有一個 Master. 所有的命令均由此 Master 發出. 其他的裝置 (Slave) 只能接收 Master 發出的命令或回覆資料給 Master.









上圖為 SMbus 開始及結束 bus 的圖. 當 SCL 為 High 而 SDA 由 High 變 Low 時表示開始一個 SMbus 的命令. 當 SCL 為 High 而 SDA 由 Low 變 High 時表示結束一個 SMbus 的命令. 這二個狀況在 Smbus 裡是唯一的. 在一般傳送資料時均不可能發生. 而在一般傳送資料時則是在每一次 SCL 的上升緣時的 SDA 狀態來決定. 這些資料包含了仲裁,確認,送出資料給那一個裝置及送出的資料.或要取得那一個裝置的資料及由裝置送出的資料.





關於I2C Bus與SMBus,許多人很少去談論與瞭解兩者的細節差異,包括很多國外的簡報文件也經常將兩者混寫、交雜描述、交替運用。
確實,在一般運用下,I2C Bus與SMBus沒有太大的差別,從實體接線上看也幾乎無差異,甚至兩者直接相連多半也能相安無誤地正確互通並運作。不過若真要仔細探究,其實還是有諸多不同,如果電子設計工程師不能明辨兩者的真實差異,那麼在日後的開發設計的驗證除錯階段時必然會產生困擾,為此本文將從各層面來說明I2C Bus與SMBus的細微區別,期望能為各位帶來些許助益。 
附註:關於I2C Bus的基礎,可參考筆者之前的「I2C介面之線路實務」,網址為: http://www.digitimes.com.tw/n/article.asp?id=304799064272FED148256FDC00481D68
或參考Philips半導體網站的I2C官方規格: http://www.semiconductors.philips.com/acrobat/literature/9398/39340011.pdf

運用背景、版本演進之別 

首先從規格的制訂背景開始,I2C是在設計電視應用時所發創的介面,首版於1992年發表;而SMBus(System Management Bus)則是Intel與Duracell(金頂電池)共同制訂筆記型電腦所用的智慧型電池(Smart Battery)時所發創的介面,首版於1995年發表,不過SMBus文件中也提及,SMBus確實是參考自I2C,並以I2C為基礎所衍生成。 
I2C起源於電視設計,但之後朝通用路線發展,各種電子設計都有機會用到I2C;而SMBus則在之後為PC所制訂的先進組態與電源管理介面(Advanced Configuration & Power Interface;ACPI)規範中成為基礎的管理訊息傳遞介面、控制傳遞介面。 

雖然I2C與SMBus先後制訂時間不同,但都在2000年左右進入成熟化改版,I2C的過程改版以加速為主要訴求,而SMBus以更切合Smart Battery及ACPI的需求為多。 
I2C三次主要改版: 1992年 v1.0 1998年 v2.0 2000年 v2.1 
SMBus三次主要改版: 1995年 v1.0 1998年 v1.1 2000年 v2.0
 













▲圖說:MAXIM公司的MAX6641晶片,具有溫度監督及風扇控制功能(用PWM脈寬調變方式控制風扇轉速),圖中腳位7、8即是SMBus(圈處),其他裝置可透過SMBus與此晶片溝通,取得溫度及相關資訊,或進行命令操控。(圖/MAXIM-IC.com) 

電氣特性差異:邏輯位準定義、限流、相關限制 
I2C的Hi/Lo邏輯準位有兩種認定法:相對認定與絕對認定,相對認定是依據Vdd的電壓來決定,Hi為0.7 Vdd,Lo為0.3 Vdd,絕對認定則與TTL準位認定相同,直接指定Hi/Li電壓,Hi為3.0V,Lo為1.5V。相對的SMBus只有絕對認定,且準位與I2C有異,Hi為2.1V,Lo為0.8V,與I2C不全然吻合但也算部分交集。不過,SMBus後來也增訂一套更低電壓的準位認定,Hi為1.4V,Lo為0.6V,這是為了讓運用SMBus的裝置能更省成本而有的作法。 
瞭解電壓後再來是電流,由於SMBus一起頭就是運用在筆記型電腦內,所以低用電的表現優於I2C,只需100uA就能維持工作,I2C卻要到3mA,同樣的低用電特性也反應在漏電流(Leakage Current)的要求上,I2C最大的漏電流為10uA,SMBus為1uA,但是1uA似乎過度嚴苛,使運用SMBus的裝置在驗證測試時耗費過多的成本與心力,因此之後的SMBus 1.1版放寬了漏電流上限,最高可至5uA。 
再者是相關限制,I2C有線路電容的限制,SMBus卻沒有,但也有相類似的配套規範,即是準位下拉時的電流限制,當SMBus的開集極接腳導通其閘極而使線路接地時,流經接地的電流不得高於350uA,另外拉升電流(即相同的開集極接腳開路時)也一樣有規範,最小不低於100uA,最高也是不破350uA。 
既然對電流有限制,那麼也可容易地推斷對提升電阻的阻值之範圍要求,I2C在5V Vdd時當大於1.6k ohm,在3V Vdd時當大於1k ohm,類似的SMBus於5V Vdd時當大於14k ohm,3V Vdd時當大於8.5k ohm,不過這個定義並非牢不可破,就一般實務而言,在SMBus上也可用2.4k∼3.9k ohm範疇的阻值。 
附註:I2C的時脈線稱SCK或SCL,資料線稱SDA。SMBus的時脈線稱SMBCLK,資料線稱SMBDAT。 




▲圖說:I2C與SMBus在邏輯位準的電壓定義不盡相同,基本上I2C的定義較為寬裕、彈性,而SMBus則更專注在省電方面的要求。(圖/MAXIM-IC.com) 
時序差別與考驗 實體層面的空間要求完後,再來就是實體層面的時間,即是時序(Timing)方面的差別。 
先以運作頻率來說,I2C此方面相當寬裕,最低頻可至0Hz(直流狀態,等於時間暫停),高可至100kHz(Standard Mode)、400kHz(Fast Mode)、乃至3.4MHz(High Speed Mode),相對的SMBus就很拘限,最慢不慢於10kHz,最快不快於100kHz。很明顯的,I2C與SMBus的交集運作頻率即是10kHz∼100kHz間。 
用於筆記型電腦的電池管理或PC組態管理、用電管理的SMBus,很容易體會不需要更高運作頻率的理由,只要傳遞小資料量的監督訊息、控制指令本就不用過於高速,而朝向廣遍運用的I2C自然希望用更高的傳輸以因應各種可能的需求。然而大家可能會疑惑,為何SMBus有最低速的要求?何不放寬到與I2C相同的毫無最低速限呢? 
SMBus一定要維持10kHz以上的運作時脈,主要也是為了管理監控,另一個用意是只要在保持一定傳速運作的情況下加入參數,就可輕鬆獲知匯流排目前是否處於閒置(Idle)中,省去逐一偵測傳輸過程中的停斷(STOP)信號,或持續保有停斷偵測並輔以額外參數偵測,如此對匯流排閒置後的再取用會更有效快速。 
傳速要求之後還有資料持留時間(Data Hold Time)的要求,SMBus規定SMBCLK線路的準位下降後,SMBDAT上的資料必須持續保留300nS,但I2C卻沒有對此有相同的強制要求。類似的,SMBus對介面被重置(Reset)後的恢復時間(Timeout)也有要求,一般而言是35mS,I2C這方面亦無約束,可以任意延長時間。相同的SMBus也要求無論是在主控端(Master)或受控端(Slave),其時脈處於Lo準位時的最長持續時間不得超越限制,以免因為長時間處在Lo準位,而致收發兩端時序脫軌(失去同步,造成後續誤動作)。 
還有,I2C與SMBus在準位的上升時間、下降時間等也有不同的細部要求,此點必要時也必須進行確認,或在驗證過程中稍加留意。 

▲圖說:Smart Battery或ACPI的實現、監督、與操控,最底層都需要SMBus(圈處)作為後援,圖為簡易的多組式智慧型電池系統,圖中有Smart Battery A、B兩組電池。(圖/SBS-Forum.org) 
「已妥」與「未妥」機制的強制性差別 
不單是電氣、時序有別,更高層次的協定機制也有不同。在I2C中,主控端要與受控端通訊前,會在匯流排上廣播受控端的位址資訊,每個受控端都會接收到位址資訊,但只有與該位址資訊相切合的受控端會在位址資訊發佈完後發出「已妥」的回應(Acknowledge;ACK),讓主控端知道對應的受控端確實已經備妥,可以進行通訊。但是,I2C並沒有強制規定受控端非要作出回應不可,也可以默不作聲,即便默不作聲,主控端還是會接續工作,開始進行資料傳遞及下達讀/寫指令,如此的機制在一般運用中還是可行,但若是在一些即時(Real Time)性的應用上,任何的動作與機制都有一定的時限要求,這種可有可無式的回應法就會產生問題,可能會導致受控端無法接收資訊。 
相同的情形,在SMBus上是不允許受控端在接收位址資訊後卻不發出回應,每次都要回應,為何要強制回應?其實與SMBus的應用息息相關,SMBus上所連接的受控裝置有時是動態加入、動態移除的,例如換裝一顆新電池,或筆記型電腦接上船塢埠等,如果接入的裝置已經改變卻不回應,則主控端的程式所掌握的並非是整體系統的最新組態,就會造成誤動作。 
類似的情形也適用於ACPI,PC機內機外經常有一些裝置可動態增入、移除,如機內風扇、外接印表機等,這些也一樣該強制對主控端廣發的位址資訊作出完整回應。 
位址動作方面有異,資料傳輸方面也有異。在I2C方面,Slave雖然對Master所發出的位址作出回應,但在後續的資料傳遞中,可能因某些事務必須先行處理、因應而無法持續原有的傳輸,這時候Slave就要對Master發出「未妥」的回應(Not Acknowledge;NACK),向Master表示Slave正為他務忙碌中。 
而SMBus方面,與I2C相同的,會以NACK的回訊向Master表達Slave尚未收妥傳遞的資訊,但是SMBus的Slave會在後續的每個Byte傳輸中都發出NACK回訊,這樣設計的原因是因為SMBus沒有其他可向Master要求重發(Resend)的表示法。更直接說就是:NACK機制是SMBus標準中的強制必備,任何的訊息傳遞都很重要,不允許有漏失。 

▲圖說:I2C在完成一段位址或資料資訊的傳輸後,受接端可發出訊息收妥(ACK)、未妥(NACK)的回應,SMBus也具相同的機制,但由於應用之故有更強制的回應要求。(圖/Semiconductors.Philips.com) 
傳輸協定的子集、超集 
互動知會機制上有強制與否的差別,協定方面也是。SMBus的通訊協定與協定中所用的訊息格式,其實只是取自I2C規範中,對於資料傳輸格式定義中的子集合(Subset)而已。所以,如果將I2C與SMBus交混連接,則I2C裝置在存取SMBus裝置時,只能使用SMBus範疇的協定與格式,若使用I2C的標準存取方式反而無法正確存取。 
另外,I2C規範中有一種稱為「General Call」的廣呼方式,當發出「0000000」的位址資訊後,所有I2C上的Slave裝置統統要對此作出反應,此機制適合用在Master要對所有的Slave進行廣播性訊息更新與溝通上,是一種總體、批次的運作方式。 
SMBus一樣有General Call機制,但在此之外SMBus還多了一種特用的ALERT(警訊)機制,不過這必須於時脈線與資料線外再追加一條線(稱為:SMBSUS)才能實現,ALERT雖名為警訊但其實是中斷(Interrupt)的用意,Slave可以將SMBSUS線路的電位拉低(ALERT#,#表示低準位有效),這時就等於向Master發出一個中斷警訊,要求Master儘速為某一Slave提供傳輸服務。Master要回應這個服務要求,是透過I2C/SMBus的時脈線與資料線來通訊,但要如何知道此次的通訊只是Master對Slave的一般性通訊?還是特別針對Slave的中斷需求而有的服務回應? 
這主要是透過Master發出的位址資訊來區別,若為回應中斷的服務,位址資訊必然是「0001100」,當Slave接收到「0001100」的位址資訊,就知道這是Master特為中斷而提供的服務通訊。因此,韌體工程師須留心,規劃時必須讓所有的Slave都不能佔用「0001100」這個位址,以供ALERT機制運用(當然!若現在與未來都不會用上ALERT機制則可儘管佔用)。事實上各種進階的規範標準(如Smart Battery、ACCESS.bus、VESA DDC等)都在I2C的短定址中訂立了一些為自用而保留的位址,這在最初設計與定義時就該有所留意,以免因先行佔用而導致日後須改寫韌體的麻煩。 
補充提醒的是,SMBSUS一樣是開集極外加提升電阻的線路,所以有一個Slave將電位拉下後,其餘Slave偵測到電位被拉下,表示已有Slave正在與Master進行中斷需索與回應服務,須等待搶到中斷服務權的Slave確實被服務完畢,重新將SMBSUS釋放回高準位後,才能持續以「看誰能先將線路準位拉低?」的方式來爭取中斷服務。 
最後,若有進一步興趣的讀者,筆者建議可參考兩份資料: 
1.SMBus 2.0版規範(SMBus規格網站) http://www.smbus.org/specs/smbus20.pdf
2.比較I2C與SMBus(MAXIM公司網站) 
 
▲圖說:MAXIM公司的MAX6641晶片之典型應用方式,圖左為溫度感測電路,圖右上為風扇轉速控制電路,圖右圈處即是SMBus介面電路。(圖/MAXIM-IC.com)
轉自

2008年8月12日 星期二

第一篇

公司不能上BBS
這裡主要是希望能夠讓我紀錄一些程式語言和工作上的心得