操作系统:GDT简介&实模式到保护模式的跳转
前言(瞎逼逼)
自從AFO之后就想著開(kāi)一個(gè)什么新坑,但是包括但不限于大學(xué)申請(qǐng)之類(lèi)的破事兒讓我的確忙活了一陣子。。。閑下來(lái)的時(shí)候突然想起來(lái)以前曾經(jīng)開(kāi)過(guò)一個(gè)小頭(當(dāng)然很快就因?yàn)樽藙?shì)水平不夠就放棄了)的編寫(xiě)操作系統(tǒng),于是自己就想著把兒時(shí)的夢(mèng)想繼續(xù)堅(jiān)持下去吧
這篇博客主要是依靠于淵的《Orange's 一個(gè)操作系統(tǒng)的實(shí)現(xiàn)》里面的代碼來(lái)給出的解釋?zhuān)钥赡軙?huì)有很多的匯編。
實(shí)模式
CPU自啟動(dòng)的時(shí)候一開(kāi)始會(huì)首先進(jìn)入實(shí)模式,此時(shí)我們只能訪問(wèn)1MB的內(nèi)存,并且任何程序都可以訪問(wèn)內(nèi)存段內(nèi)的任意位置,這樣顯然是不安全的。但是現(xiàn)在的32位CPU可以訪問(wèn)4GB的內(nèi)存,我們主要是通過(guò)進(jìn)入保護(hù)模式來(lái)達(dá)到這個(gè)目的的(保護(hù)模式帶來(lái)的好處并不限于訪問(wèn)內(nèi)存的擴(kuò)大)
GDT
我們先來(lái)想一下,實(shí)模式下CPU的尋址方式是:
[段地址:段內(nèi)偏移
]
這種方式在以前固然好,但是新的32位CPU提供了32位地址線。老的16位寄存器已經(jīng)不夠用了,我們現(xiàn)在需要一種新的內(nèi)存尋址方式,GDT就此誕生了
GDT本質(zhì)上一個(gè)表,表內(nèi)包含了一段內(nèi)存的段基址,段界限,段屬性。實(shí)模式下我們使用段地址:偏移地址通過(guò)地址處理器來(lái)直接訪問(wèn)內(nèi)存,但是實(shí)模式下段地址的意義發(fā)生了根本的變化。段地址不再表示實(shí)際內(nèi)存中的某一段,而是表示的我們要訪問(wèn)的段在GDT表里面的偏移。通過(guò)訪問(wèn)GDT表的特定表項(xiàng)來(lái)得出段基址,緊接著通過(guò)段內(nèi)偏移來(lái)訪問(wèn)內(nèi)存。此時(shí)的段地址我們有另外一個(gè)名字——段選擇子。我們通過(guò)定義不同的選擇子來(lái)對(duì)應(yīng)到不同的表項(xiàng)里面去,接著就可以訪問(wèn)特定內(nèi)存了。
總結(jié)起來(lái)就是這個(gè)樣子的:
當(dāng)然,要使用GDT我們必須先有一個(gè)GDT,下面是GDT的定義部分:
[SECTION .gdt]
LABEL_GDT: Descriptor 0, 0, 0 ;空描述符
LABEL_DESC_CODE32: Descriptor 0,SegCode32Len-1, DA_C+DA_32 ;32位代碼段
LABEL_DESC_VIDEO: Descriptor 0B8000h,0ffffh ,DA_DRW ;顯存映射到內(nèi)存中的地址
然后加上GDT的長(zhǎng)度和界限
GdtLen equ $-LABEL_GDT ;GDT長(zhǎng)度
GdtPtr dw GdtLen-1 ;GDT界限
dd 0
在這里面的 LABEL_DESC_CODE32 和 GdtPtr 都要-1,因?yàn)樵L問(wèn)指定表項(xiàng)所對(duì)應(yīng)的段基址的時(shí)候都是包含基址的,長(zhǎng)度自然要-1
再往后面就是定義段選擇子了,其實(shí)很簡(jiǎn)單,就是表項(xiàng)相對(duì)于GDT表的偏移
SelectorCode32 equ LABEL_DESC_CODE32-LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO-LABEL_GDT
然后我們就要開(kāi)始準(zhǔn)備載入GDT了。這里 LABEL_DESC_CODE32 的基地址并不急著給出,因?yàn)槟阋膊恢?2位代碼段的起始位置。
接著就是在16位下初始化GDT了:
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h
mov ax,0003h
int 10h
這里除了一些基本的指令以外就是我調(diào)用了10號(hào)中斷來(lái)清了屏,因?yàn)閎ochs一開(kāi)始屏幕上會(huì)有一堆BIOS提示。
且聲明了棧段和sp。
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_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
這里我們就要開(kāi)始處理之前留下來(lái)的32位代碼段基地址的問(wèn)題了。要注意的是,由于此時(shí)我們?nèi)匀惶幱?6位下,并且GDT并沒(méi)有被載入到i內(nèi)存中去,所以 LABEL_DESC_CODE32+2 指向的仍然是標(biāo)號(hào)指向的位置,也就是表項(xiàng)內(nèi)部(和之前在實(shí)模式下尋址方式一樣,不要搞混了)
xor eax,eax
mov ax,ds
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr+2],eax
CPU是通過(guò)寄存器GDTR來(lái)得知GDT的基地址和界限的,GDTR的示意圖在這里:
我們要做到的就是在GdtPtr里面填充符合標(biāo)準(zhǔn)的數(shù)據(jù)然后使用后lgdt指令來(lái)加載GDTR寄存器,也就是上面的這段代碼所做的事情。
然后就可以快樂(lè)地加載GDTR了
lgdt [GdtPtr]
接著我們需要屏蔽掉所有16位中斷。因?yàn)楸Wo(hù)模式下中斷處理的方式都是不同的(比如說(shuō)尋址),如果計(jì)算機(jī)在保護(hù)模式之后又跑去處理實(shí)模式下的中斷代碼的化會(huì)發(fā)生錯(cuò)誤
cli ;關(guān)中斷
再就是打開(kāi)A20地址線了。關(guān)于A20地址線的歷史性問(wèn)題這里就不再贅述,感興趣的化可以去《x86匯編語(yǔ)言 從實(shí)模式到保護(hù)模式》的11.5章節(jié)里面找到細(xì)節(jié)的描述。
具體來(lái)講,打開(kāi)A20地址線的方法就是操作92號(hào)端口來(lái)實(shí)現(xiàn)的:
in al,92h
or al,00000010b
out 92h,al
接下來(lái)就是置控制寄存器cr0的PE(Protected Enable)位到1來(lái)開(kāi)啟保護(hù)模式
mov eax,cr0
or eax,1
mov cr0,eax
然后就是跳轉(zhuǎn)到32位保護(hù)模式下的代碼段了:
jmp dword SelectorCode32:0
這里有一些細(xì)節(jié)要注意。因?yàn)榇藭r(shí)GDTR已經(jīng)獲得了GDT的位置,所以這里就已經(jīng)是通過(guò)GDT來(lái)尋址了。并且此時(shí)偏移地址仍然是在16位下進(jìn)行編譯的,所以如果偏移地址不是0而是一個(gè)很大的值,普通的 jmp SelectorCode32:0x12345678 可能會(huì)讓偏移位上的地址發(fā)生截?cái)唷K晕覀兺ㄟ^(guò)聲明dword類(lèi)型的地址避免編譯器把偏移地址給自動(dòng)截?cái)?/p>
接下來(lái)就是32位代碼段的代碼了,有一些之前使用過(guò)的標(biāo)號(hào)是在這里聲明的,可以和之前的代碼結(jié)合起來(lái)看:
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax,SelectorVideo
mov gs,ax
mov edi,(80*11+39)*2
mov ah,0ch
mov al,'p'
mov [gs:edi],ax
jmp $
SegCode32Len equ $-LABEL_SEG_CODE32
就是在屏幕中央打印一個(gè)紅色的 'p'
所有代碼:
%include "pm.inc"
org 07c00h
jmp LABEL_BEGIN
[SECTION .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
GdtLen equ $-LABEL_GDT
GdtPtr dw GdtLen-1
dd 0
SelectorCode32 equ LABEL_DESC_CODE32-LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO-LABEL_GDT
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0100h
mov ax,0003h
int 10h
xor eax,eax
mov ax,cs
shl eax,4
add eax,LABEL_SEG_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 [GdtPtr+2],eax
lgdt [GdtPtr]
cli
in al,92h
or al,00000010b
out 92h,al
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectorCode32:0
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax,SelectorVideo
mov gs,ax
mov edi,(80*11+39)*2
mov ah,0ch
mov al,'p'
mov [gs:edi],ax
jmp $
SegCode32Len equ $-LABEL_SEG_CODE32
運(yùn)行截圖:
后續(xù)
很憋屈的是,這段代碼依然是放在引導(dǎo)扇區(qū)里面的。所以如果你用bximage新創(chuàng)建了一個(gè)1.44mb大小的img的化,需要在尾部寫(xiě)入0xaa55以讓cpu識(shí)別到引導(dǎo)扇區(qū),否則就會(huì)報(bào) no bootable device的錯(cuò)
第一次寫(xiě)OS相關(guān)的博客,可能會(huì)有一些地方出錯(cuò)。。。
總結(jié)
以上是生活随笔為你收集整理的操作系统:GDT简介&实模式到保护模式的跳转的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 无所适从是什么意思(瑕不掩瑜是什么意思)
- 下一篇: 杭州端午节有什么风俗