天天看點

第11章:進入保護模式

描述符格式:

高32位
31 24 23 22 21 20 1916 15 1413 12 11 8 7 0
段基位址 24-31(8bit) G D/B L AVL 段界限(19-16 = 4bit = 1個16位) p DPL S TYPE 段基位址 23-16(8bit)
低32位:
31 16 15
段基位址 15-0 段界限15-0
  1. G(粒度):

    0=1 byte

    1= 4k byte

  2. D/B (操作數大小)

    0=16bit

    1=32bit

  3. L:64bit處理器用的
  4. AVL(空閑)
  5. P 段是否存在記憶體中

    0 = 不存在

    1 = 存在

  6. DPL 描述符的特權級别 : 指定通路該段需具有的最低特權級别

    保護模式執行的代碼特權級别都是 0

  7. S: 段的類型

    0=系統段

    1=代碼or資料段

  8. type: 代碼段or資料段的的類型

分析例子:

mov dword [bx+0x08],0x7c0001ff ;低位
 mov dword [bx+0x0c],0x00409800	;高位
     

0x00409800
0x7c0001ff
           
  1. 線性位址

    高位(前1個位元組+最後1個位元組)+低位(前2個位元組) =

    0x00007c00

  2. 段界限 (20bit = 4+16 bit)
    • 4bit恰好是1個16進制數
    高位第4個16進制數(0) + 低位最後2個位元組(

    01ff

    ) =

    0x001ff

  3. 剩下 高位第3個(

    3

    )、第5個(

    9

    )、第6個(

    4

    )16進制數,每個位置對應看就行了

80486: 南橋的處理器接口的端口0x92:第0位

INIT_NOW

用于初始化處理器,此端口=1,處理器複位,計算機重新開機,第1位控制A20,

2種模式的切換開關:CR0是 控制寄存器(Control register),并且第1位(位0)是保護模式允許位置(Protection Enable), =1則處理器進入保護模式,

32bit處理器下的段寄存器變成了段選擇器(存描述符在table中的偏移位置),且cpu内部對應了一個使用者不可見的描述符高速緩存寄存器, 并且新增了2個額外的段寄存器

Fs

,

Gs

(注意:仍是16位)

32bit處理器實模式下,引用一個段,處理器自動将段位址左移4位,傳送到描述符高速緩存器中,(32bit下向段寄存器傳送16bit的邏輯段位址處理器不看做是描述符選擇子)

保護模式通路記憶體的方式(段選擇子的組成
  • 傳送到段寄存器的不是邏輯段位址而是在描述符表(局部或者全局)中的索引号(偏移位址)
    1. TL指出在GDT(=0)還是LDT(=1)中
    2. RPL:自己的特權級别
15 3 2 1 0
描述符索引(偏移位址) TI RPL
在設定CR0的位0後進入保護模式的問題
  • 處理器進入16位保護模式

    操作數預設大小由描述符D位決定,此時段寄存器的描述符高速緩存器仍是實模式(低20有效,高12為0)内容,是以D位 = 0,此時處于16bit保護模式下

  1. 描述符高速緩存器低20位有效,高12位為0,應重新整理段寄存器的内容
  2. 流水線中待執行的都是按照16bit操作數或者32bit編譯後的位元組,因為對位址的解釋不同,可能導緻執行結果不正确

通過

jmp

重新整理段寄存器和段寄存器描述符高速緩沖器,因為無條件轉移,流水線被清空

JMP

執行時:将GDT()中描述符加載到cs寄存器描述符高速緩存器,偏移量送到EIP中

;實模式下
mov ax,cs
mov ss,ax
mov sp,0x7c00

mov ax,[cs:gdt_base+0x7c00]
mov dx,[cs:gdt_base+0x7c00+0x02]
mov bx,16
div bx
mov ds,ax   ;商
mov bx,dx   ;bx=偏移位址

;将ds:bx設定成 gdt的起始位址
mov dword [bx+0x00],0x00
mov dword [bx+0x04],0x00

;code段描述符
mov dword [bx+0x08],0x7c0001ff
mov dword [bx+0x0c],0x00409800

;data段設定成顯存 0xb800
mov dword [bx+0x10],0x8000ffff
mov dword [bx+0x14],0x0040920b

;棧段
mov dword [bx+0x18],0x00007a00
mov dword [bx+0x1c],0x00409600

;将gdt的線性及位址和大小加載到gdtR寄存器中
mov word [cs:gdt_size+0x7c00],31 ;gdt的大小=描述符個數*8-1

;lgdt指令:
lgdt [cs:gdt_size+0x7c00];gdt加載完畢

in al,0x92
or al,0000_0010B    ;打開A20.因為要相容是以A20一直設定成0,現在打開,因為要進入保護模式
out 0x92,al

cli ;清除IF位置,禁止中斷
;此時仍然處于實模式
mov eax,cr0 ;将控制寄存器的内容讀入到eax中
or eax,1    ;打開pe位置(保護模式的位置)
mov cr0,eax
;進入保護模式,但是還未清空流水線
jmp dword 0x0008:flush ;通過無條件轉移指令來清空流水線

bits 32;接下來的按照32bit來編譯
flush:
    mov byte [0x00],'P'
    mov byte [0x02],'r'
    mov byte [0x04],'o'
    mov byte [0x06],'t'
    mov byte [0x08],'e'
    mov byte [0x0a],'c'
    mov byte [0x0c],'t'
    mov byte [0x0e],' '
    mov byte [0x10],'m'
    mov byte [0x12],'o'
    mov byte [0x14],'d'
    mov byte [0x16],'e'
    mov byte [0x18],' '
    mov byte [0x1a],'O'
    mov byte [0x1c],'K'
;填寫段選擇子來選擇位于gdt中的棧段
    mov cx,00000000000_11_000B
    mov ss,cx ;修改ss段寄存器,向其中填寫段選擇子
    mov esp,0x7c00

    mov ebp,esp
    push byte '.'

    sub ebp,4
    cmp ebp,esp
    jnz ghalt   ;ebp!=esp則cpu暫停
    pop eax     ;ebp=esp 将值pop到eax中
    mov [0x1e],al ;al的值寫到ds:0x1e中,因為ds=0xb800是以顯示到螢幕上了
ghalt:
    hlt

gdt_size    dw 0
gdt_base    dd 0x00007e00
times 510-($-$$) db 0
db 0x55,0xaa
           

Bochs

調試:

  1. r:顯示所有的通用寄存器
  2. sreg:段寄存器
  3. creg:控制寄存器内容: PE=1 進入保護模式
  4. lgdt加載gdt到gdtr後,用info gdt檢視gdt

來源:《X86實模式到保護模式》