9.1 實地址模式與保護模式之間的切換
我們知道,IA-32計算機在加電或者Reset信號有效之后,首先進入實地址模式,執行BIOS程序,然后再進入保護模式,執行Windows環境下的程序。因此,IA-32 CPU在工作的時候,需要從實地址模式切換到保護模式。從實地址模式切換到保護模式,通常需要建立描述符表(descriptor table),設置控制寄存器CR0的PE位,如例9.1所示。
例9.1 IA-32 CPU從實地址模式切換到保護模式,然后又切換回實地址模式。
;
;IA-32 CPU enters into protected mode from real address mode,
;a string is displayed on the screen, IA-32 CPU returns into real
;address mode from protected mode.
;
.386p
.model small,c
.stack 100h
;
Descriptor STRUCT
limit word 0
basel word 0
basem byte 0
attrib word 0
baseh byte 0
Descriptor ENDS
;
Data Segment use16
;
gdt0 Descriptor <>
;
DataSel equ $-gdt0
DataDes Descriptor <0ffffh,,,92h,>
;
CodeSel equ $-gdt0
CodeDes Descriptor <0ffffh,,,98h,>
;
VideoSel equ $-gdt0
VideoDes Descriptor <0ffffh,8000h,0Bh,92h,>
;
GdtLen equ $-gdt0
;
GdtPtr word GdtLen
dword 0
;
Mess byte ‘IA-32 CPU is in Protected Mode.’,0
;
JmpTable dword 0
;
Data ends
;
Code Segment use16
assume cs:Code,ds:Data
;
Start: xor eax,eax ;Clear eax.
;
mov ax,Data ;Initialize DS.
mov ds,ax ;
;
;Prepare for entering into protected mode.
;
shl eax,4 ;create base address for data segment
;
mov dword ptr [GdtPtr+2],eax ;Base address for load GDTR
;
mov DataDes.basel,ax ;Fill base address for data segment
shr eax,16 ;into data segment descriptor.
mov DataDes.basem,al ;
mov DataDes.baseh,ah ;
;
xor eax,eax ;Fill base address for code segment
mov ax,Code ;into code segment descriptor.
shl eax,4 ;
mov CodeDes.basel,ax ;
shr eax,16 ;
mov CodeDes.basem,al ;
mov CodeDes.baseh,ah ;
;
lgdt fword ptr GdtPtr ;Load GDTR
;
cli ;Disable interrupt.
mov eax,cr0 ;SetPE of CR0 into 1.
or eax,1
mov cr0,eax
;
mov dx,CodeSel ;Use “jmp far ptr [esi]” to flush
shl edx,16 ;instruction prefetch queue.
mov dx,offset Code16
mov JmpTable,edx
lea esi,JmpTable
jmp far ptr [esi]
;
;Enter into protected mode.
;
Code16: mov ax,DataSel ;The string is displayed on the screen.
mov ds,ax
mov ax,VideoSel
mov es,ax
mov si,offset Mess
mov di,80*46+48
mov ah,47h
Load: mov al,[si]
inc si
cmp al,0
jz ReadyToRmode
mov es:[di],ax
add di,2
jmp Load
;
;Prepare for returning to real address mode.
;
ReadyToRmode: mov eax,cr0 ;Clear PE of CR0.
and eax,0fffffffeh
mov cr0,eax
;
mov dx,seg Rmode ;Use “jmp far ptr [esi]” to flush
shl edx,16 ;instruction prefetch queue.
mov dx,offset Rmode
mov JmpTable,edx
lea esi,JmpTable
jmp far ptr [esi]
;
;Return to real address mode.
;
Rmode: sti ;Set IF.
;
mov ax,Data ;Initialize DS and ES.
mov ds,ax
mov es,ax
;
mov ax,4c00h ;Retire.
int 21h
;
Code ends ;Code segment is over.
;
end Start ;The source program is over.
課堂練習:對於例9.1中所示的程序,我們可以在MASM 6.11環境下進行匯編和鏈接,然后運行並觀察其結果:
A. 啟動PWB。
B. 更改設置:點Options,在彈出的選單上選中Build Options,在彈出的Build Options窗口中選中Use Release Options,點OK。
C. 打開源程序文件sam9-1.asm,建立可執行程序sam9-1.exe。注意,請不要進入CodeView環境去運行sam9-1.exe。
D. 重啟計算機,根據計算機的不同,進行適當操作,使其進入MS-DOS方式。
E. 在MS-DOS方式下,運行sam9-1.exe,即可看到在屏幕上顯示出的字符串。
現在對例9.1中所示程序的工作原理進行分析:
1. 聲明結構類型Descriptor
保護模式下的段寄存器:其內容不再是段的基地址,而是稱之為選擇符(selector),其功能在於從全局段描述符表(Global Descriptor Table,GDT)或者局部段描述符表(Local Descriptor Table,LDT)中選出一個段描述符。選擇符的字段結構如表9.1所示。
表9.1 選擇符的字段結構
b15~b3 |
b2 |
b1~b0 |
Index |
TI |
RPL |
選擇符的位b1~b0稱之為請求特權級(Requested Privilege Level,RPL)。RPL的數字必須小於當前特權級(Current privilege level,CPL)的數字,才可以訪問某個段的數據。CPL是當前正在執行的任務所具有的特權級,也就是段寄存器CS和SS中b1~b0所具有的數字。另外,段描述符的訪問權限字節(字節5)的b6~b5稱之為段描述符特權級(Descriptor privilege level,DPL)。DPL、CPL、RPL是Windows操作系統中保護機制的重要基礎。
TI位:為0時,說明段描述符在GDT中,應該訪問GDT;為1時,說明段描述符在LDT中,應該訪問LDT。
Index:利用它可以從GDT或者LDT中選出一個段描述符。具體作法是,由CPU硬件首先把Index的值乘以8,然后把乘積加到段描述符表的基地址上。段描述符表的基地址和限長分別存在全局段描述符表寄存器(Global Descriptor Table Register,GDTR)和局部段描述符表寄存器(Local Descriptor Table Register,LDTR)中。
GDT和LDT都放在內存中,由Windows操作系統維護。通常,系統任務的段描述符存放在GDT中,用戶任務的段描述符存放在LDT中。每個段描述符表包含8192( )個段描述符。
GDT和LDT中的段描述符可以認為是一種數據結構,每個段描述符具有8個字節的長度,其結構如表9.2所示。
表9.2 段描述符的結構
字節7 |
字節6 |
字節5 |
字節4~2 |
字節1~0 |
|
基址b31~b24 |
控制位 |
段限b19~b16 |
訪問權限 |
基址b23~b0 |
段限b15~b0 |
在例9.1中,所聲明的結構類型Descriptor與表9.2所示的段描述符結構是完全一致的。
2. 關於完全段
本例中的數據段和代碼段都使用了完全段來定義。原因在於只有使用完全段才可以實現在保護模式下,定義16位段的功能。
3. 數據段
定義結構變量必須在數據段中,而聲明結構類型則可不必在數據段中。
gdt0:NULL段描述符,Windows操作系統要求第一個段描述符必須定義為NULL。
DataSel:定義數據段選擇符。
DataDes:定義數據段描述符,基地址需用指令填充。
CodeSel:定義代碼段選擇符。
CodeDes:定義代碼段描述符,基地址需用指令填充。
VideoSel:定義視頻緩沖區段選擇符。
VideoDes:定義視頻緩沖區段描述符。
GdtLen:全局段描述符表限長。
GdtPtr:指向全局段描述符表限長和基地址的指針。
Mess:進入保護模式后顯示在屏幕上的字符串。
JmpTable:在jump指令的間接尋址方式下,用來存放轉移目標地址。
4. 代碼段:初始化DS,建立數據段基地址,建立用來裝入GDTR的基地址,把數據段基地址填入數據段描述符,把代碼段基地址填入代碼段描述符。
5. 代碼段:裝全局描述符表寄存器指令
該指令助記符用“LGDT”表示,其功能是把內存數據段中的GDT的限長和基地址裝入GDTR。
GDTR的字段結構如表9.3所示。
表9.3 GDTR的字段結構
b47~b16 |
b15~b0 |
GDT的基地址 |
GDT的限長 |
LGDT指令只需要一個操作數。由表9.3知道GDTR的長度具有6個字節,所以其操作數的類型也應該是6個字節。LGDT指令的形式和操作列在表9.4中。
表9.4 LGDT指令的形式和操作
匯編語句格式 |
編碼示例 |
尋址方式與操作 |
LGDT m16&32 |
LGDT fword ptr GdtPtr |
直接尋址 |
注:在表9.4中,m代表memory,ptr代表pointer,以下同。
LGDT指令只能由操作系統使用,用戶程序不能使用。通常,LGDT指令運行在實地址模式下,以便於CPU在切換到保護模式以前能夠執行初始化操作。
6. 代碼段:關中斷,設置CR0的PE位,清除指令預取隊列
用cli指令關中斷。
控制寄存器(Control Register,CR)CR0具有32位的長度,其b0為保護模式允許位(Protection Enable,PE)。當該位為1時,允許CPU工作在保護模式下;當該位為0時,禁止CPU工作在保護模式下。系統加電啟動時,該位被置0。
用“jmp far ptr [esi]”指令,執行一個無條件轉移,以便於清除指令預取隊列,保證順利地進入保護模式。此處jmp指令使用了間接尋址方式,方法是:首先把轉移目標地址存入內存數據段變量JmpTable中,然后把JmpTable的偏移量傳送到esi中,最后執行一個以esi內容為指針的far轉移。
7. 代碼段:進入保護模式
在屏幕上顯示字符串“IA-32 CPU is i.n Protected Mode.”。此處采用直接寫顯示緩沖區的方法。
顯示緩沖區的基地址:實地址模式下為B8000H(以物理地址形式表示),保護模式下為000B8000H(以物理地址形式表示)。
指定屏幕上的顯示坐標:mov di,80*46+48。屏幕上可顯示字符的容量為24行×80字符。在顯示緩沖區中,每個字符用兩個字節來表示,高字節表示顏色與屬性,低字節存儲字符的ASCII代碼。
指定屏幕上的顯示屬性:mov ah,47h,請參考MASM 6.11的聯機幫助。
8. 代碼段:准備返回到實地址模式
清除CR0的PE位,以便於CPU能夠工作在實地址模式下。
用“jmp far ptr [esi]”指令,執行一個無條件轉移,以便於清除指令預取隊列,保證順利地返回到實地址模式。
9. 代碼段:返回到實地址模式
設置ds和es,返回到系統。
課堂練習:調節屏幕顯示坐標和顏色與屬性。