DOS ,一個很經典的作業系統,但是我對他的了解還是不夠清楚。
以前如果要在DOS下做Reboot ,在我寫程式的經驗中不外乎就是下面幾種方式:
1. Jmp FFFF:0
2.KBC Cmd FEh
3.CF9h
4.Port 92h
沒想到,在我接觸麥金塔作業系統測試工作的時間中竟然讓我荒廢了那麼多的該學習的知識沒去學,果然前一份工作不應該搞太久的 >.< ! 亡羊補牢,所以我在處理ㄧ些BIOS Bug的過程中所找到的相關文獻中發現,原來Jmp FFFF:0 這個動作不是那麼的安全,有可能會造成問題,這個問題還真的很特別。 因為在一些含有記憶體管理的作業系統中,原來在做這個動作前還需要廣播給相關的程式去做一些動作,例如通知EMM386去清除一些相關設定..等,等他清除完畢後在做Jmp FFFF:0 才不會造成一些不可預期的事情,而這個方式是透過INT 15h/AH=4F來做的。 話雖如此,一般應用程式在Jmp FFFF:0 之後,通常都會Jmp F000:E05B的位址,而這個位址內會放著"相容性的BIOS程式碼位址",簡言之這邊就是BIOS負責Reboot動作的程式碼。 而這段程式碼內,每家的作法都不太ㄧ樣,但最後都會做ㄧ個Warm Boot動作,即CPU Reset。 當CPU Reset後,會從4G頂端開始執行,因此在做Warm boot前,BIOS一般都會把A20打開,然後才做Warm Boot。 而4G頂端的BIOS程式碼,一般都會去判斷ㄧ個條件(目前),而這個條件就是判斷系統是Warm boot/Cold boot ,如果是Warm boot,則BIOS會在做ㄧ次Cold boot,因為做了Cold boot,所以就算沒有透過INT15h廣播,你重新進入到DOS下時也不會有問題。 所以看起來整個DOS下的流程如下所示:
1. DOS下的AP要做Reboot
2.DOS下的AP透過INT15去廣播,告知相關軟體要重新開機了
3.相關軟體因為有Hook INT15h,所以得知要關機了,所以各自負責相關需要關閉或是清理的部份。
4.控制權回到DOS下的AP,此AP做了一個Jum FFFF:0的動作
5.控制權交給BIOS,BIOS檢查相關Flag,BIOS廣播INT15h,BIOS設定BDA Flag=1234h,BIOS設定System Flag,BIOS開啟A20,BIOS做ㄧ個Warm boot(KBC Feh或是CF9)
6.系統重新開機,CPU從4G那邊的Code開始跑
7.BIOS檢查System Flag , 如果是Warm boot -->在做ㄧ次Cold boot,此次 Cold boot 會清除所有的Flag,所以下次從4G那邊開始Run的時候就不會檢查到Warm boot -->正常開機程序
[註1] 開啟A20有三種方式
1.KBC Cmd (D1h - 設定 , D0h讀取狀態)
如果你有I公司的Kx工具 ,你可以使用下列參數去做:
Enable: Kx Cd1 W02 (寫入02或是FEh都可以,因為A20 在bit 1)
Disable: Kx Cd1 W00 (寫入00或是FDh都可以,因為A20 在bit 1)
Read Status: Kx Cd0 I1 (I1,代表讀取1次)
2.Port 92h
需查看EDS Spec,一般都在Bit 1=A20
Enable : Out 92h,02h (或是FEh)
Disable: Out 92h,00h(或是FDh)
3.Call INT 15h/AH=24 (Fast A20)
AL=01 - Enable A20
AL=00 - Disable A20
其實呼叫的中斷內所做的事情就是方式2的方式
[註2] A20開關程式的撰寫
ㄧ般我們會先去呼叫INT15h來開啟,如果失敗則試試看Port 92h方式,如果在失敗才又使用KBC Cmd方式,你可能會問說INT15h不是跟Port 92hㄧ樣嗎,對他們最後都是ㄧ樣方式,但是INT15h是透過BIOS提供的中斷服務程式介面,簡單說就是BIOS可能沒寫INT 15h Services,所以你只好手動去開啟。
[註3]A20是否開啟成功
檢查方式是透過檢查記憶體內容是否ㄧ樣,因為如果A20沒有開啟成功,則記憶體會迴繞,因此你去讀取0000:0000 所看到的資料會跟FFFF:10所看到的一樣,因此我們去比對這兩個記憶體的內容:
相同 : A20開啟失敗
不相同: A20開啟成功,也就是可以存取1M以上的內容
Debug.com方式:
-D 0000:0000
-D FFFF:0010
[註4] INT 15h/AH=4F廣播
1.呼叫INT15h廣播前,須手動設定BDA 40:17內的旗標,bit 3:2=11,也就是填0Ch (Ctrl與Alt Flag=1)
2.呼叫INT15h時,須把DEL key的Scancode放在AL,即AL=53h
其實就是模擬Ctrl+Alt+Del動作,很類似Windows底下的API去通知Driver關閉他們負責的設備,其實DOS下也有,酷吧!
Win98 - 本身沒有記憶體管理,除非掛Himem.sys 掛的時候他會去呼叫INT15h/E820h來得到記憶體容量,因此沒掛的時候你在DOS下A20應該是預設被關閉的,如果有被打開應該就是BIOS開的。
WinME-本身就有記憶體管理,自己會開啟A20,開啟的時候不ㄧ定是透過KBC/Port 92h ,我還沒找到判斷的地方,因此目前我手上的機器中所看到的現象是有可能OS會透過Port 92h來開,也有可能會透過KBC來開,因此BIOS端應該有地方提供資訊,不過我還沒找到~~~
[註5] BIOS真的要開啟A20後ㄧ定要做Warm Boot嗎?
上述的動作是:
Jmp F000:E05b -->BIOS Enable A20-->BIOS檢查/設定Flag -->BIOS Warm boot --> CPU Frist Instruction --> BIOS check Cold/Warm boot --> If warm boot , do cold boot.
我懷疑某些BIOS廠商的動作會變成是:
Jmp F000:E05b -->BIOS Enable A20 --> BIOS檢查/設定Flag -->BIOS check System Flag --> If System flag=warm boot --> do cold boot.
兩者的差別在於有沒有回到4G 頂端。
[註6] Ctrl+Alt+Del
這個對我來說還是個謎,因為DOS會去Hook INT09h,所以搞不清楚到底是誰去判斷Ctrl+Alt+Del,有家BIOS廠商的程式碼中看起來DOS並不會去處理他們,所以會回到BIOS的INT09h中斷去處理,因此我可以在這邊去做ㄧ些事情,但是又有的BIOS廠商在Win98時會回到INT09h,但是在WinME DOS環境下時我又攔截不到OS會把控制權交回去BIOS INT09h,所以只能說 OEM/ODM端的BIOS能拿到的資料還是有限,有時候想追問題也是心有餘而力不足啦~~~
P.S 有時間在去寫一個Hook INT09h來自己處理Ctrl+Alt+Del好了 >.<
以上純屬個人實驗筆記,未必正確!請大家不吝指正!
7 則留言:
wow ! 原來一個 DOS reset 有這些知識...
之前我們在DOS測 reboot都是直接對 SB register下 command. SB內有一個register;填特定值就會 reset system. 當然,depends on chipset...
reboot
-HW reset 系統才不會容易亂hang
-jmp f000_fff0 很容易hang
呵呵! 寫這篇文章就是探討Warm boot可能造成的影響,以及過去的人是如何解決!
目前bios都喜歡Cold boot(Intel是透過CF9h),把問題堆的一乾二淨! 只是沒想到那麼經典的dos中還有那麼多我不知道的歷史~~~ ^^
順帶一提: Intel目前還搞了一些專有名詞! Global reset / Platform reset 只能說光是個reset動作,真的要寫文章的話也可以寫上好幾篇了!
以前可能就只會覺得Reset有那麼複雜嗎? 但接觸bios後,如果你問我Reset是啥? 我可能會反問你,你是要問HW觀點還是SW觀點...因為觀點跟做法大不同啦!
-jmp f000_fff0 很容易hang
原因如果從H/W 觀點就容易理解.因為power-on sequence不對,對南北橋,CPU,memory...沒有符合timing
從H/W 觀點來說.在 BIOS 或 DOS 跑的時候power-on sequence早就完成了,怎麼可能有Timing的問題呢? 倒是北橋或其他 device沒有reset,原來留下的設定狀態可能造成問題.
只要不是Hardware Reset都是在Power Good之後, 但這時還是有可能發生timing 的問題的 especially for PLL...
你好, 想向你請教.
原來有一個小工具, 當年設計是 486 的年代, 俺沒有源碼. 今日的Pentium5 PC, 不管用 DOS 3.3 還是DOS 6.2, 只要啟動這個程序, 就會出現錯誤 - DIVIDE OVERFLOW, 然後直接跳回去 DOS.
所以想想你請教 80486 和 80586 在 DIV 的處理上有什麼分別, 除了 0xF7 以外, 還有其他 OPCODE 是跟 DIV 有關的嗎. 謝謝
張貼留言