PM複習 進入保護模式

2021-05-25 19:55:33 字數 4375 閱讀 9829

我們知道,現在大部分的cpu都是32位的,我們編寫的os當然也想在執行在32位的模式下,因為32位的cpu功能自然比16位的強大許多,32位的cpu在硬體給予了os很大的支援。但是當機器剛剛啟動時,cpu是執行在16位模式下的,所以我們必須學習怎麼樣進入32位模式,也即保護模式。

16位模式也就是實模式,是我們最開始學習彙編時cpu執行的模式,是用段暫存器左移4位+偏移位址來定址的;而在保護模式下則大不一樣,是先分段再分頁定址的,分頁是可選的,分段是必須的。所以想進入保護模式的第一步就是建立一張段表,也就是gdt,這個表中的每乙個項就是乙個段描述符descriptor,有8個位元組,記錄了乙個段的起始位址,段界限,以及段屬性,從而對每乙個段進行保護。

code:

[section .gdt]   

label_gdt:   

label_desc_dummy:   

times 8 db 0   

label_desc_code32:   

dw 0ffffh   

times 3 db 0   

db 9ch   

db 4fh   

db 0   

label_desc_video:   

dw 0ffffh   

dw 8000h   

db 0bh   

db 92h   

db 4fh   

db 0  

如上面的**所示,gdt中有3個描述符,第1個是啞描述符,沒什麼意義;第2個表示1個32位的可執行非一致**段,段界限為0xfffff,段基址沒有填寫,日後會填充;第3個是1個可讀寫的資料段,段界限位0xfffff,段基址為0xb8000,此段指示了視訊記憶體。描述符的結構在這裡就不細說了,查查書就好了。

有了gdt,我們定址的時候只要把想訪問的段的相對於gdt的索引送到段暫存器就ok了,那麼怎麼知道gdt在記憶體中的位置呢?在cpu有乙個48位的暫存器,就存放著gdt在記憶體的位址和gdt的長度。所以在進入保護模式必須給這個暫存器填上正確的位址和長度。如下**所示:

code:

gdt_len equ $ - label_gdt   

gdt_ptr:   

dw gdt_len - 1   

dd 0  

我們看到,gdt的長度已填上,gdt實際的實體地址還不知道,因為我們的測試程式是在dos下動態載入的,所以在日後再填寫,這個以後填充段描述符的段基位址是一樣的道理。

還有乙個概念是選擇子selector,是乙個16位的結構,它的高13位是相應段在gdt中的索引,為什麼只用高13位表示呢?簡單啊,因為乙個描述符是8個位元組,所以索引肯定是8個倍數,低3位就用不著,可以拿來做別的事,至於是什麼事呢,日後再說。下面就是定義選擇子的**:

code:

selector_code32 equ label_desc_code32 - label_desc_dummy    

selector_video  equ label_desc_video - label_desc_dummy  

準備好了這些,還不夠啊,還有一些東東沒有填充好呢。先不理它,先明確我們進入保護模式後要幹嘛,那就在螢幕上列印個字元吧,這樣可以測試下我們的gdt有沒有設定正確,下面就是這個32位的**段:

code:

[section .s32]   

[bits 32]   

label_code32:   

mov ax,selector_video   

mov gs,ax   

mov ah,0ch   

mov al,'x'

mov [gs:80 * 10],ax   

jmp $  

為了簡單起見,列印完後進入死迴圈。

最後只剩16位**了,這段**當然是做與進入保護模式有關的事情了,下面是**:

code:

[section .s16]   

[bits 16]   

label_begin:   

mov ax,cs   

mov ds,ax   

mov es,ax   

xor eax,eax   

mov ax,cs   

shl eax,4   

add eax,label_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  

xor eax,eax   

mov ax,ds   

shl eax,4   

add eax,label_gdt   

mov dword [gdt_ptr + 2],eax   

lgdt [gdt_ptr]   

cli   

in  al,92h   

or  al,00000010b   

out 92h,al   

mov eax,cr0   

or  eax,1   

mov cr0,eax   

jmp dword selector_code32:0  

8到15行是填充32位**段的段基址,17到21行是填充gdt的位址,接著載入到gdtr暫存器中,然後是關掉中斷,因為在保護模式下中斷的機制跟實模式不太一樣,先關掉日後再說。接著開啟a20位址線使得能夠定址到更大的範圍,接著開啟cr0暫存器的最低位,值得讓cpu進入保護模式,最後是歷史性的跳轉正式跳轉到保護模式。

下面是完整的**:

code:

org 0100h   

jmp label_begin   

[section .gdt]   

label_desc_dummy:   

times 8 db 0   

label_desc_code32:   

dw 0ffffh   

times 3 db 0   

db 9ch   

db 4fh   

db 0   

label_desc_video:   

dw 0ffffh   

dw 8000h   

db 0bh   

db 92h   

db 4fh   

db 0   

gdt_len equ $ - label_desc_dummy   

gdt_ptr:   

dw  gdt_len - 1   

dd  0   

selector_code32 equ label_desc_code32 - label_desc_dummy   

selector_video  equ label_desc_video - label_desc_dummy   

[section .s16]   

[bits 16]   

label_begin:   

mov ax,cs   

mov ds,ax   

mov es,ax   

xor eax,eax   

mov ax,cs   

shl eax,4   

add eax,label_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   

xor eax,eax   

mov ax,ds   

shl eax,4   

add eax,label_desc_dummy   

mov dword [gdt_ptr + 2],eax   

lgdt    [gdt_ptr]   

cli   

in  al,92h   

or  al,00000010b   

out 92h,al   

mov eax,cr0   

or  al,1   

mov cr0,eax   

jmp selector_code32:0   

[section .s32]   

[bits 32]   

label_code32:   

mov ax,selector_video   

mov gs,ax   

mov ah,0ch   

mov al,'x'

mov [gs:80 * 10],ax   

jmp $   

執行結果如下:

進入保護模式

本文為 每個描述符佔8位元組,下圖中,上面位高32位,下面為低32位 disk boot ata channel 0 first hd cd on channel 0 type of disk image這個選項是vpc,我用2.6.2配置時選vpc無法啟動,得選flat才可以,2.6.0貌似選vp...

進入保護模式

以下圖2,圖4和圖5截自intel手冊 每個描述符佔8位元組,下圖中,上面位高32位,下面為低32位 disk boot ata channel 0 first hd cd on channel 0 type of disk image這個選項是vpc,我用2.6.2配置時選vpc無法啟動,得選fl...

進入保護模式總結

1.最主要的就是那個jmp dword,一切都是為了jmp做準備 2.int 13h讀取扇區,既可以讀硬碟,又可以讀軟盤,應該是兩個的驅動器號不同 這個還沒搞清楚,現在只會讀軟盤 用硬碟映像的時候,總是說找不到引導裝置,不知道是哪兒的引數錯了。一些細節 cl是讀取的扇區號,1就是第1個扇區,並不是從...