重拾保護(hù)模式----整理一下筆記
標(biāo)題可能起的有點(diǎn)大了。總之本文是對進(jìn)入保護(hù)模式過程的一些針對性復(fù)習(xí),主要是為了寫代碼做的準(zhǔn)備。
實(shí)模式的一些特點(diǎn)
16?位數(shù)據(jù)總線、20?位地址總線,1MB?尋址能力。
尋址方式:Physical?Address?=?Segment *?16?+?Offset
實(shí)模式的進(jìn)化
原因:自?80386?時(shí)代開始,Intel?的?CPU?進(jìn)化到?32?位。尋址能力上升到?4GB。
解決方法:保護(hù)模式下的地址依然使用Segment:Offset的形式來表示,但其中段的含義發(fā)生了變化:段值的含義由原來的地址的一部分(這么稱呼是因?yàn)閷?shí)模式的段值直接參與了地址的計(jì)算)變?yōu)橐粋€數(shù)據(jù)結(jié)構(gòu)表(GDT)的其中一個表項(xiàng)(描述符)的指針。該結(jié)構(gòu)包含一些屬性,用于描述段的一些性質(zhì)。
GDT?相關(guān)
每個?GDT?描述符占?8?字節(jié),包括了段屬性、段基地址、段界限三個部分。其中基址和界限根據(jù)名字就可理解,屬性一共使用了?2?字節(jié)(里面有?4bit?是段界限的內(nèi)容。由于歷史原因,GDT?描述符的結(jié)構(gòu)很混亂)來表示段的一些特征。(用電視分割線來標(biāo)注代碼了)

;從上到下對應(yīng)地址從低到高
%macro?Descriptor?3
????dw??%2?&?0FFFFh?????????????;?段界限1
????dw??%1?&?0FFFFh?????????????;?段基址1
????db??(%1?>>?16)?&?0FFh???????;?段基址2
????dw??((%2?>>?8)?&?0F00h)?|?(%3?&?0F0FFh);?屬性1?+?段界限2?+?屬性2
????db??(%1?>>?24)?&?0FFh???????;?段基址3
%endmacro?;?共?8?字節(jié)

上述代碼利用?NASM?的宏定義了一個描述符的數(shù)據(jù)結(jié)構(gòu)。
這里提一下?NASM?的語法,Descriptor?3中的?3?表示數(shù)據(jù)結(jié)構(gòu)里有三個變量需要傳入?yún)?shù)(用%1,%2,%3?來表示)。使用時(shí),按照下列代碼的格式就可以傳入?yún)?shù)完整構(gòu)造一個實(shí)例化的描述符結(jié)構(gòu)。

LABEL_GDT:??????????Descriptor???????0,????????????????0,????????????0
LABEL_DESC_CODE32:??Descriptor???????0,?SegCode32Len?-?1,?DA_C?+?DA_32
;B8000h處為顯存基地址
LABEL_DESC_VIDEO:???Descriptor?0B8000h,???????????0ffffh,???????DA_DRW

上面的代碼創(chuàng)建了三個描述符,對應(yīng)定義了三個段。

mov?ax,?SelectorVideo
mov?gs,?ax??????????;?視頻段選擇子(目的)
============分割線======================
SelectorVideo???????equ?LABEL_DESC_VIDEO?-?LABEL_GDT
============分割線======================
mov?[gs:edi],???ax

前兩行將SelectorVideo移動到了?gs?中,最下面那一行代碼的`[gs:edi]`實(shí)際上就等于[SelectorVideo:edi]。
一個選擇子結(jié)構(gòu)的?3-15?位是描述符的索引,根據(jù)定義可以看到是相對于一個描述符相對于?LABEL_GDT?的偏移量。選擇子指向一個段描述符,正如上節(jié)“實(shí)模式的進(jìn)化”中提到的那樣。
上述的尋址計(jì)算過程結(jié)束后將得到一個線性地址。總體尋址過程可以描述為:`SEG:OFFSET?<=>?GDT.BASE?+?OFFSET`,其中`GDT.BASE`正是由`SEG`索引得到。

[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??????????;左移4位,代碼段首地址
????add?eax,?LABEL_SEG_CODE32???????;eax保存描述符地址
????mov?word?[LABEL_DESC_CODE32?+?2],?ax
????shr?eax,?16
????mov?byte?[LABEL_DESC_CODE32?+?4],?al
????mov?byte?[LABEL_DESC_CODE32?+?7],?ah????;使用32位存放描述符地址
????;?為加載?GDTR?作準(zhǔn)備
????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]
????;?關(guān)中斷
????cli
????;?打開地址線A20
????in??al,?92h
????or??al,?00000010b
????out?92h,?al
????;?準(zhǔn)備切換到保護(hù)模式
????mov?eax,?cr0
????or??eax,?1
????mov?cr0,?eax
????;?真正進(jìn)入保護(hù)模式
????jmp?dword?SelectorCode32:0??;?執(zhí)行這一句會把?SelectorCode32?裝入?cs,
????????????????????????????????;?并跳轉(zhuǎn)到?Code32Selector:0??處
;?END?of?[SECTION?.s16]

上面一段代碼為進(jìn)入保護(hù)模式做了一些準(zhǔn)備工作:
初始化保護(hù)模式代碼段:首先把.s32代碼段的物理地址存入eax,接著將這個地址拆分存入32位代碼段描述符,這里只操作了DESC_CODE32的段基址,段界限和屬性在前面已經(jīng)定義過。后面的`xor?eax,?eax`意味著DESC_CODE32已經(jīng)完成了賦值,現(xiàn)在該描述符已經(jīng)可以描述保護(hù)模式下的地址了。
接下來是將GDT基地址復(fù)制到gdtptr這個結(jié)構(gòu)中(gdtptr的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)和gdtr寄存器的訪問方式是完全一致的),然后使用lgdt指令將指針?biāo)腿雊dtr寄存器,這步結(jié)束后,就可以通過寄存器很方便地訪問GDT了。
A20
關(guān)于這部分的詳細(xì)介紹在ucore第一章的筆記里。(https://bwonsamdi.github.io 專欄投稿貌似不能加站外鏈接)
對于實(shí)模式的地址,其最大尋址空間為1MB,但可表示的最大地址為FFFF:FFFF(即0x10ffef),比1MB多了0xffef,實(shí)模式在遇到這種情況時(shí)會采取回滾機(jī)制。
隨著計(jì)算機(jī)的發(fā)展,地址線位數(shù)增加后,既要允許更大的地址空間訪問,又要保證對原20根地址線的兼容,因此IBM使用鍵盤控制器來控制第20根地址線,具體控制過程不再描述。
進(jìn)入保護(hù)模式
首先把CR0寄存器第0位置1,表示CPU切換到保護(hù)模式,此時(shí)cs的值仍為實(shí)模式的地址。
接下來將事先初始化后的32位代碼段選擇子復(fù)制到CS中,這個過程借助jmp指令實(shí)現(xiàn)。這條指令本身處于16位的代碼段,但是跳轉(zhuǎn)目的地是32位的地址,因此諸如SelectorCode32:0x11112222會被截?cái)喔呶?,只剩?x2222。
在Linux中,對于上述問題的解決方案,采用了使用DB直接寫入機(jī)器碼強(qiáng)制執(zhí)行的方法,在NASM中,支持利用jmp???dword?SelectorCode32:0的方式(加上dw)來解決。
特權(quán)級
Current?Privilege?Level
CPL是當(dāng)前執(zhí)行的程序或任務(wù)的特權(quán)級,存儲在CS和SS的0位和1位上,通常情況下CPL等于代碼所在段的特權(quán)級。當(dāng)程序轉(zhuǎn)移到不同特權(quán)級的代碼段時(shí),CPL將會改變。
一致代碼段可以被相同或更低級的代碼訪問。訪問與CPL不同的一致代碼段時(shí),CPL不會改變。
Descriptor?Privilege?Level
DPL表示段或門的特權(quán)級,存儲在段描述符或門描述符的DPL字段中。當(dāng)前代碼段訪問段或門時(shí),DPL會與CPL、段或門選擇子的RPL進(jìn)行比較,根據(jù)段或門的類型,DPL會被區(qū)別對待
數(shù)據(jù)段:DPL規(guī)定了可以訪問該段的最低特權(quán)級。若數(shù)據(jù)段的DPL為1,則CPL為0或1的程序可以訪問此段。
沒有調(diào)用門的非一致代碼段:DPL規(guī)定了可以訪問該段的特權(quán)級。
調(diào)用門:與數(shù)據(jù)段一致。
一致代碼段/通過調(diào)用門訪問的非一致代碼段:DPL規(guī)定了可以訪問該段的最高特權(quán)級。若段DPL為1,則CPL為2或3的低特權(quán)代碼段可以訪問該段。
TSS:和數(shù)據(jù)段一致。
Requested?Privilege?Level
RPL是段選擇子的0位和1位,表示當(dāng)前進(jìn)程想要的請求權(quán)限。通過RPL和CPL可以確認(rèn)訪問請求的合法性。即便段的特權(quán)級足夠,也要考慮RPL的級別,即訪問合法性的判斷取決于CPL和RPL中特權(quán)級最低的那個(數(shù)字最大)。
操作系統(tǒng)使用RPL避免低特權(quán)級應(yīng)用程序訪問高特權(quán)級段數(shù)據(jù)。當(dāng)被調(diào)用過程(操作系統(tǒng)過程)從調(diào)用過程(應(yīng)用程序)接收到選擇子時(shí),會把選擇子的RPL設(shè)置成調(diào)用者的特權(quán)級,當(dāng)操作系統(tǒng)使用該選擇子訪問對應(yīng)段時(shí),處理器會用已經(jīng)被存到RPL的調(diào)用過程的特權(quán)級,而不是CPL進(jìn)行檢驗(yàn)。
特權(quán)級總結(jié)
調(diào)用門本質(zhì)上是一個描述符,長8字節(jié)。一個門描述了由一個選擇子和一個偏移指定的線性地址。
一般通過call/jmp加上遠(yuǎn)指針的方式訪問調(diào)用門。這個遠(yuǎn)指針的段選擇子用于指定調(diào)用門。
通過調(diào)用門進(jìn)行程序控制流的段轉(zhuǎn)移時(shí),CPU會按以下順序檢查:
?? 當(dāng)前代碼段的CPL
?? 調(diào)用門描述符的DPL
?? 調(diào)用門描述符的RPL
?? 目的代碼段描述符的DPL
?? 目的代碼段描述符的一致性標(biāo)志C
?? 同時(shí),對于call和jmp,優(yōu)先級檢查規(guī)則也不同:
對于call指令
CPL<=調(diào)用門描述符DPL
RPL<=調(diào)用門描述符DPL
CPL>=目的代碼段描述符DPL
對于jmp指令
CPL<=調(diào)用門描述符DPL
RPL<=調(diào)用門描述符DPL
如果目的代碼段為一致代碼段:CPL>=目的代碼段描述符DPL
如果目的代碼段為非一致代碼段:CPL=目的代碼段描述符DPL
4.?調(diào)用門的作用是使得一個代碼段被不同特權(quán)級的程序訪問。
5.?一致代碼段:屬于內(nèi)核,允許用戶訪問的代碼段。對于一致性代碼來說,特權(quán)級高的程序不允許訪問特權(quán)級低的數(shù)據(jù)(內(nèi)核不能訪問用戶數(shù)據(jù)),特權(quán)級低的代碼可以訪問到特權(quán)級高的數(shù)據(jù),但訪問中**特權(quán)級不會改變**,訪問內(nèi)核的程序依然屬于用戶態(tài)。
6.?非一致性代碼段:僅允許同級訪問,內(nèi)核態(tài)只能訪問內(nèi)核代碼,用戶態(tài)只能訪問用戶代碼。
7.?通常低特權(quán)代碼必須通過門來實(shí)現(xiàn)對高特權(quán)代碼的訪問。
8.?RPL說明的是進(jìn)程對段訪問的請求權(quán)限,意思是當(dāng)前進(jìn)程想要的請求權(quán)限。RPL的值由程序員自己來自由的設(shè)置,并不一定RPL>=CPL,
9.?當(dāng)RPL?<?CPL時(shí),實(shí)際起作用的就是CPL了。當(dāng)選擇子成功裝入CS寄存器后,相應(yīng)的選擇子中的RPL就變成了CPL。
10.?在call調(diào)用門時(shí),需要進(jìn)行堆棧轉(zhuǎn)移,堆棧轉(zhuǎn)移需要借助TSS實(shí)現(xiàn)。
11.?TSS是一個包含了多個字段的數(shù)據(jù)結(jié)構(gòu),其中包括了ring0-ring3的棧區(qū)指針,利用這個結(jié)構(gòu),可以將棧參數(shù)復(fù)制。
后記
關(guān)于特權(quán)級的部分,看得不是很詳細(xì),以后需要再特別寫一篇文章來按位解析GDT、選擇子、調(diào)用門、TSS這些數(shù)據(jù)結(jié)構(gòu),否則在理解一些機(jī)制的時(shí)候常常會暈頭轉(zhuǎn)向。這一部分的一些細(xì)節(jié)的地方就暫時(shí)擱置起來,目前距離脫離匯編進(jìn)入C編程還有很長一段路要走。
參考
《自己動手寫操作系統(tǒng)》
https://www.cnblogs.com/bamboos/archive/2009/03/26/1422041.html

專欄投稿屬實(shí)難用,什么時(shí)候可以支持markdown啊......
另外github求一波關(guān)注......最近在做xv6的實(shí)驗(yàn),不過暫時(shí)還沒有放到遠(yuǎn)程倉庫里。
近期的目標(biāo)是xv6實(shí)驗(yàn)和linux kernel的實(shí)驗(yàn)
五月份開始學(xué)校就沒課了,那個時(shí)候該專門琢磨考研了。要是大家能多投幾個幣,說不定我就有動力把實(shí)驗(yàn)都做完了呢

要是看到這里了,就投個票吧
