我們知道,現在大部分的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個扇區,並不是從...