2009年3月31日 星期二

●DJGPP和保護模式

本文轉貼於http://hengch.blog.163.com/
DOS不能工作在保護模式
讓CPU工作在保護模式很容易,但CPU工作在保護模式時你無法調用DOS和BIOS服務,為什麼呢?因為DOS和BIOS的代碼是按照在真實模式下運行的方式寫的,不符合保護模式程式的規範,比如,在真實模式下,DOS下的程式碼可以把任意數值放到段寄存器(CS、DS、ES、SS)中,只要不超過64K就可以,但在保護模式下,段寄存器只能放一個已經存在的selector的值,任何其它的值都將引起一個“General Protection Fault”錯誤異常。
所以,如果程式讓CPU進入保護模式,並且調用DOS服務,比如列印一行資訊,將馬上使系統崩潰,如果你不明白這一點,恐怕連一個最簡單的“Hello World”程式都寫不出來。
更糟糕的是,雖然應用程式不能調用DOS和BIOS服務,但DOS和BIOS卻必須要運行,比如時鐘晶片產生的硬體中斷,每秒18.2次的時鐘中斷等,時鐘中斷是BIOS的一部分,工作在真實模式。
所以,哪怕程式不調用任何真實模式代碼,一些非同步的系統事件仍然會發生,機器仍然會很快崩潰,所以,要在DOS下進入保護模式,必須首先解決DOS/BIOS和保護模式之間的這種衝突。
DOS Extender允許DOS和保護模式共存
如果你不想寫一個保護模式下的作業系統來完全取代DOS和BIOS,解決衝突的辦法是在你的應用程式和DOS/BIOS之間加入一個軟體層,這個軟體層可以視情況讓CPU在真實模式和保護模式之間切換,這個軟體層叫做DOS Extender。
在DOS Extender下,當保護模式下的程式要調用真實模式下的服務時,Extender給這個調用設置一個陷阱,把CPU切換到真實模式,再重新發出 這個調用,等待其完成調用,然戶再切換回保護模式,並返回到那個調用真實模式代碼的應用程式中,像時鐘中斷、鍵盤這樣的硬體中斷,也會被Extender設 置一個陷阱,並產生保護模式到真實模式的切換和返回。
你可能會想,這種方式會使應用程式運行變的很慢,然而在實際應用中,大多數程式並不會頻繁地調用OS的服務,即使調用很多,由於大多數的OS服務都是存取外部設備,比如硬碟,而這些設備的速度比起CPU而言是非常慢的,所以很少有人注意到模式切換上的系統開銷。
DJGPP v1.X和go32 Extender
在DJGPP v1.x中使用的go32程式,就是這樣一個DOS Extender,每個程式啟動時都會自動地裝載go32,go32除了完成DOS Extender的任務外,還接管下面一些與DJGPP相關的任務。
• 裝載應用程式,並為運行做好準備
由於DJGPP的執行檔使用COFF格式,DOS看不懂這種格式,go32負責讀取 COFF頭並初始化代碼、資料和其它檔頭中的分段
• 提供UNIX形式的命令列擴展
• 保護模式下浮點運算模擬
• 圖形支援
go32中有一些特殊的代碼,使其能夠適應各種各樣的進入保護模式的方法並管理擴展記憶體,所以他可以工作在任何DOS配置下,但這也使其產生一些不能忽視的缺陷,就是Extender必須被裝入常設記憶體,並且每個程式實例大約要佔用130KB的記憶體,大多數情況下DOS啟動以後都會有500-600K剩餘的常設記憶體,這意味著一個DJGPP程式只能有3-4級的嵌套。這是一個很嚴重的限制,DJGPP v2.x已經解決了這個問題。
DJGPP v2.X與DPMI服務
DJGPP v2.x放棄了Extender,取而代之的是需要一個已經運行的DPMI服務,DPMI:DOS Protected-Mode Interface的縮寫,是一個特殊的API,它允許保護模式的應用程式在DOS的上層運行,他定義了 一些函數,使保護模式下的程式(稱為DPMI客戶)可以做一些諸如:進入保護模式、分配記憶體和段描述符、調用真實模式的服務、連接中斷等等,許多使用 Intel CPU的作業系統都有DPMI服務,包括windows的所有版本,OS/2,以及LINUX DOS模擬器都是著名的例子,還有一些持有專 利的DOS下的DPMI伺服器,通常和DOS的記憶體管理器捆綁在一起,比如QEMM和386MAX,FreeDOS 也包含有一個DPMI伺服器作為缺省設置的一部分,對於那些沒有DPMI伺服器的系統,DJGPP v2.x提供一個免費的DPMI伺服器,叫做 CWSDPMI,CWSDPMI很多地方使用了go32的代碼,DJGPP啟動代碼檢查DPMI服務,如果沒有,將自動搜索並載入 cwsdpmi.exe----CWSDPMI伺服器。
DPMI伺服器(又稱為DPMI HOST)可以解決運行在DOS上層的保護模式程式的大部分問題,餘下的那些在1.x版本中由go32完成的函數,在v2.x中被DJGPP的啟動代碼接管並放在低級函式程式庫中,下面簡短地說一下DJGPP啟動代碼的兩個特點。
DJGPP v2.x的啟動代碼
DJGPP v2.x啟動代碼包括兩部分:短小的裝載程式和庫啟動模組,前者是由組合語言寫成,是一段有特殊作用的組合語言程式,叫做djasm,它是一段 16-bit的DOS可執行程式,這個短小的裝載程式會被連結到每一個DJGPP程式中,是唯一一部分可以被DOS識別的部分,其餘部分----COFF 可執行文檔----在DOS看來只是一些奇怪的資料。
第二部分是一個庫模組,它包括許多模組,有些用C寫成,有些用彙編寫成,當裝載程式把程式調入並初始化完成後,這裡就是COFF格式程式的入口點。
裝載程式完成以下工作:
• 為傳輸緩衝區申請記憶體
這個緩衝區用於在DOS服務和程式之間傳送資料。
• 檢查是否DPMI服務已經運行
以下兩種情況說明DPMI已經啟動
(1)有一個常駐記憶體的DPMI伺服器,比如windows中內置的DPMI伺服器
(2)當前程式是一個嵌套的DJGPP程式,他的父程式已經啟動了CWSDPMI
如果DPMI服務還沒有啟動,則裝入CWSDPMI。首先在目前的目錄下搜索cwsdpmi.Exe, 然後到環境變數PATH指定的目錄下去查找。
• 把COFF可執行部分的檔頭裝入記憶體
需要知道要為DJGPP程式申請多少記憶體
• 調用DPMI host提供的入口點,把CPU切換到保護模式
注意:裝入程式的其餘部分運行在保護模式下
• 為程式碼和資料申請記憶體空間
通過DPMI的功能調用可以為代碼和資料申請段描述符和記憶體空間,並設置基底位址、 界限和許可權。
• 把COFF格式的可執行部分調入記憶體
通過DPMI服務調用DOS(主要指檔操作)服務,把代碼、資料和BSS節讀入上 面申請的記憶體中,DPMI服務允許你從保護模式下調用真實模式下的服務。
• 跳轉到COFF鏡像的入口點執行
這個入口點在上面提到過的庫啟動模組中。
下面是庫啟動代碼部分完成的工作
• 生成一個不受約束的空頁
這將產生一個NULL Pointer dereference的錯誤,並將觸發一個異常處理,程式 會收到SIGSEGV信號,但這個功能並非基本DPMI 0.9規範中的一部分,所以 windows和其它許多有專利的DPMI伺服器並不支持這個功能,但CWSDPMI支援這 一功能。
• 改變申請記憶體的資料段的大小
這個聽起來簡單,但由於DPMI記憶體調用的特殊性,實際上是非常複雜的,例如: 它需要把一段真實模式下的16-bit代碼調入常設記憶體的緩衝區並運行。
• 設置程式的堆疊
DJGPP程式堆疊的缺省大小是512KB,但應用程式或改變設置都可以改變堆疊大小
• 為存取常設記憶體申請selector
與DOS/BIOS函數之間傳遞資料,或者像視頻界面上的顯示緩衝區那種使用記憶體 映射的設備,許多DOS程式需要存取常設記憶體,但由於在缺省情況下,常設記憶體並 沒有映射在程式的資料段中,為了存取常設記憶體,使用了一個特殊的selector---- _dos_ds。
• 初始化信號管理
需要連結一些硬體中斷,例如:按下CTRL-時產生的SIGINT信號。還有時鐘中 斷產生的SIGPROF信號等。
• 拷貝程式的環境變數到environ[]陣列中
• 讀出定義了DJGPP附加環境變數的檔
• 獲得並解釋命令列參數
• 如果需要,設置x87 FPU並載入浮點運算模擬器
• 調用靜態構造函數
• 調用用用程式的主函數

博客心情

博客大陸人稱呼部落格為博客,不知為何寫博客會上癮,可能是我這ㄍ宅男實在太無趣ㄌ,所以才透過文字抒發心情,然而每次寫完一篇就會覺得心情好過些,就好像每天早上ㄉ那杯咖啡,我目前的工作整天就是在跟記憶體測試軟體打交道,雖說不上心得滿滿,但從剛開始的牙牙學語,到現在一年半載,至少該懂的應該也有七八成,Debug也成為我的嗜好之一,一開始抱持著以前寫ap都搞不懂(應該說領域不同,所以沒機會接觸更深入)的,這回有機會一定要以頃城之力來回報自己內心的不踏實。雖然我很想跳出這個dram產業去接觸更多‧‧‧,但其實不那麼迫切,因為我覺的自己還有成長的空間,學習x86這一塊,讓我感觸良多,畢竟這是整個IT產業的最大宗,然而我相信搞好這一塊,要跳到其它嵌入式系統應該熟悉度會加快。雖說技術一日千里,但只要你夠耐心及細心,我想你也可以日進萬里(內心ㄉ成長)。

●中斷、保護模式、分頁‧‧真是夠ㄌ___PART2


以下ㄉCODE是截取"自己動手寫作業系統",其中還有其他範例您就自己捨得花錢買一本吧。買這種書不會讓你看ㄌ一兩年後會過時ㄉ感覺 ,所以買ㄅ。 用其他顏色是我ㄉ註解。
; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.com
; ==========================================
%include "pm.inc" ; 常量, 宏, 以及一些说明
org 0100h
jmp LABEL_BEGIN

[SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32 ; 非一致代码段, 32
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束
;以上3個描述子請參閱pm.inc你才能真的懂。第一ㄍ為空描述子,一定要ㄉ,什麼?誰規定ㄉ,當然是intel
GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限;由這ㄍ界定的定義dw就知道gdt表最多可以容納8192個描述子:dw=2ㄉ16次方=65536/8=8192
dd 0 ; GDT基地址

; GDT 选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]

[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs;程式ㄉ啟始點
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h;因為是com檔所以是100h

; 初始化 32 位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah

; 为加载 GDTR 作准备
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
; 加载 GDTR
lgdt [GdtPtr]
;lgdt作ㄌ什麼事ㄚ(就是將GdtPtr這個標籤所指ㄉ6ㄍbytesㄉ資料載入到gdtr暫存器;待進入保護模式,段式保護機制就生效ㄌ,從此過這和以前一樣ㄉ日子,只要和ds es fs gs打交道就可以ㄌ。
; 关中断
cli
; 打开地址线A20;a20位址線問題我已經有另闢文書說明請自行參閱
in al, 92h or al, 00000010b
out 92h, al
; 准备切换到保护模式;短短3行你就進入保護模式。簡單ㄅ
mov eax, cr0 or eax, 1 mov cr0, eax
; 真正进入保护模式
jmp dword SelectorCode32:0
; 执行以上這一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0處;請仔細品嘗這ㄍ註解1000遍,直到你真的懂為止。
; END of [SECTION .s16]
[SECTION .s32]
; 32 位代码段. 由实模式跳入.
[BITS 32] LABEL_SEG_CODE32: mov ax, SelectorVideo mov gs, ax
; 视频段选择子(目的)
mov edi, (80 * 10 + 0) * 2
; 屏幕第 10 行, 第 0 列。 mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'P' mov [gs:edi], ax
; 到此停止
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32 ;
END of [SECTION .s32]
;==========================================
; pm.inc 有些描述必須參考上圖
;==========================================
(1) P: 存在(Present)位。 ; P=1 表示描述符对地址转换是有效的,或者说该描述符所描述的段存在,即在内存中; ; P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常。 ; ; (2) DPL: 表示描述符特权级(Descriptor Privilege level),共2位。它规定了所描述段的特权级,用于特权检查,以决定对该段能否访问。 ; ; (3) S: 说明描述符的类型。 ; 对于存储段描述符而言,S=1,以区别与系统段描述符和门描述符(S=0)。 ; ; (4) TYPE: 说明存储段描述符所描述的存储段的具体属性。 ; ; ; 数据段类型 类型值 说明 ;
; 0 只读 ; 1 只读、已访问 ; 2 读/写 ; 3 读/写、已访问 ; 4 只读、向下扩展 ; 5 只读、向下扩展、已访问 ; 6 读/写、向下扩展 ; 7 读/写、向下扩展、已访问 ; ; ; 类型值 说明 ; 代码段类型 ; 8 只执行 ; 9 只执行、已访问 ; A 执行/读 ; B 执行/读、已访问 ; C 只执行、一致码段 ; D 只执行、一致码段、已访问 ; E 执行/读、一致码段 ; F 执行/读、一致码段、已访问 ; ; ; 系统段类型 类型编码 说明 ;; 0 <未定义>
; 1 可用286TSS
; 2 LDT
; 3 忙的286TSS
; 4 286调用门
; 5 任务门
; 6 286中断门
; 7 286陷阱门
; 8 未定义
; 9 可用386TSS
; A <未定义>
; B 忙的386TSS
; C 386调用门
; D <未定义>
; E 386中断门
; F 386陷阱门
;
; (5) G: 段界限粒度(Granularity)位。
; G=0 表示界限粒度为字节;
; G=1 表示界限粒度为4K 字节。
; 注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。
;
; (6) D: D位是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同。
; ⑴ 在描述可执行段的描述符中,D位决定了指令使用的地址及操作数所默认的大小。
; ① D=1表示默认情况下指令使用32位地址及32位或8位操作数,这样的代码段也称为32位代码段;
; ② D=0 表示默认情况下,使用16位地址及16位或8位操作数,这样的代码段也称为16位代码段,它与80286兼容。可以使用地址大小前缀和操作数大小前缀分别改变默认的地址或操作数的大小。
; ⑵ 在向下扩展数据段的描述符中,D位决定段的上部边界。
; ① D=1表示段的上部界限为4G;
; ② D=0表示段的上部界限为64K,这是为了与80286兼容。
; ⑶ 在描述由SS寄存器寻址的段描述符中,D位决定隐式的堆栈访问指令(如PUSH和POP指令)使用何种堆栈指针寄存器。
; ① D=1表示使用32位堆栈指针寄存器ESP;
; ② D=0表示使用16位堆栈指针寄存器SP,这与80286兼容。
;
; (7) AVL: 软件可利用位。80386对该位的使用未左规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。
;----------------------------------------------------------------------------
; 描述符类型值说明
; 其中:
; DA_ : Descriptor Attribute
; D : 数据段
; C : 代码段
; S : 系统段
; R : 只读
; RW : 读写
; A : 已访问
; 其它 : 可按照字面意思理解
;----------------------------------------------------------------------------
DA_32 EQU 4000h ; 32 位段

DA_DPL0 EQU 00h ; DPL = 0
DA_DPL1 EQU 20h ; DPL = 1
DA_DPL2 EQU 40h ; DPL = 2
DA_DPL3 EQU 60h ; DPL = 3
;----------------------------------------------------------------------------
; 存储段描述符类型值说明
;----------------------------------------------------------------------------
DA_DR EQU 90h ; 存在的只读数据段类型值
DA_DRW EQU 92h ; 存在的可读写数据段属性值
DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值
DA_C EQU 98h ; 存在的只执行代码段属性值
DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值
DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值
DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值
;----------------------------------------------------------------------------
; 系统段描述符类型值说明
;----------------------------------------------------------------------------
DA_LDT EQU 82h ; 局部描述符表段类型值
DA_TaskGate EQU 85h ; 任务门类型值
DA_386TSS EQU 89h ; 可用 386 任务状态段类型值
DA_386CGate EQU 8Ch ; 386 调用门类型值
DA_386IGate EQU 8Eh ; 386 中断门类型值
DA_386TGate EQU 8Fh ; 386 陷阱门类型值

; RPL(Requested Privilege Level): 请求特权级,用于特权检查。
;
; TI(Table Indicator): 引用描述符表指示位
; TI=0 指示从全局描述符表GDT中读取描述符;
; TI=1 指示从局部描述符表LDT中读取描述符。
;----------------------------------------------------------------------------
; 选择子类型值说明
; 其中:
; SA_ : Selector Attribute

SA_RPL0 EQU 0 ; ┓
SA_RPL1 EQU 1 ; ┣ RPL
SA_RPL2 EQU 2 ; ┃
SA_RPL3 EQU 3 ; ┛

SA_TIG EQU 0 ; ┓TI
SA_TIL EQU 4 ; ┛

; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low 20 bits available)
; Attr: dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限 1 (2 字节)
dw %1 & 0FFFFh ; 段基址 1 (2 字节)
db (%1 >> 16) & 0FFh ; 段基址 2 (1 字节)
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性 1 + 段界限 2 + 属性 2 (2 字节)
db (%1 >> 24) & 0FFh ; 段基址 3 (1 字节)
%endmacro ; 共 8 字节

; usage: Gate Selector, Offset, DCount, Attr
; Selector: dw
; Offset: dd
; DCount: db
; Attr: db
%macro Gate 4
dw (%2 & 0FFFFh) ; 偏移 1 (2 字节)
dw %1 ; 选择子 (2 字节)
dw (%3 & 1Fh) | ((%4 <<>> 16) & 0FFFFh) ; 偏移 2 (2 字节)
%endmacro ; 共 8 字节
/*****************************************************/
以下我們來看memtest86+如何進入保護模式:請自行參閱head.S,用/****/隔開表示代碼為不連續性。
#define RSTART startup_32
/* Fixup the gdt_descr */
leal gdt@GOTOFF(%ebx), %eax
movl %eax, 2 + gdt_descr@GOTOFF(%ebx)
/*****************************************************/
/* Load the global descriptor table */
addr32 lgdt gdt_descr - RSTART //
addr32這ㄍ標紀是什麼意思我還沒查到,但可以確定ㄉ一點是現在早已在保護模式,因為setup.S時就切進去ㄌ。
/*****************************************************/
gdt:
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x0000000000000000 /* not used */
.quad 0x00cf9a000000ffff /* 0x10 main 4gb code at 0x000000 */
.quad 0x00cf92000000ffff /* 0x18 main 4gb data at 0x000000 */

.word 0xFFFF # 16bit 64KB - (0x10000*1 = 64KB)
.word 0 # base address = SETUPSEG
.byte 0x00, 0x9b # code read/exec/accessed
.byte 0x00, 0x00 # granularity = bytes


.word 0xFFFF # 16bit 64KB - (0x10000*1 = 64KB)
.word 0 # base address = SETUPSEG
.byte 0x00, 0x93 # data read/write/accessed
.byte 0x00, 0x00 # granularity = bytes

gdt_end:

●First Instruction Executed




剖析First Instruction Executed
一、Processor 寄存器初始狀態,如上圖。
1、CR0.PE = 0,處理器處於真實模式
2、RIP = FFF0h
3、CS.Base = FFFF_0000h
4、由於在真實模式下,第一條指令的物理位址就在:CS.Base + RIP = FFFF_FFF0h。

二、MCH(NorthBridge)的處理
FFFF_FFF0h位址由Processor送到MCH進行解析,這個位址落在MCH固定為High BIOS 分配的區域中,MCH直接通過DMI介面送到ICH進行處理。MCH相當於一個派發者角色,根據定義的位址映射分派到不同的介面。

三、ICH(SouthBridge)的處理
1、ICH對MCH提交過來的位址進行解碼。
2、ICH的位址映射機制中:FFFF_FFF0地址落 FFF8_0000 ~ FFFF_FFFFh這個範圍中,這個範圍的位址將直接分派到LPC介面的Frimware Hub進行處理,LPC 介面相當於一個PCI-to-ISA 橋,BIOS寄居在LPC bus上,從而訪問BIOS區域。
3、在物理器件上,ICH Firmware Hub的IDSEL選擇固定位址設為FFF8_0000 ~ FFFF_FFFF範圍,LPC介面寄存器(B#0, D#30,F#0)中的,Firmware Hub Decode Enable寄存器的bit15固定為1,允許FFF8_0000 ~ FFFF_FFFF位址提交到Firmware Hub。這樣確保FFFF_FFF0提交到LPC bus。
ICH相當於一個解碼器角色,解碼後分配任務給設備執行。

四、第一條指令執行
經典地在FFFF_FFF0絕大多數是一條far jmp指令:jmp far ptr 0F000h:0E05Bh,不同的BIOS跳轉的位址或許不同。這條指令跳轉到FE05B這個位址上執行。同時刷新CS.Selector、CS.Base以及EIP寄存器。引一段 Intel 的話:

The first instruction that is fetched and executed following a hardware reset is
located at physical address FFFFFFF0H. This address is 16 bytes below the
processor’s uppermost physical address. The EPROM containing the software initialization
code must be located at this address.
The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor
while in real-address mode. The processor is initialized to this starting address as
follows. The CS register has two parts: the visible segment selector part and the
hidden base address part. In real-address mode, the base address is normally
formed by shifting the 16-bit segment selector value 4 bits to the left to produce a
20-bit base address. However, during a hardware reset, the segment selector in the
CS register is loaded with F000H and the base address is loaded with FFFF0000H. The
starting address is thus formed by adding the base address to the value in the EIP
register (that is, FFFF0000 + FFF0H = FFFFFFF0H).

以上這段文字表明,Intel從架構上規定了,processor復位後,FFFFFFF0H地址上必須包含一些初始化程式,BIOS必須在定位在這段位址區域上。
MCH與ICH的配合從物理上通過位址映射機制保證了這一點的實施。

五、驗證一下FFFF_FFF0位址上是否是一條far jmp指令,可以簡單地打開cmd視窗執行debug命令。
C:>debug
-d F000:FFF0
F000:FFF0 EA 5B E0 00 F0 30 32 2F 32 37 2F 30 38 00 FC
EA 5B E0 00 F0 就是 jmp far ptr F000:E05B 指令,這條指令跳轉到FE05B的物理位址上,這將是 BIOS 的 BOOTBLOCK。

六、BIOS位址空間高端與低端的別名機制

1、在整個位址空間中 FFE0_0000 ~ FFFF_FFFF 這段2M 區域真正地被固定分配給 BIOS 使用,包括基本的 BIOS 區以及一些設備擴展的 BIOS 區。這段空間是不能提交至 DRAM 的。

2、實際上,BIOS的低端地址C_0000 ~ F_FFFF 屬於PAM區 (即:Programmed Attibute Memory) ,這段區域可被賦為4個訪問屬性:disable,read-only,write-only 以及 read/write。根據訪問屬性的不同可被提交到ICH,也可以提交DRAM,但初始化的屬性是Disable,為DRAM不可用,初始狀態是通過DMI提交到ICH處理的,但是可以在 BIOS 代碼修改的,從而提交至DRAM。

3、C_0000 ~ F_FFFF 與 FFFC_0000 ~ FFFF_FFFF 指向同一個區域,被冠以別名稱呼,即C_0000 ~ F_FFFF 被映射到 FFFC_0000 ~ FFFF_FFFF。

AMD 如是說:參閱圖4
Accesses to BIOS space in the low megabyte (between 000C_0000h and 000F_FFFFh) are mapped to the top megabyte (between FFFC_0000h and FFFF_FFFFh) on the LPC bus; the OAR locks for these apply to these accesses based on the remapped address at the top megabyte.

●無論是C_0000 ~ F_FFFF,還是 FFFC_0000 ~ FFFF_FFFF 最終結果都將送到LPC bus上的FFFC_0000 ~ FFFF_FFFF 地址上。參閱圖5

2009年3月30日 星期一

●分解BIOS

注意:本網站討論的部分內容不詳,還沒瞭解透,定義為:不清昕,可能有錯誤的。

這裡主要以Intel平臺的BIOS檔討論,輔助參考AMD平臺的BIOS文件。要分解的BIOS檔選了當下較新的X38晶片組平臺的ex38dq6.f2 這個BIOS檔,而BIOS的檔是ma79xds4.f4,這是AMD的最新的7系晶片組其中的790X晶片組平臺。好,下面開始進行分解ex38dq6.f2這個BIOS檔。

一、工具的使用
1、Ex38dq6.f2是Award Bios,有一個圖形化的BIOS編輯軟體awdbedit,可以很方便的將BIOS的元件分解出來。
2、通用的BIOS編輯軟體cbrom,這裡使用的是cbrom182版本。下面是使用cbrom182顯示BIOS元件的清單,命令列下使用:Cbrom182 ex38dq6.f2 /D,結果如下(部分):

******** ex38dq6.f2 BIOS component ********
No. Item-Name Original-Size Compressed-Size Original-File-Name
================================================================================
0. System BIOS 20000h(128.00K)15478h(85.12K)ex38dq6.BIN
1. XGROUP CODE 0FC40h(63.06K)0B0ECh(44.23K)awardext.rom
2. ACPI table 04E16h(19.52K)0193Ch(6.31K)ACPITBL.BIN
3. EPA LOGO 0168Ch(5.64K)0030Dh(0.76K)AwardBmp.bmp
4. GROUP ROM[18] 031D0h(12.45K)0225Ah(8.59K)ggroup.bin
5. YGROUP ROM 0C180h(48.38K)066E4h(25.72K)awardeyt.rom
6. GROUP ROM[ 0] 08210h(32.52K)0303Dh(12.06K)_EN_CODE.BIN
7. PCI ROM[A] 10000h(64.00K)09DBEh(39.44K)ICH9RAID.BIN
8. PCI ROM 03600h(13.50K)02553h(9.33K)ICH8AHCI.BIN
9. PCI ROM[C] 07A00h(30.50K)04479h(17.12K)JMB59.BIN
10. MINIT 08220h(32.53K)0824Fh(32.58K)DDR2_MRC.X38
11. PCI ROM[D] 0C800h(50.00K)079FDh(30.50K)rtegrom.lom
12. LOGO1 ROM 00B64h(2.85K)00520h(1.28K)dbios.bmp
13. LOGO BitMap 4B30Ch(300.76K)07EEEh(31.73K)x48dq6.bmp
14. GV3 01EFDh(7.75K)00B66h(2.85K)PPMINIT.ROM
15. OEM0 CODE 028ABh(10.17K)01E1Bh(7.53K)SBF.BIN
(SP) NCPUCODE 1D000h(116.00K)1D000h(116.00K)NCPUCODE.BIN

Total compress code space = E5000h(916.00K)
Total compressed code size = 75C8Dh(471.14K)
Remain compress code space = 6F373h(444.86K)

清單: 2.1

整個ex38dq6.f2 檔1M大小,包含了16個元件,最後的NCPUCODE.BIN元件,是虛擬的或者說物理上不存在,用awdbedit軟體分解不包括這個元件,實際上只有15個真實元件,這些元件全都是經過壓縮的。第2列是元件的名字,第3列是元件真實的大小,第4列是元件中部分壓縮的資料在ex38dq6.f2檔中的大小,最後1列是分解後元件存在磁片上的物理檔案名,以ex38dq6.bin為例,這個元件真實的大小為128K,其中85.12K是壓縮部分,其餘的以純代碼形式分佈在FE000 ~ FFFFF區域,典型地:第一條far jmp就分佈在這個區域。
ex38dq6.BIN 是BIOS的主體組件。
awardext.rom、awardeyt.rom 是BIOS的擴展部分。
ACPITBL.BIN 是供ACPI所使用的低級部件,可供作業系統使用。
PCI ROM 是 PCI 設備的一些元件。
還有一些顯示的BMP圖片
其餘組件不詳,有待瞭解
3、使用cbrom182來分解元件的方法:
Cbrom182 ex38dq6.f2 /XGROUP extract 分解出 awardext.rom
Cbrom182 ex38dq6.f2 /ACPI extract 分解出 ACPITBL.BIN
如此類推,可以逐步分解出各個元件,但是,SYSTEM BIOS元件,也即是 ex38dq6.bin 這個元件,我怎麼試也沒分解出來,所以用以下推薦的方法分解。

4、推薦分解BIOS元件的方法
使用圖形化的BIOS編輯軟體awdbedit可以很方便簡單分解全部的元件。運行awdbedit軟體,打開ex38dq6.f2,忽略掉一些警告資訊,進入後,選擇 [Actions] –> [Extract All] 就可以分解出全部的元件。

二、BIOS元件位置分析
1、ex38dq6.f2檔共1M大小,除了包含各個BIOS元件外,還充斥著大量的“填充碼”,這些“填充碼”是FF位元組以及00位元組,主要用來分隔各個元件,以及填充檔。
2、壓縮元件是以LZH形式壓縮,每個壓縮元件以“-lh5-”開頭,十六進位碼形式為 2D 6C 68 35 2D,這是壓縮組件的戳記,因此,在BIOS檔中只要尋找到這個戳記就可以區分開每個元件。
seg000:0000 24 F7 2D 6C 68 35 2D 50 54 01 00 00 00 02 00 00 $?lh5-PT ... ..
seg000:0010 00 00 50 20 01 0B 65 78 33 38 64 71 36 2E 42 49 ..P
ex38dq6.BI
seg000:0020 4E 24 D3 20 00 00 2D 20 8F 77 BF 74 89 29 BB AA N$?..- 弚縯?華
seg000:0030 7F 33 33 37 37 4D 07 73 55 45 55 78 35 91 D5 66 3377MsUEUx5懻f
seg000:0040 85 B7 54 49 34 52 21 0E 9B A5 10 91 11 BC 1D 28 叿TI4R!
洢 ??(
seg000:0050 B1 2A 66 A0 DD 5B BB BA 9C 0D 51 0C C5 17 AA F2 ?f犦[緩?Q
?
seg000:0060 FB DD BC AC AD 34 F1 55 DB 53 CC 03 DD A6 86 30 棘?馯跾?葒?
seg000:0070 2A CF 42 B5 DC 53 52 22 43 F0 75 84 66 40 00 77 *螧弟SR"C饀刦@.w
seg000:0080 7F FE 66 83 37 77 79 E7 9E BC F6 FF BD 7A FD EE �?wy鐬薦�絲
seg000:0090 BE FF 04 3E F7 76 B2 49 1B 6D C9 D1 4B 2D 15 A0 ? >鱲睮 m裳K- ?
seg000:00A0 AE 84 C4 52 58 5F FF CF ED 24 AC C1 42 64 1F F0 畡腞X_�享$Bd­?
seg000:00B0 BF 45 55 49 0A A2 CE C2 97 58 58 AF 0E 62 22 84 縀UI⑽聴XX?b"?
seg000:00C0 7E CF 94 2F E7 24 F7 E3 CE 0F 55 B8 E0 94 E0 D5 ~蠑/?縻?U膏斷?
seg000:00D0 D1 BE E0 9E FB 99 C1 F8 3B 86 C5 B8 86 6C 6B 85 丫酁麢柳;喤竼lk?
seg000:00E0 88 B3 F7 05 5A F0 BA CB C3 2E 5F 89 F8 AF ED B2 埑?Z鷙嗣._夬?
seg000:00F0 91 9C 42 50 B7 CA 60 34 B6 4A 55 8C 65 D3 8E EA 憸BP肥`4禞U宔訋?
seg000:0100 6A 5D E1 4F 7E DB 97 7F 4C A0 AE 9E 15 B7 8E 86 j]酧~蹢L牣?穾?

清單 2.2

3、以ex38dq6.f2為例,用十六制編輯軟打開,從00000000開始到000FFFFF共1M的大小,每個壓縮元件在ex38dq6.f2的物理位置如下:
0. 0 ~ 15477: System Bios (ex38dq6.bin)
1.15478 ~ 20563: XGROUP CODE(awardext.rom)
2.20564 ~ 21E9F: ACPI table(ACPITBL.BIN)
3. 21EA0 ~ 221AC: EPA LOGO(awardBmp.bmp)
4.221AD ~ 24406: GROUP ROM[18](ggroup.bin)
5.24407 ~ 2AAEA: YGROUP ROM(awardeyt.rom)
6.2AAEB ~ 2DB27: GROUP ROM[0](_EN_CODE.BIN)
7.2DB28 ~ 378E5: PCI ROM[A](ICH9RAID.BIN)
8.378E6 ~ 39E38: PCI ROM(ICH8AHCI.BIN)
9.10. 39E39 ~ 46500: PCI ROM[C]、MINIT(JMB59.BIN、DDR2_MRC.X38)
11.46501 ~ 4DEFD: PCI ROM[D](rtegrom.lom)
12.4DEFE ~ 4E41D: LOGO1 ROM(dbios.bmp)
13.4E41E ~ 5630B: LOGO BIGMAP(X48DQ6.bmp)
14.5630C ~ 56E71: GV3(PPMINIT.ROM)
15.56872 ~ 58C8C: OME0 CODE(SBF.BIN)

以上是各個壓縮元件在BIOS檔中的物理位置,從58C8D ~ FDFFF 這段區間中混合著一些資料,還在大量充斥著“填充碼”,沒有什麼實際的意義,或者說:沒看到什麼實際意義。從 FE000 ~ FFFFF 這段區間中,包含一些非壓縮的純二進位碼,其中有重要的BOOTBLOCK,以及一些初始化代碼。也包含著大量的“填充碼”。這些純代碼分散分佈在這個區間,純代碼與部分壓縮元件的混合在一起,幾乎很難區分哪些是純代碼,哪些是壓縮資料,指令:jmp far ptr 0F000:0E05B 與其它資料混合在一起,如下清單2.2所示:
seg000:FFB25 db 0C3h ; ?
seg000:FFB26 db 66h ; f
seg000:FFB27 db 0EFh ; ?
seg000:FFB28 db 8Bh ; ?
seg000:FFB29 db 0D7h ; ?
seg000:FFB2A db 8Eh ; ?
seg000:FFB2B db 0D9h ; ?
seg000:FFB2C ; ---------------------------------------------------------------------------
seg000:FFB2C jmp far ptr 0F000h:0E05Bh
seg000:FFB2C ; ---------------------------------------------------------------------------
seg000:FFB31 db 0
seg000:FFB32 db 0
seg000:FFB33 db 0
seg000:FFB34 db 0
seg000:FFB35 db 0
seg000:FFB36 db 0
seg000:FFB37 db 0
seg000:FFB38 db 0
seg000:FFB39 db 0

清單 2.3

4、BIOS的主體文件 ex38dq6.BIN的大小是128K,正好映射到系統位址空間的FFFE_0000 ~ FFFF_FFFF(E_0000 ~ F_FFFF)共128K的空間上。
當分解出BIOS主體元件ex38dq6.BIN後,這條指令就在F000:FFF0 的位置上,如下清單2.3所示:

seg000:FFFEC db 80h ; €
seg000:FFFED db 1
seg000:FFFEE db 0Ch
seg000:FFFEF db 89h ; ?
seg000:FFFF0 ; ---------------------------------------------------------------------------
seg000:FFFF0 jmp far ptr 0F000h:0E05Bh
seg000:FFFF0 ; ---------------------------------------------------------------------------
seg000:FFFF5 db 30h ; 0
seg000:FFFF6 db 32h ; 2
seg000:FFFF7 db 2Fh ; /
seg000:FFFF8 db 32h ; 2
seg000:FFFF9 db 37h ; 7
seg000:FFFFA db 2Fh ; /
seg000:FFFFB db 30h ; 0
seg000:FFFFC db 38h ; 8
seg000:FFFFD db 0
seg000:FFFFE db 0FCh ; ?
seg000:FFFFF db 0B3h ; ?
seg000:FFFFF seg000 ends

清單 2.4

在ex38dq6.BIN 檔中的FFFF0位置對應著物理FFFFFFF0這個位址上,第1條指令是far jmp,跳轉到BOOTBLOCK中,通常在這條指令的周圍是些有意議的字元描述,如:02/27/08這是BIOS日期,在far jmp 下麵處於BIOS尾端。

三、不同BIOS檔之間的異同

1、結構不同,AMI和award的BIOS是有差別的。
2、BIOS檔的大小不同,一般的BIOS檔大小為512K,以ma79xds4.f4為例,它是512K,ex38dq6.f2是1M,但結構上差什麼差異,下面是ma79xds4.f4的結構:

No. Item-Name Original-Size Compressed-Size Original-File-Name
================================================================================
0. System BIOS 20000h(128.00K)13944h(78.32K)ma79xds4.BIN
1. XGROUP CODE 0F7D0h(61.95K)0AB7Bh(42.87K)awardext.rom
2. ACPI table 06391h(24.89K)02B35h(10.80K)ACPITBL.BIN
3. EPA LOGO 0168Ch(5.64K)0030Dh(0.76K)AwardBmp.bmp
4. GROUP ROM[18] 03340h(12.81K)02339h(8.81K)ggroup.bin
5. YGROUP ROM 0B310h(44.77K)05023h(20.03K)awardeyt.rom
6. GROUP ROM[ 0] 07100h(28.25K)02C90h(11.14K)_EN_CODE.BIN
7. PCI ROM[A] 0C800h(50.00K)0AC37h(43.05K)sata22.bin
8. OEM1 CODE 0AE4Fh(43.58K)06B6Dh(26.86K)ui22.bin
9. PCI ROM[B] 0A800h(42.00K)06007h(24.01K)RTLGPXE.LOM
10. LOGO1 ROM 00B64h(2.85K)00520h(1.28K)dbios.bmp
11. OEM0 CODE 028ABh(10.17K)01E1Bh(7.53K)SBF.BIN
12. GV3 088C6h(34.19K)026FBh(9.75K)AGESACPU.ROM
13. MINIT 11B80h(70.88K)11BB3h(70.92K)MEMINIT.BIN
14. HTINIT 04BC0h(18.94K)04BF0h(18.98K)HT.DLL
15. 2 PE32 in MB 00552h(1.33K)00582h(1.38K)HT32GATE.BIN
(SP) NCPUCODE 04000h(16.00K)04000h(16.00K)NCPUCODE.BIN

Total compress code space = 63000h(396.00K)
Total compressed code size = 621F3h(392.49K)
Remain compress code space = 00E0Dh(3.51K)

清單 2.5

上面清單所示,與ex38dq6.f2的結構一樣,每個元件都有一部分是經過壓縮的。

●跟著流程走(一):far jmp後發生什麼?




跟著流程走(一):far jmp後發生什麼?
一、所需材料
1、主要BIOS :ex38dq6.f2 :技嘉主機板上Intel X38 MCH + ICH9 平臺。
備用BIOS:ma79xds4.f4 : 技嘉主機板上AMD 790X 北橋 + SB600 南橋平臺。
2、cbrom:這是一個BIOS編輯工具,這裡所用的是cbrom182版本
3、lha2.55:LHA格式的解壓工具。
4、awdbedit:award bios 的圖形化編輯工具,方便簡單。
5、hex workshop:一個十六進位編輯工具,簡單小巧。
6、IDA:一個反彙編工具,這裡使用的是IDA 5.2版本

二、所需知識
1、組合語言:這是必備的知識,彙編掌握的程度和理解能力成正比。
2、機器語言:這個不是必需的,但推薦能夠讀懂機器語言,某些場合下當組合語言也陷入窘境時,機器語言是唯一的解釋手段。
3、x86體系知識:具體可以查看相關的Intel 或 AMD 手冊
4、ISA/PCI 匯流排知識:可以查看相應的 ISA/PCI Specification
5、north/south bridge 知識:Intel 現在以MCH代稱north bridge,ICH代稱south bridge,可以查看相應的 datasheet


接下來用IDA pro打開ex38dq6.BIN觀察,這個是BIOS的主體檔,跟著流程走,看看far jmp後BIOS做什麼工作。
1、第一條指令 jmp far ptr F000:E05B 經過幾個跳轉,跳到F000:F46C處
2、以下是F000:F46C的代碼:
seg000:FF46C cli
seg000:FF46D cld
seg000:FF46E xchg bx, bx
seg000:FF470 smsw ax
seg000:FF473 test al, 1
seg000:FF475 jz short near ptr 0F480h
seg000:FF477 cli
seg000:FF478 mov al, 0FEh ; '?
seg000:FF47A out 64h, al ; AT Keyboard controller 8042.
seg000:FF47A ; Resend the last transmission
seg000:FF47C cli
seg000:FF47D hlt
--------------------------------------------------------------
取機器狀態字,也就是CR0寄存器,測試CR0.PE是否為1,判斷CPU是否處於真實模式狀態,若不是則停機。
若處於真實模式轉到F000:F480繼續處理。
3、轉到F000:F480又經過一道跳轉,來到F000:E043進行處理
4、下麵是F000:E043的代碼:

seg000:FE043 mov al, 8Fh ; '? ; disable NMI# and get 0Fh offset register
seg000:FE045 out 70h, al ; CMOS Memory:
seg000:FE045 ;
seg000:FE047 out 0EBh, al
seg000:FE049 in al, 71h ; get OFh offset register data
seg000:FE04B out 0EBh, al
seg000:FE04D or al, al ; is RESET ?
seg000:FE04F jmp near ptr 0F483h

在這裡,取 CMOS RAM 中位於0F處1個位元組的資料,通過測試這個位元組是否為0,判斷是否屬正常啟動。
5、正常啟動的話,調用F000:54DE這個子過程進行處理,否則跳到F000:3468。
二、下面看看F000:54DE的處理,以下是第二張流程圖:
下面proc_F54DE的代碼:

seg000:F54DE mov ax, 0
seg000:F54E1 mov es, ax
seg000:F54E3 cmp word ptr es:472h, 1234h
seg000:F54EA jnz short near ptr 54F8h
seg000:F54EC mov al, 8Fh ; '?
seg000:F54EE out 70h, al ; CMOS Memory:
seg000:F54EE ;
seg000:F54F0 out 0EBh, al
seg000:F54F2 mov al, 0AAh ; '?
seg000:F54F4 out 71h, al ; CMOS Memory:
seg000:F54F4 ;
seg000:F54F6 out 0EBh, al
seg000:F54F8 mov dx, 3C4h
seg000:F54FB mov al, 1
seg000:F54FD out dx, al ; EGA: sequencer address reg
seg000:F54FD ; clocking mode. Data bits:
seg000:F54FD ; 0: 1=8 dots/char; 0=9 dots/char
seg000:F54FD ; 1: CRT bandwidth: 1=low; 0=high
seg000:F54FD ; 2: 1=shift every char; 0=every 2nd char
seg000:F54FD ; 3: dot clock: 1=halved
seg000:F54FE inc dl
seg000:F5500 in al, dx ; EGA port: sequencer data register
seg000:F5501 or al, 20h
seg000:F5503 out dx, al ; EGA port: sequencer data register
seg000:F5504 call near ptr 76FBh
seg000:F5507 retn

1、在BIOS資料區的0472處存放著一個重定標誌:
seg000:F54E3 cmp word ptr es:472h, 1234h
通過比較 [0472] 是否1234h,標誌1234h是一個暖開機標誌位元,機器暖開機時,例如:按下CTRL+ALT+DEL 三個鍵時,由鍵盤中斷處理常式在[0472]處寫標誌1234h。
2、是暖開機的話,將寫入AA標誌到CMOS RAM 的0F處。
3、接著設置EGA相應的工作狀態。
4、在proc_F76FB過程裡置計時器1的狀態。
5、最後調用過程proc_F2941進行晶片組的初始化。

三、下面是本站節的重點,初始化某部分晶片組,下面是流程圖:
1、下面重點理解 write_pci_byte這個BIOS提供的rontine,代碼如下:

seg000:FF798 xchg ax, cx ; write_byte routine
seg000:FF799 shl ecx, 10h
seg000:FF79D xchg ax, cx
seg000:FF79E mov ax, 8000h ; Bus 0
seg000:FF7A1 shl eax, 10h
seg000:FF7A5 mov ax, cx
seg000:FF7A7 and al, 0FCh
seg000:FF7A9 mov dx, 0CF8h ; config_address register
seg000:FF7AC out dx, eax
seg000:FF7AE add dl, 4 ; config_data register
seg000:FF7B1 mov al, cl
seg000:FF7B3 and al, 3
seg000:FF7B5 add dl, al
seg000:FF7B7 mov eax, ecx
seg000:FF7BA shr eax, 10h
seg000:FF7BE out dx, al
seg000:FF7BF retn

將這個routine功能簡化為C代碼形式來看比較直觀:
void wirte_pci_byte(int offset_number, int mask)
{
if (number == -1)
jmp_7666();

do_wirte_pci_byte(offset_number, mask);
}

這段routine固定寫PCI的Bus0,Device0,Function0,offset 值放在cx中,由調用者傳來,置什麼值放在al寄存器,這是1個位元組的值。Bus0,Dev0,Fun0是hostbrige控制器(NorthBridge),也即是DRAM控制器的地址所在。這段代碼是典型的寫PCI設置的手法。PCI設置位址送入config_address_register中,然後往config_data_register裡寫資料,這個PCI設備位址將映射到PCI設備的寄存器,如前面介紹的位址空間圖所示,PCI設備位址範圍是E000_0000 ~ EFFF_FFFF,這段空間提交到相應的PCI設備。
2、現在回過頭來看調用者,cx=95,al=33 這個參數傳給 write_pci_byte。Offset是95,mask碼是33。Offset 95在write_pci_byte將被置為94,這將是DRAM控制器的PAM4寄存器,PAM4寄存器控制D_8000 ~ D_FFFF記憶體空間的屬性。寫入33,結果是:將這段空間置為read/write屬性,這將是所有訪問這段空間的操作會提交到DRAM。而不再是ROM。

3、Offset 96的結果和offset 95一樣,在write_pci_byte的遮罩中被置為offset 94。

●瞭解幾個資料結構


瞭解幾個資料結構
一、BIOS的簡短介紹。
1、IBM推出個人電腦,簡稱:IBM PC。定義了BIOS規範。最初的BIOS由IBM工程師獨立完成。後來為了普及IBM PC,IBM 公開了PC內部結構以及BIOS的程式設計規範,介面並鼓勵協助各廠商開發BIOS
2、各BIOS廠商迅速崛起,代表廠商有:AMI、Award、Phoenix等。逐漸發展成為一個小廠商被大廠商吞併的時代。
3、BIOS的基於工作任務:
● POST:開機自檢
● 初始化階段:對DRAM、晶片組,週邊設備等設置
● 保存相應資料:在COMS RAM 及 BIOS 資料區保存相應的資料
● 駐入服務常式:寫入中斷向量表及BIOS的插斷服務常式等

二、 中斷向量表
1、BIOS啟動初期的一個重要工作就是在記憶體最低端設置相應的BIOS中斷向量表,若啟動了DOS系統,DOS的系統還負責設置相應的DOS中斷向量表,典型的代表是INT 21h服務。
2、中斷服務程式獲取的演算法:
● Interrupt Handler = IDTR.base + vector × vector size
● X86 支持 256(FF)中斷。
● 處理器RESET 後處於 16 位real mode,IDTR.base 初化為 0,因此:整個BIOS 及 DOS 系統的中斷向量表位於:0 + 0 × 4 ~ 0 + FF × 16,也就是 0 ~ 3FFh 的物理位址。

3、BIOS及DOS的整個中斷向量表佈局如下(0 ~ 3FF):
中斷號 位址 說明
00 0000 除0錯
01 0004 單步執行
02 0008 不可遮罩中斷
03 000C 中斷點調試
04 0010 溢出中斷
05 0014 BIOS列印螢幕中斷
06 0018 無效指令錯
07 001C 無效浮點指令
08 0020 IRQ0 計時器中斷
09 0024 IRQ1 鍵盤中斷
0A 0028 IRQ2 串聯次中斷控制器
0B 002C IRQ3 COM2
0C 0030 IRQ4 COM1
0D 0034 IRQ5 LPT2
0E 0038 IRQ6 軟碟控制卡
0F 003C IRQ7 LPT1
10 0040 BIOS 視頻服務常式
11 0044 BIOS 週邊設備檢查
12 0048 BIOS 檢測記憶體大小
13 004C BIOS 磁片常式
14 0050 BIOS 通信服務常式
15 0054 BIOS 擴展系統常式
16 0058 BIOS 鍵盤服務常式
17 005C BIOS 列印服務常式
18 0060 ROM BASIC 常式
19 0064 引導系統
1A 0068 BIOS 時間/RTC
1B 006C BIOS Ctrl-Break處理常式
1C 0070 Int 8h調用的計時子常式
1D 0074 視頻參數表
1E 0078 軟碟機參數表
1F 007C 字元點陣表
20 0080 DOS 程式終止
21 0084 DOS 系統服務常式
22 0088 DOS 程式結束位址
23 008C DOS Ctrl-Break處理常式
24 0090 DOS 程式嚴重錯誤處理
25 0094 DOS 讀磁片常式
26 0098 DOS 寫磁片常式
27 009C DOS TSR
28 00A0 DOS 空閒中斷
29 00A4 DOS 字元輸出
2A 00A8 網路介面
2B ~ 2D 00AC ~ 00B4 保留
2E 00B8 DOS Shell傳遞參數
2F 00BC DOS 多重功能中斷
30 00C0 保留
31 00C4 DOS 保護模式介面
32 00C8 保留
33 00CC 滑鼠服務常式
34 ~ 3E 00D0 ~ 00F8 浮點模擬運算攔截碼入口
3F 00FC 覆蓋管理
40 0100 軟碟中斷服務
41 0104 硬碟1參數表
42 0108 視頻中斷(用於EGA)
43 010C EGA 參數表
44 0110 EGA 點陣字元表
45 0114 保留
46 0118 硬碟2參數表
47 ~ 49 011C ~ 0124 保留
4A 0128 CMOS/RTC 報警中斷
4B ~ 66 012C ~ 0198 保留
67 019C 擴充記憶體管理員
68 ~ 6F 01A0 ~ 01BC 保留
70 01C0 IRQ8 CMOS/RTC 中斷
71 01C4 IRQ9 重定向到 Int 0A
72 01C8 IRQ10 PnP設備
73 01CC IRQ11 PnP 設備
74 01D0 IRQ12 PS/2、USB 設備使用
75 01D4 IRQ13 數字輔助處理器
76 01D8 IRQ14 IDE設備
77 01DC IRQ15 IDE 設備
78 ~ FF 01E0 ~ 03FC 保留

4、關於中斷向量表的後續話題
1)中斷向量表的重定位:Interrupt Vector Table 的基底位址存儲在 IDTR寄存器。Processor 復位後,IDTR.base = 0。也即中斷向量表在物理位置0位址上。中斷向量表的基底位址可以通過 LIDT 指令更改。但真實模式的DOS系統不作出任何改變。
2)中斷向量表的變遷:保護模式的現代作業系統重新對中斷向量表進行了定位。Interrupt Vector Table 被 Interrupt Descriptor Table 取代了。前者是存放真實的中斷服務程式的入口位址。後者是存放的稱為門符的描述符結構。描述符定義了相應的屬性及許可權。真實的中斷服務程式被門符間接索引。
3)中斷向表的變遷:時至今日,中斷體系的變遷,保護模式下的中斷向量的含義大部分發生了改變。如:13號向量是 #GP(General Protection)異常。發生了如系統資料結構產生違例訪問或越權訪問等就產生保護異常。
4)中斷向量表結構:中斷向量表以及中斷服務程式依然位於記憶體的低位元。這份由BIOS維護的中斷向量表結構只在系統啟動初期產生一些作用。成功引導作業系統後,將被作業系統所拋棄,作業系統將建立屬於自己的一套中斷向量表以及中斷服務程式。典型的是Linux 系統定義了 80h 號向量作為system call 門符。

三、CMOS 資料區域
CMOS 資料區存放一些基本系統資料,如RTC(真實的時間戳記),它是一個主機板上的一個RAM,為了使CMOS RAM裡面的資料不丟失,主機板上提供了一個電池供電。

1、CMOS RAM 的訪問方式
訪問 CMOS RAM 是過I/O 埠在IO Space進行的,CMOS RAM 大小為128 位元組,位址範圍從0 ~ 7Fh。
● 70h 埠:這個埠是個索引位址埠,通過向這個埠輸送一個位址值,這個位址值位於 CMOS RAM裡。
位址索引值的結構如下:
Bit7:最高位為 NMI Disable 位。置1則 Disable NMI
Bit6 ~ 0:CMOS RAM 位址索引,範圍從 0 ~ 7Fh
● 71h 埠:這個埠是資料埠。從這個埠獲取71h埠輸送索引位址的內容。
● 72h 埠:這個埠如同71h埠一樣,但這個埠可以訪問整個256位元組空間。也就是它的Bit7是有效索引值。
● 73h 埠:這個埠如同72h 埠一樣,獲取從72埠輸送索引位址的內容。

錯誤:此處有誤,謝謝 zx_wing 指出.
正確為:
● 72h 埠:這個埠如同70h埠一樣,但這個埠可以訪問整個256位元組空間。也就是它的Bit7是有效索引值。
● 73h 埠:這個埠如同71h 埠一樣,獲取從72埠輸送索引位址的內容。

2、CMOS RAM 資料內容
位址索引 含義
00 ~ 09h 相應的系統時間域

0A Bit7:0-時間可讀 1-等待更新再讀
Bit6 ~ 4:除法器頻率
Bit3 ~ 0:時間中斷頻率

0B Bit7:0-設定時間,但仍在計時狀態 1-設定時間,但在停止計時狀態
Bit6:同期性中斷,0-Disable 1- Enable
Bit5:時間警報中斷,0 – Disable 1-Enable
Bit4:1-允許中斷在更新時間時 0-Disable
Bit3:方波設定 0 – Disable 1-Enable
Bit2:日期/時間格式 0 – BCD格式 1- Binary格式
Bit1:時間制式 0- 12時制 1- 24時制
Bit0:日光節約時間 0-Disable 1-Enable

0C Bit7:IRQ標誌
Bit6:週期性中斷標誌
Bit5:警報中斷標誌
Bit4:更新中斷標誌
Bit3~0:保留

0D Bit7:CMOS RAM內容正常性,0正常 1-異常
Bit6~0:保留,為0

0E Bit7:CMOS/RTC 晶片電源,0正常 1異常
Bit6:CMOS RAM CheckSum 0 正常 1 異常
Bit5:CMOS RAM 配置狀態,0配置與檢測的一致 1不一致
Bit4:CMOS RAM 記憶體狀態,0記錄的記憶體與檢測一致 1不一致
Bit3:硬碟C啟動狀態 0啟動通過可Boot 1失敗,無法Boot
Bit2:時間記錄 0 正常無誤 1記錄異常
Bit1~0:保留,為0

0F 00: 軟體重定
01:真實模式/保護模式下發生RESET或實模下晶片組初始化時發生RESET
02:真實模式/保護模式下記憶體檢查通過後RESET
03:真實模式/保護模式下記憶體檢查失敗後RESET
04:通過INT 19h 重新開機(Boot)
05:清除鍵盤中斷(EOI),跳到40:67記錄的位置
06:保護模式下測試成功後RESET跳到40:67記錄的位置
07:保護模式下測試失敗後RESET
08:由POST切到保護模式下進行記憶體檢測
09:BIOS INT1使用
0A:返回跳到40:67記錄的程式入口
0B:IRET回到40:67 記錄的程式入口
0C:RET回到40:67記錄的程式入口

0D~FF: 上電時初始復位

10h ~ 2Fh ISA系統組態

30h ~ 3Fh BIOS設置

40h ~ 7Fh 晶片組配置


四、BIOS 記錄資料區域
1、如下圖所示,緊接著中斷向量表結構的是BIOS用來記錄資料的區域,位址範圍:400h ~ 600h 共 512 位元組。這個區域被 BIOS 用來檢測系統狀態而記錄使用的區域,以及 BIOS 定義的某些資料結構(例如:鍵盤緩衝區)。
2、在 BIOS 執行初期 BIOS 將檢測 BIOS 資料區域的某些資料確定當前機器狀態。例如:BIOS 通過檢查 [0472] 的內容是否是 1234H 從而確定機器是暖開機還是冷開機。
3、BIOS 定義了鍵盤緩衝區在 01Eh 開始的 32 個位元組的。