【我所認知的BIOS】->反彙編BIOS之Bootblock(2)
--CPU micro code update
By Lightseed
5/12/2010
1、CPU micro code的背景
先做個鋪墊爲什麼要在BIOS剛剛開始跑的時候就來講CPU的micro code。
以下引用自網絡:
;-------------------------------------
在十多年前的Pentium時代, INTEL曾經發布過有缺陷的CPU, 因爲浮點運算表邊界上有幾個數據錯誤, 導致在某些應用會出現錯誤, 這個錯誤概率很小, 出錯機率小於千萬分之一, 但在還是被捅出來了. INTEL爲此召回CPU.
從Pentium Pro起, INTEL決定給CPU留出一個補丁接口, 當CPU內部有缺陷的時候, 通過加載微碼(Microcode), 可以修復CPU的部分缺陷.
INTEL說, 他們只測試過的CPU都是加載了微碼的, 如果沒有加載微碼, INTEL不保證會出現什麼問題.
現在的CPU有一個CPUID, 通過執行CPUID指令, 可以知道當前CPU的版本和Stepping. 根據這個信息, 再給CPU打相應的補丁.
下圖就是CPUID=06D2, Rev.A2
何時給CPU打補丁?
在給CPU初始化的時候, 就需要把INTEL提供的微碼寫進CPU去, 因此, 加載CPU微碼就是系統BIOS的任務.
如果系統的CPU是可更換的, 那麼其微碼也需要更換. 因此, 在BIOD裏, 一般要包進若干個ID的CPU微碼, 以便工廠安排不同的SKU出貨. 如果BIOS發佈是在CPU發佈之前, 那麼BIOS裏很可能就沒有包進最新的微碼, 這個系統要使用新CPU的時候, CPU微碼是無法加載的.
另外, BIOS ROM容量有限, 一個微碼補丁最小有2K, 如果平臺兼容的CPU很多, 則微碼數量是十分巨大的, 臺式機某些主板可能兼容20多個CPU版本, 那麼微碼的體積很大, BIOS里根本包不下這麼多東西, 於是, 廠商不得不縮水, 去掉一些不常用的微碼. 這些不常用的微碼一般都是早期的CPU, 如DOTHAN早期的A STEPPING就很可能沒有對應的微碼包進你的本本中去.
不打補丁會有什麼問題?
INTEL說他們沒測過不打補丁的CPU, 也就不知道會出什麼問題. 呵呵, 這顯然, 他們不想說太多技術細節而已. 以俺的經驗, 如果不打補丁, 99.99%的時候, 用戶是感覺不到的, 除非問題特別突出. 只是俺遇到過幾個明顯的例子, 爲此出了幾身汗. 有幾個案例:
1, 某Prescott CPU, 在臺式機上發熱量特別大, 超出Design Point, 後來發現沒加載微碼, 加上微碼就正常了;
2, 某Pentium D CPU, 進WINDOWS XP會藍屏, 以安全模式進去後, 安裝一個SP2補丁, 就正常了. 後來查出, 也是微碼沒加載;
3, 某Pentium M架構CPU, 在使用CPU內部TSC時, 發現測出的CPU內部頻率高出實現的4倍, 如2GHz CPU測出卻有8GHz, 後查, 也是沒加載微碼造成的異常.
此類案例很多, 特別是Core架構CPU, 不但微碼必須加載, 而且要求儘早加載, 否則, 連BIOS都跑不完, 系統就掛了. 但是INTEL但至今沒有任何官方對每個CPU微碼版本進行描述的文件.
怎麼檢查CPU微碼是否加載?
加載微碼後, 在CPU的MSR(機器特定寄存器)裏可以讀出版本號. INTEL IA-32編程手冊上給出標準檢查方法:
MOV ECX, 008bh
XOR EAX, EAX
XOR EDX, EDX
WRMSR ;向MSR 8BH寫0, 清除MSR中的信息
MOV EAX, 0001
CPUID ;讀CPUID, 讓CPU查看微碼版本, 並把微碼版本送到MSR 8B中
MOV ECX, 008bh
RDMSR ;讀出當前CPU微碼版本
執行上面的代碼後, 如果EDX的值爲0, 則說明你的CPU微碼是沒有沒加載的, 你的CPU運行在有缺陷的狀態. 如果不爲0, 則顯示的是當前微碼版本號
以上代碼可以在DOS環境下, 用DEBUG32調試界面執行.
;----------------------------------------------------
2、來來來,動手反彙編了!!!
2.1 BIOS開始的代碼
如果您做了前兩篇的東西,那麼您會不難反彙編到這裏來,(F000_FFF0跳到F000:E05B,然後再一個near jmp到了F000:E1B0)也就是下面這段代碼。這段代碼也就是Award BIOS最開始的代碼部分了。
之前我們有研究過,當CPU 收到reset信號後它內部會做一系列的動作,而且當第一次從FFFF_FFF0H處取指令執行的時候,還會在對應的寄存器中保存一些相關的信息。在
http://blog.csdn.net/lightseed/archive/2009/10/27/4735101.aspx
中的圖1您可以看到DX裏面究竟存了什麼值。(其實就是CPU的type。)
在加註的時候,其實我個人更喜歡用英語註釋哈。但是在之前有人說想要我用中文註釋,想了又想,其實真的沒有必要。如果連這點英語都看不懂,那麼我想看datasheet也估計很困難。所以最後還是確定用English來註釋。註解有誤的話,還往各位諒解並指正,我會盡快修正。
_F000:E1B0
_F000:E1B0 loc_FE1B0: ; CODE XREF: _F000:E05BJ
_F000:E1B0 mov gs, dx ; Save CPU type to GS
_F000:E1B2 cli ; Close interrupt
_F000:E1B3 cld
_F000:E1B4 mov ax, cs
_F000:E1B6 mov ss, ax ; CS and SS use the same segment, that can use ROM_CALL
_F000:E1B8 mov sp, 0E1BEh ; First Use ROM_CALL
_F000:E1BB jmp BT_CPU_Init ; Save esp (Return Address)
這段代碼也就是BIOS開始兩個JMP後的代碼,應該說比較好理解。在E1B8這裏BIOS第一次用了上一節中提到的ROM_CALL這個宏。也遇到了第一個函數。BT_CPU_Init其實是在CPU開始跑code的最開始就針對特殊的CPU做patch。那麼我就來詳細和大家說說這個函數。
2.2 CPU micro code update
關於這個函數,原本我是有反彙編的源代碼的,代碼不復存在,但是原理卻能夠說清楚。(下面的說明,基本上就是這個函數所作的所有動作。反彙編出來的地址,其實我們可以暫且跳過,看原理就好。)
上面micro code update的全部函數。一些簡單的動作,我也在code中註釋了。我着重拎一些不太好懂的說說。
①_F000:0036在這行中,主要是先對比一下CPU是否爲Cedar Mill B1,如果不是那麼這個函數就不用跑了。Patch當然也就不用做了。(如果你照着我之前做的動作,那麼這個行數應該是能對的上的,但是就算對不上也沒有關係。只看我的算法講解就可以了。)至於code的細節嘛,那你就自己慢慢體會吧。
②_F000:0049在這行中,有一個對比的指令,其實很明顯可以猜到這就是CPU micro code的標誌。
_F000:0049 cmp dword ptr [esi], '1SB*' ; Compare if it is CPU micro code?
讓我們用ultraedit打開之前說的那個BIOS的BIN來看看,如圖1
圖1
從圖1中可以看出,“*BS1”是處於bin文件中的6000段的,也就是BIOS在實際跑中的E000段。所以和代碼中的段剛好符合。如果把整個E000段都找完了還是找不到的話,那麼就說明沒有這個部分的micro code,直接就return了。
③_F000:005B這行是爲了得到CPU micro code的base address。這個地址是CBROM填入了,我們可以不用care它。只需要知道從這裏可以得到CPU micro code就好了。見圖2 bin文件中實際的micro code位置FFFE_57E0H。
圖2
緊接着,我們可以從對應的地址處看到實際的CPU micro code。如圖3
圖3
而牽涉到CPU的micro code那麼就不得不提它的結構。從micro code的第0個字節算起,會有如下一個表。
CPU micro code開始幾個flag
HeaderVersion |
;offset 00h |
UpdateRevision |
;offset 04h |
UpdateData |
;offset 08h |
ProcessorVersion |
;offset 0ch |
Checksum |
;offset 10h |
LoaderRevision |
;offset 14h |
Platform_ID |
;offset 18h |
DataSize |
;offset 1ch |
TotalSize |
;offset 20h |
Reserved |
;offset 24h |
Update_Data |
;offset 30h |
有了這幾個說明,我想從Update_Micro_code_Start:開始的那些EBX+XX之類的比較就應該沒有什麼問題了哦。看看圖3中,我還給大家標了一些標誌出來。您可以追追看。
④_F000:0080和_F000:0086兩行操作的是MSR寄存器17H。它的說明在3 B裏面有說,以下是截圖。
_F000:0080 mov ecx, 17h
_F000:0086 rdmsr
圖4
⑤_F000:00A2這行主要是初始化MSR的79H。關於它的說明也是在3B裏面見圖5所示。
_F000:009B mov eax, ebx
_F000:009E add eax, 30h ; '0'
_F000:00A2 mov ecx, 79h ; 'y' ; MSR register to update micro code
_F000:00A8 xor edx, edx
_F000:00AB wrmsr
圖5
經過那麼多的判斷後,最終只確定了一件事,那就是目前的CPU確實是應該update以下它的micro code了,於是在_F000:00AB wrmsr命令執行後,CPU開始升級它的micro code。
至此關於CPU在Bootblock的時候升級micro code的動作就做完了。其實在後面POST的時候,也會有這樣的動作,但是原理都是一樣的。(稍微有些些差別)我們理解原理就好啦。呵呵。。。希望大家喜歡。