核心目的:實現由實模式到保護模式的轉換
核心步驟:
? ? 1)程序定義了GDT數據結構
? ? 2)16位代碼進行了一些與GDT有關的操作
? ? 3)程序最后跳到32位代碼中做了一點操作顯存的工作
進入保護模式的主要步驟:
? ? 1)準備GDT
? ? 2)用lgdt加載gdtr
? ? 3)關中斷、打開A20
? ? 4)置cr0的PE位
? ? 5)跳轉,進入保護模式
邏輯地址到線性地址的轉換:
? ? 1)先從GDTR寄存器中獲得GDT基址。
? ? 2)然后再GDT中以段選擇器高13位位置索引值得到段描述符。
? ? 3)段描述符符包含段的基址、限長、優先級等各種屬性,這就得到了段的起始地址(基址),再以基址加上偏移地址yyyyyyyy才得到最后的線性地址。
怎樣查看線性地址的內容:
? ? sudo ndisasm -o 0x7c00 pmtest1.bin >> wz.asm 同
? ? x /80bx 0x00007c80 命令對比,可以看到線性地址中的內容一一對應
; ==========================================
; pmtest1.asm
; 編譯方法:nasm pmtest1.asm -o pmtest1.bin
; ==========================================
%include "pm.inc" ; 常量, 宏, 以及一些說明
org 07c00h
? ? ? ? jmp LABEL_BEGIN
[SECTION .gdt]
; GDT
; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 段基址,? ? ? 段界限 , ? ? ? ? ? ? ? ? ? ? ? 屬性
LABEL_GDT: ? Descriptor ? ? ? ? ? ? ? ? ? ?0,? ? ? ? ? ? ? ? 0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0? ? ? ? ? ; 空描述符
LABEL_DESC_CODE32: Descriptor ? 0, ? ? ? ? ? ? ? ? SegCode32Len - 1, ? ?DA_C + DA_32; 非一致代碼段
LABEL_DESC_VIDEO:? Descriptor ? ? ?0B8000h, ? ?0ffffh, ? ? ? ? ? ? ? ? ? ? ? ? ? ?DA_DRW ? ? ; 顯存首地址
; GDT 結束
GdtLen equ $ - LABEL_GDT ; GDT長度
GdtPtr dw GdtLen - 1 ; GDT界限
? ? ? ? ? ? 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 ? //棧頂指針寄存器
? ? ? ? ; 初始化 32 位代碼段描述符
? ? ? ? xor eax, eax
? ? ? ? mov ax, cs
? ? ? ? shl eax, 4
? ? ? ? add eax, LABEL_SEG_CODE32 ? ? ? ? ? ? ? ? ? ? ? ?//eax為 cs:LABEL_SEG_CODE32
? ? ? ? mov word [LABEL_DESC_CODE32 + 2], ax ? ? ?//將LABEL_SEG_CODE32放到第二個字(也即3 4字節)
? ? ? ? shr eax, 16
? ? ? ? mov byte [LABEL_DESC_CODE32 + 4], al ? ? ? ? ?//將al放到低字節
? ? ? ? mov byte [LABEL_DESC_CODE32 + 7], ah ? ? ? ? ?//將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]
? ? ? ? ; 關中斷
? ? ? ? cli
? ? ? ? ; 打開地址線A20
? ? ? ? in al, 92h
? ? ? ? or al, 00000010b
? ? ? ? out 92h, al
? ? ? ? ; 準備切換到保護模式
? ? ? ? mov eax, cr0
? ? ? ? or eax, 1
? ? ? ? mov cr0, eax
? ? ? ? ; 真正進入保護模式
? ? ? ? jmp dword SelectorCode32:0 ; 執行這一句會把 SelectorCode32 裝入 cs,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ; 并跳轉到 Code32Selector:0? 處
; END of [SECTION .s16]
[SECTION .s32]; 32 位代碼段. 由實模式跳入.
[BITS 32]
LABEL_SEG_CODE32:
? ? ? ? mov ax, SelectorVideo
? ? ? ? mov gs, ax ; 視頻段選擇子(目的)
? ? ? ? mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
? ? ? ? mov ah, 0Ch ; 0000: 黑底? ? 1100: 紅字
? ? ? ? mov al, 'P'
? ? ? ? mov [gs:edi], ax
? ? ? ? ; 到此停止
? ? ? ? jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
1.實模式
? ? Intel 8086 16位CPU,有16位的寄存器、16位的數據總線以及20位的地址總線(1MB的尋址能力)。
? ? 物理地址 = 段地址 x 16 + 偏移
? ? 此時段地址可以看做是地址的一部分。
2.保護模式
? ? Intel 80386進入32位時代,有32位地址總線(尋址空間可達4GB)。
? ? 保護模式下段的概念發生了變化,段地址仍雖然由原來16位cs、ds寄存器表示,但此時它變成了一個索引,這個索引指向一個數據結構的一個表項,表項中詳細定義了段的起始地址、界限、屬性等。這個數據結構,就是GDT(LDT),GDT中的表項也有一個專門的名字,叫做描述符。
? ? GDT的作用是提供段式存儲機制,這種機制是通過段寄存器和GDT中描述符共同提供的。
; 宏 ------------------------------------------------------------------------------------------------------
;
; 描述符
; 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 ? //將limit的低16位放在最低0 1兩個字節
? ? ? ? dw? ? ? %1 & 0FFFFh? ? ? ? ? ? ? ? ? ? ? ? ? ? ; 段基址1 ? //將base的低16位放在2 3字節
? ? ? ? db? ? ? (%1 >> 16) & 0FFh? ? ? ? ? ? ? ? ? ? ? ; 段基址2 ? //將base的第三位字節放在4字節
? ? ? ? dw? ? ? ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)? ? ; 屬性1 + 段界限2 + 屬性2 ?//按字節位置進行拼湊成 5 6字節
? ? ? ? db? ? ? (%1 >> 24) & 0FFh? ? ? ? ? ? ? ? ? ? ? ; 段基址3 ? //將base的第四字節(從低位開始計數)放在7字節
%endmacro ; 共 8 字節
3.保護模式——段選擇子(段選擇符)
? ? 段選擇子包括三部分:描述符索引(index)、TI、請求特權級(RPL)。他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位置,由這個位置再根據在GDTR中存儲的描述符表基址就可以找到相應的描述符。然后用描述符表中的段基址加上邏輯地址(SEL:OFFSET)的OFFSET就可以轉換成線性地址,段選擇子中的TI值只有一位0或1,0代表選擇子是在GDT選擇,1代表選擇子是在LDT選擇。請求特權級(RPL)則代表選擇子的特權級,共有4個特權級(0級、1級、2級、3級)。
? ? SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT? // ?所需要的段的描述符在描述符表的位置
? ? mov ax, SelectorVideo
? ? mov gs, ax ; 視頻段選擇子(目的) ? ? ?//段選擇子(Selector)由GDTR訪問全局描述符表是通過“段選擇子”(實模式下的段寄存器)來完成的。段選擇子是一個16位的寄存器(同實模式下的段寄存器相同),這里是寄存器gs
? ? mov [gs:edi], ax ? //gs的之為SelectorVideo,它對應顯存的描述符DESC_VIDEO,這條指令將ax的值寫入段DESC_VIDEO顯存中偏移位edi的位置。
4.實模式到保護模式
step1.將描述符LABEL_DESC_CODE32初始化完成:將LABEL_SEG_CODE32的物理地址賦值給eax,并將它分成三部分賦值給描述符LABEL_DESC_CODE32中的相應位置。
step2.將GdtPtr指示的6字節加載到寄存器gdtr
? ? GdtLen equ $ - LABEL_GDT ; GDT長度
? ? GdtPtr dw GdtLen - 1 ; GDT界限
? ? ? ? ? ? dd 0 ; GDT基地址
? ? lgdt [GdtPtr]
step3.關中斷,保護模式下中斷處理機制是不同的
step4.打開A20地址線。A20地址不打開,只能尋址1MB
step5.將寄存器cr0的第0位置為1
? ? 寄存器cr0的第0位是PE位,0-實模式,1-保護模式
step6.此時cs的值仍是實模式下的值,需要把代碼段的選擇子裝入cs
? ? jmp dword SelectorCode32:0
? ? jmp dword ptr內存地址:以內存地址單元處的雙字來修改指令,高地址內容修改CS,低地址內容修改IP,內存地址可以以任何合法的方式給出
? ? sudo ndisasm -o 0x7c00 pmtest1.bin >> wz.asm查看值
? ? 00007C75? 66EA000000000800? jmp dword 0x8:0x0 ? 這里的SelectorCode32為8,賦值給cs
? ? bochs調試模式中,sreg可以看到cs的內容:
? ? cs:0x0008, dh=0x00409900, dl=0x7c800014, valid=1
? ? Code segment, base=0x00007c80, limit=0x00000014, Execute-Only, Non-Conforming, Accessed, 32-bit
5.描述符屬性
LABEL_DESC_CODE32: Descriptor? 0,? ? ? ? ? ? ? ? SegCode32Len - 1,? ? DA_C + DA_32; 非一致代碼段
LABEL_DESC_VIDEO:? Descriptor ? ? ?0B8000h, ? ?0ffffh, ? ? ? ? ? ? ? ? ? ? ? ? DA_DRW ? ? ; 顯存首地址
DA_C? ? ? ? ? ? EQU? ? 98h? ? ; 存在的只執行代碼段屬性值 ? ? ? ? ? ? ? ? ? ? ? 1001 1000
DA_32? ? ? ? ? EQU? ? 4000h? ; 32 位段 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 100 0000 0000 0000
DA_DRW? ? ? EQU? ? 92h? ? ; 存在的可讀寫數據段屬性值 ? ? ? ? ? ? ? ? ? ? ? ?1001 0010
dw? ? ? ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)? ? ; 屬性1 + 段界限2 + 屬性2? //按字節位置進行拼湊成 5 6字節