一、80386特權級保護介紹
80386CPU為了給作業系統提供硬體級的可靠保護,提供了特權級保護功能。80386處於保護模式時,會改變CPU的行為方式,其中便包括開啟特權級保護。實現良好的特權級保護是需要軟硬體相協調的,CPU提供硬體機制的同時也需要與作業系統相配合,共同實現完善的特權級保護功能。
要較為全面的理解特權級保護的工作原理,需要了解相互關聯的各個機制。下面會介紹在80386特權級保護中起到關鍵作用的:段描述符和描述符表、保護模式下的記憶體訪問方式、特權級的不同維度與特權級校驗規則等內容。
二、段描述符和描述符表
在8086中,為了能夠讓程式在記憶體中浮動的載入裝配,通過段地址和段內偏移地址共同組成最終的實體地址。而在保護模式下,段機制依然存在,只不過為了支援特權級保護,在一個段能夠被訪問之前,需要先進行"登記註冊"。
用於登記註冊一個段詳細資訊的資料結構被稱為段描述符(Segment Descripter),固定佔8個位元組,64位。在一個多工系統中會定義很多不同的段,一個段就對應著一個段描述符,為了統一的進行管理,需要在記憶體中開闢一段連續的空間集中存放緊密相連的段描述符,而這一段連續的記憶體空間被稱為描述符表。
描述符表有多種型別,如全域性描述符表(Global Descripter Table GDT)、區域性描述符(Local Descripter Table LDT)、和中斷描述符表 (Interrupt Descriptor Table IDT)等。
段描述符
段描述符佔8個位元組,共64位。其結構如下圖所示,上半部分是高32位、下半部分是低32位。
(截圖自 x86組合語言 從真實模式到保護模式)
段基地址
段基地址是一共32位的線性地址。被分為了物理上不連續的兩段是因為之前的80286保護模式定址是24位的,而為了和80286的相容便只能在之前的段描述符設計上進行擴充。
在沒有開啟頁機制的情況下,這32位的段基地址就是最終的實體地址。
段界限
段界限一共20位。被分為兩段的原因和段基地址一樣,都是為了相容之前80286的48位段描述符。段界限用於控制段的擴充範圍,在訪問時,段內偏移超過段界限時會引發錯誤。
對於向上擴充的段,如資料段、程式碼段來說,偏移量從0開始遞增,段界限決定了段內偏移量的最大值。對於向下擴充的段,如棧段來說,偏移量從0開始遞減,段界限決定了段內偏移量的最小值。
有了段界限這一限制,在使用訪問段時,就能及時發現程式中對記憶體段訪問的越界行為。更重要的是,段界限的限制能夠防止程式越界,訪問原本不屬於該程式,不能被其訪問的記憶體空間。
G位
G位(Granularity)粒度位,用於解釋段界限的含義。
段描述符中的段界限是20位的,咋看之下一個段的最大空間似乎最大隻能是為20位即1MB,這和80386的最大定址空間4GB可不太匹配。
20位的段界限是80386的設計者為了相容20位定址空間而採取的相容性設計,而G粒度位便是其中的關鍵。當G位=0時,段界限以位元組為單位,段的最大界限就是(2^20)*1B=1MB;當G位=1時,段界限以4KB為單位,段的最大界限擴充套件為(2^20)*4KB=4GB,和80386的最大定址範圍相匹配,此時段界限最小也是4KB。
S位
S位(Descriptor type)型別位,用於標識當前段的型別。
當S=0時,表示當前段為系統段;當S=1時,表示當前段是一個程式碼段或資料段(棧段也被視為特殊的資料段)。
DPL欄位
DPL欄位(Descriptor Privilege Level)代表當前段描述符的特權級,佔兩bit位。
80386共支援4種特權級級別,分別是0,1,2,3。數字越小,特權級越高,0代表最高特權級,3代表最低特權級。
P位
P位(Segment Present)段存在位,用於標識當前段是否存在於記憶體中。
P=0時,代表段在記憶體中不存在;P=1時,代表段存在於記憶體之中。通常段描述符所指向的段總是位於記憶體中的。但有時實體記憶體空間緊張時,可能只是預先構造了段描述符,而沒有分配使用對應的記憶體空間;或者有的作業系統會在記憶體緊張時將不常用的段暫時的互動到磁碟中,騰出記憶體給當前正在執行的程式,以實現段式虛擬記憶體。
上述情況下,段描述符的P位就應該被置為0。CPU在對段描述符所指向的段進行訪問時,會對P位進行校驗。當發現P=0時,CPU會發出一個異常中斷,作業系統應該提供對應的中斷處理程式用於將對應記憶體段從磁碟中置換入記憶體中,同時將對應段描述符的P位置為1。
TYPE欄位
TYPE欄位共4位,用於表示段描述符的子型別。
(截圖自 x86組合語言 從真實模式到保護模式)
對於資料段來說,TYPE的4位分別是X/E/W/A位,而對於程式碼段來說,TYPE的4位分別是X/C/R/A位。其中X、A位的含義是相同的,而中間的兩位由於資料段和程式碼段的性質不同,被賦予了完全不同的含義。
既然都是記憶體段,如何判斷一個段是資料段還是程式碼段呢?
事實上,一個段是資料段還是程式碼段並不取決於定義時的段描述符,而取決於當前段被用於何種場景。
如果被載入在CS程式碼段暫存器中,便被視為程式碼段;如果被載入在DS、ES、SS等資料段暫存器、棧段暫存器中,則被視為資料段。
TYPE欄位各bit位介紹
X(eXecutable)位標識當前段是否可執行。資料段總是不可執行的,因此X始終為0;程式碼段總是可執行的,因此X始終為1。
A(Accessed)位標識當前段是否已被訪問。在段描述符初始化時,應該被設定為0。當對應段被訪問時,由CPU硬體將A位設定為1。作業系統在設計虛擬記憶體管理時,通常需要通過某些演算法策略決定應該將哪些記憶體移入磁碟。段描述符中的A位可以幫助作業系統判斷某一段時間內,對應段記憶體是否被訪問過,為置換演算法提供一定的依據。A位的清零操作也由作業系統來負責。
E(Expand)位標識資料段的擴充方向。E=0代表資料段是向上,向高地址方向擴充,一般的資料段都是向上擴充的。E=1代表資料段是向下,向低地址方向擴充,這裡主要指的就是棧段。因為入棧時,棧頂指標是自減的。
W(Writeable)位標識資料段是否可寫。資料段總是可讀的,但是不一定可寫 。W=0時,代表當前資料段不可寫,如果對當前段有寫入指令執行時,會引發CPU異常中斷。W=1時,資料段可讀可寫。
C(Confirming)位標識程式碼段是否是特權級依從的。C=0表示當前程式碼段是非依從的,意味著當前程式碼段只能被相同特權級的程式呼叫(或者門呼叫);C=1表示當前程式碼段是依從的,意味著當前程式碼段允許被更低特權級的程式呼叫。
關於特權級依從和門呼叫的概念,會在後面進行進一步介紹。
R(ReadAble)位標識程式碼段是否是可讀的。程式碼段總是可以執行的,但80386不允許對一個程式碼段進行寫入(如果要對程式碼段中的內容進行修改,應該改用一個可寫的資料段指向對應的記憶體空間)。同時,80386還對程式碼段的可讀性做了限制。R=0時,代表該程式碼段是不可讀的,定址R=0的程式碼段將會引發處理器異常中斷;R=1時,代表該程式碼段是可讀的。這裡的不可讀,不是指CPU無法讀取記憶體中的指令內容,而是用於限制程式軟體的行為,例如在記憶體定址中使用段超越字首"CS:"來定址訪問程式碼段中的內容。
全域性描述符表GDT
描述符表中最核心的是全域性描述符表。從名稱中的全域性二字可知,全域性描述符表在80386CPU執行時是為整個軟硬體提供服務的,只能存在一個。在進入保護模式前,需要事先定義好全域性描述符表。
為了讓CPU能夠在訪問段的時候隨時定位並讀取到全域性描述符表中的資料,80386提供了全域性描述符表暫存器(Global Descripter Table Register GDTR)。
GDTR是48位的,高位的32位用於存放全域性描述符表的起始線性地址,低位的16位用於存放GDT的界限。對GDTR賦值的彙編命令為:lgdt m16&32。
32位的起始線性地址,最大可定址4GB,意味著理論上GDT可以被定義在記憶體的任意位置。由於進入保護模式前需要先定義GDT,因此GDT一般被設定在真實模式下可定址的1MB內的低地址(進入保護模式後也可重新定義GDT)。
16位的界限值最大為64KB。由於段描述符大小為8個位元組(8B),因此GDT中可以存放的段描述符的上限為2^10^8=8192個,這在多數情況下是綽綽有餘的。
區域性描述符表LDT
現代的作業系統是多工的,那麼什麼是任務呢?
程式是儲存在儲存介質中的指令和資料的結合體,而正在執行程式的一個副本就是任務。一個程式可以有執行在記憶體中的多個副本,每個副本都是一個任務。支援多工的作業系統要讓併發的多個任務和諧共處,就需要在任務之間通過一些手段令不同任務間彼此隔離。一般情況下,不同任務之間的記憶體是不互通的,每個任務都有自己的記憶體空間,一個任務不能隨意訪問另一個任務獨有的記憶體空間。
80386CPU的設計者建議為每個任務分配獨有的描述符表,這一描述符表被稱為區域性描述符表LDT。每個任務私有的段,其段描述符不放在GDT中,而是放在LDT中。
CPU通過可以通過GDTR暫存器來找到GDT所在的位置。同樣的,80386提供了區域性描述符表暫存器(Local Descripter Table Register LDTR)來追蹤LDT的位置。
和GDT不同的是,每個任務都有著自己的LDT。在多工輪流執行的多工系統中,正在執行的任務被成為當前任務,而LDTR則指向當前任務的LDT。任務切換時,LDTR中的資料會發生變化,指向新的當前任務。
三、保護模式下的記憶體訪問方式
前面介紹了段描述符和GDT,下面說明80386CPU在定址時是如何與段描述符、GDT互動的。
在8086中,段暫存器存放的是段基址。CPU進行記憶體定址時,16位的段基址左移4位與16位的偏移地址相加生成最終的實體地址。而在80386中,定址方式發生了一定的變化。
80386中,段暫存器被擴充套件為了兩部分。真實模式下,80386段暫存器只有16位的前半部分參與工作,使用方式和8086的16位段暫存器無異,可以相容的執行8086程式。而在保護模式下,使用段暫存器進行記憶體定址的方式發生了變化。此時,前半部分16位裝載的不再是段基址,而是一種被稱為段選擇子的資料結構,保護模式下段暫存器的前半部分被稱為段選擇器;後半部分用於儲存所載入的段描述符相關資料,被稱為描述符高速緩衝器。
此外,80386在8086的段暫存器CS/DS/ES/SS的基礎上,還新增了兩個資料段暫存器FS和GS,為複雜彙編程式的開發提供了更好的支援。
(截圖自 x86組合語言 從真實模式到保護模式)
什麼是段選擇子?
前面說到,保護模式下段暫存器中裝載的不再是段基址,而是段選擇子。但CPU從本質上來說依然是通過段基址+偏移地址進行記憶體定址的,只不過在中間引入了一層段選擇子的抽象,用於實現特權級保護。
段選擇子是一個16位的資料結構,由三部分組成:佔高13位的段描述符索引、TI(Table Indicator)描述符表指示器以及RPL(Request Privilege Level)請求特權級。
(截圖自 x86組合語言 從真實模式到保護模式)
描述符索引
段描述符索引用於在段描述符表中定位對應的段描述符。如果將段描述符表看作一個結構體陣列,那麼描述符索引就是陣列的下標。
假如段暫存器載入的是描述符表中第3個段描述符所對應的段,那當前段描述符索引的值就是0000000000010(下標從0開始)。段描述符表所能容納的最大描述符個數是8192個,13位的描述符索引恰好能夠與之一一對應。
描述符表指示器TI
TI用於標識當前段描述符位於何種描述符表中。TI=0時,表示當前段位於GDT中;TI=1時,表示當前段位於LDT中。根據TI的不同,在載入段選擇子時,CPU將會去訪問對應的段描述符表,根據描述符索引獲取對應的段描述符資訊,載入到段暫存器中。
由於段選擇子中並沒有段基址、段界限等記憶體定址時的關鍵資料,這些資料都只在段選擇子指向的段描述符中。但每次使用段暫存器定址時,不能總是通過段描述符表獲取段描述符資料,頻繁的記憶體定址效率太低。
因此,80386的設計者在段暫存器中設定了描述符高速緩衝器。只有當段選擇子變化時,段暫存器才需要訪問一次描述符表,獲取對應的段描述符資料,將其存入描述符高速緩衝器中。之後,對於當前同一段選擇子的訪問,便可以直接從描述符高速緩衝器中獲取資料,極大的提高了CPU通過段暫存器進行記憶體定址的效能。
描述符高速緩衝器和儲存器快取記憶體一樣,是純硬體控制的,無法通過程式直接訪問、修改其中的資料。
請求特權級RPL
RPL請求特權級,標識著提供段選擇子的程式的特權級別。RPL的作用很難單獨拎出來說明,會在接下來的段特權級訪問保護機制中進行介紹。
GDT和LDT的關係
為了更好的保護每個任務的LDT,防止其被其它任務隨意訪問,需要將每個任務的LDT視為一個需要進行特權級保護的段,將每個任務的LDT都註冊到GDT中。
每個任務的LDT,都有一個在GDT中的段描述符與之對應(在段描述符中S位=1的系統段)。80386CPU的LDTR被設計為16位,其中存放的是對應LDT的段選擇子,當任務切換時,只需切換LDTR中的段選擇子即可。
通過段選擇子尋找對應段時,如果段選擇子中的TI=0,代表所要尋找的段描述符在GDT中。CPU根據GDTR中的資料,找到GDT,並按照下標計算偏移量,獲取對應的段描述符。如果段選擇子中的TI=1,代表所要尋找的段描述符在LDT中,CPU先根據當前LDTR中的段選擇子去GDT中尋找對應的LDT段描述符,從中獲取當前LDT的線性基地址。接著,再通過所請求的段選擇子中的描述符索引在LDT中查詢最終所需的段描述符。
四、特權級的三個維度
前面介紹了許多用於實現特權級保護的機制,現在終於可以開始說明80386究竟是如何利用這些機制來完成特權級保護的。
特權級保護從本質上來說,是保護高特權級的記憶體、外設等資源不會被沒有許可權(低許可權)的程式訪問。主體結構是程式訪問資源,而CPU需要在這個過程中進行特權級的校驗。
這裡引入三種不同概念的特權級:當前特權級CPL、描述符特權級DPL、請求特權級RPL。
CPL當前特權級(Current Privilege Level)
CPL當前特權級,用於表示當前所執行程式的特權級。更進一步的說,也就是當前CS程式碼段暫存器中所裝載段選擇子的後兩位所決定的特權級。
BIOS在載入作業系統並進入保護模式時,處理器會在執行第一條指令時自動的將CPL設定為0,可以看做作業系統在進入保護模式時擁有的最高CPL是從處理器繼承而來。之後便由作業系統程式負責整個計算機系統的管理,例如載入使用者應用程式時,將使用者程式的CPL設定為最低特權級3。應用程式雖然不希望自己被放在最低特權級,但作業系統主導了應用程式的載入,其所使用的段描述符、LDT等都由作業系統建立和管理,應用程式只能專注於自己的業務功能,無權控制自己的CPL當前特權級。
特權級分為4種,可以被看做幾個不同大小的同心圓,像一個個的指環,特權級0也被稱為ring0。因此執行在核心處ring0特權級的作業系統程式,也被稱為核心(Kernel)程式。
(截圖自 x86組合語言 從真實模式到保護模式)
特權指令
80386CPU提供了一系列的機制實現特權級保護,如段描述符、GDT等等。全域性描述符表GDT是特權級保護機制中的一個關鍵要素,通過指令lgdt可以進行GDT的設定。可如果本應該被高許可權的作業系統管理起來的低特權級程式(CPL=3)也能執行lgdt指令的話,整個特權級保護機制就像一幢巨集偉的高樓被抽離了地基,變得脆弱不堪。
80386的設計者自然不會設計出這種百密一疏的方案。因此,在整個80386的指令集中,其中很多底層的、許可權很大的指令被規定只能被最高許可權的程式(例如作業系統)執行。讓CPU停機也是通過指令來完成的,如果應用程式也能隨意的執行停機指令,那將是非常恐怖的事情。
只有處於最高當前特權級CPL=0的程式才有許可權執行的指令,叫做特權指令。其中主要包括停機指令;載入GDT、LDT的指令;讀寫控制暫存器的mov指令等等。
DPL描述符特權級(Descriptor Privilege Level)
DPL目標特權級,用於標識所指向目標的特權級。前面提到過,每個段描述符都有DPL欄位屬性。DPL特權級的高低,決定了能夠被位於何種特權級的程式所訪問。
CPU對記憶體段訪問的特權級保護是在段選擇子載入的時進行的。當有新的段選擇子準備載入到段暫存器時,CPU會根據段暫存器的型別進行相應的校驗。
程式碼段訪問的保護機制
對於程式碼段暫存器的來說,載入新的段選擇子可能意味著CPL的變化,校驗比較嚴格,段間控制轉移一般只允許發生在相同特權級的程式之間。也就是說,一個當前特權級CPL為2的程式,只能跳轉到另一個特權級DPL同樣為2的程式碼段執行,而無法跳轉到特權級DPL為0、1、3的程式碼段。一般程式內部相同特權級程式碼段間互相跳轉都是沒問題的,但還存在一些場景需要允許低特權級的程式去呼叫高特權級的程式碼:例如低CPL的應用程式去呼叫高DPL的系統呼叫例程。
有兩種方法允許低CPL的程式跳轉高DPL的程式碼段:
一是將高特權級的目的碼段定義為依從的,也就是將程式碼段描述符中TYPE欄位的C位設定為1,代表當前程式碼段是特權級依從的。當低特權級的程式跳轉至高特權級的程式碼段時,CS的後兩位不發生變化,CPL和呼叫程式保持一致。
二是通過門來進行,門(Gate)也是一種描述符,被稱為門描述符。門描述符區別於段描述符,段描述符描述的是一個段,而門描述符描述的是一段可執行的程式碼、一個程式或者一個任務,系統呼叫通常使用門描述符來實現。使用jmp far指令可以將控制通過門呼叫轉移到高特權級程式碼段,但是依然不改變當前特權級CPL;使用call far指令則在將控制轉移到高特權級程式碼段的同時,還會將當前特權級CPL提升到和目的碼段DPL一致,也就是說,一個CPL為3的應用程式,通過門呼叫呼叫到了一個DPL=0的程式碼段程式,則CPU將會將當前特權級CPL提升為0,和目的碼段特權級保持一致。
資料段訪問的保護機制
資料段訪問的保護機制相對來說簡單一些:處於低CPL的程式無法訪問高DPL的資料段。換句話說,在訪問資料段前,向資料段暫存器(DS/ES/FS/GS)載入新的段選擇子時,要求當前CPL必須高於或等於目標段DPL(數值上CPL <= DPL)。
舉個例子,在古代等級森嚴的封建制度下,皇帝可以認為是位於CPL=0的級別,普天之下莫非王土,皇帝可以在自己的國家訪問任何它想要訪問的領地(資料段)。但反過來位於CPL=3的平民是沒法去直接訪問皇宮的(DPL=0的資料段)。位於中間級別的CPL=1、2的程式就像地方諸侯,CPL=1的諸侯雖然也無法直接訪問皇宮,但對於自己的寢宮(DPL=1的資料段)和平民的家(DPL=3的資料段),都是有權利訪問的。
特別的,為了避免高特權級的程式由於棧空間不足而崩潰,處理器在特權級變化的時候,堆疊也會跟著發生變化。所以,向棧段暫存器SS載入新的段選擇子時,要求當前CPL必須完全等於目標的DPL(在數值上CPL = DPL)。
到了這裡,看起來80386的記憶體保護機制似乎已經很完善了。高特權級ring0的作業系統和低特權級ring3的應用程式彼此之間被特權級保護機制隔離開了,低特權級的應用程式不能隨意的訪問高特權級的作業系統記憶體;沒有提供對應的門描述符或者依從程式碼段,應用程式也無法呼叫高特權級的程式。
那麼80386的設計者提供的請求特權級RPL作用又是什麼呢?
RPL請求特權級(Request Privilege Level)
RPL請求特權級,代表請求者的特權級。在執行段間控制跳轉指令時,需要提供目的碼段的選擇子,載入CS程式碼段暫存器;在訪問資料段時,也需要將資料段選擇子裝載入DS、ES等資料段暫存器中。無論是執行控制轉移,還是訪問資料段,都可以看作是當前執行任務的一個請求,RPL也就是當前請求者的特權級。
大多數情況下,請求者就是當前任務,因此CPL=RPL。誰負責提供段選擇子,誰就是請求者。但在某些時刻,提供段選擇子的請求者和當前任務並不相同。
我們知道使用call far轉移指令呼叫作業系統提供的呼叫門執行系統呼叫時,會將CPL從應用程式的ring3提高到作業系統所處的ring0。
假如作業系統提供了一個系統呼叫,用於從磁碟中讀取資料,並將其寫入到應用程式資料段的指定位置中(由於系統呼叫中可能會執行一些特權指令,或是外設被限制了訪問特權級,所以通過呼叫門call far時會提升CPL)。這個系統呼叫有三個引數:磁碟的扇區號(指定從磁碟的什麼位置讀取),需要寫入的資料段的段選擇子(指定寫入哪一資料段),最後一個是資料段的段內偏移地址(用於更精確的控制寫入資料的段內位置)。
這個系統呼叫的設計看起來還不錯,能讀取指定磁碟扇區的資料並寫入指定資料段中,但卻隱藏了一個嚴重問題。
如果應用程式的編寫者是一名惡意的攻擊者,他給出的資料段選擇子引數指向的不是應用程式自己的資料段,而是作業系統的資料段選擇子。雖然只有CPL=0的程式才有許可權訪問作業系統設定的DPL=0的核心資料段,但是由於通過call far呼叫門進行系統呼叫時,會將CPL提升為0,因此這個操作會被允許執行。這是一個很嚴重的漏洞,通過call far呼叫門實現的系統呼叫,模糊了CPL和事實上的請求者特權級的關係,使得只有CPL、DPL的校驗機制在這種情況下顯得無能為力。CPU很難區分出在段選擇子的載入時,這個段選擇子究竟是作業系統提供的還是惡意應用程式提供的。
因此,80386的設計者在CPL、DPL的基礎上又提供了RPL請求特權級來解決這個問題。雖然CPU不知道段選擇子的提供者是誰,但作業系統是知道的。作業系統在核心中訪問記憶體段時,請求者自然是作業系統自己;而作業系統提供系統呼叫為應用程式服務時,也能明確知道請求者是低特權級的應用程式。
在上述磁碟讀取系統呼叫的例子中,作業系統可以在系統呼叫的程式中修改應用程式提供的段選擇子的RPL,將其設定為和應用程式匹配的低特權級後再送入段暫存器中。CPU在校驗時,除了要求CPL高於或等於目標段的DPL(數值上CPL <= 目標DPL),也要求給出的段選擇子中的RPL也必須高於或等於目標段的DPL(數值上RPL <= 目標DPL)。
引入了RPL後,並且作業系統在系統呼叫中合理的設定了段選擇子的RPL,上述漏洞就不復存在了。正常的應用程式能夠訪問磁碟資料,並正確的寫入自己的資料段中(CPL=0 RPL=3,目標DPL=3 校驗通過);但惡意的應用程式即使傳入的段選擇子RPL=0,也會被系統呼叫給重置為RPL=3,非法訪問作業系統核心資料段的企圖將會被CPU發現,引發異常中斷(CPL=0 RPL=3,DPL=0 校驗不通過)。
五、記憶體特權級保護校驗規則
前面的舉例分析中,或多或少的介紹了幾種記憶體訪問時的特權級保護校驗規則,這裡系統的總結一下。記憶體特權級保護的規則根據記憶體段的性質不同,有一定差異,分情況討論。
程式碼段特權級校驗規則
非特權級依從程式碼段直接轉移: 直接控制轉移到非特權級依從的程式碼段時,要求當前特權級CPL、請求特權級RPL都等於目的碼段DPL(數值上CPL = 目的碼段DPL,RPL = 目的碼段DPL)。
特權級依從程式碼段直接轉移:直接控制轉移到特權級依從的程式碼段時,要求當前特權級CPL、請求特權級RPL都低於或等於目的碼段DPL(數值上CPL >= 目的碼段DPL,RPL >= 目的碼段DPL)。
門描述符特權轉移:通過門描述符進行的控制轉移規則較為複雜,在後續關於門描述符的部落格再對門描述符控制轉移的規則再進行展開介紹。
資料段特權級校驗規則
CPU允許高當前特權級的程式訪問低特權級別的資料段。換句話說,低當前特權級的程式無法訪問高特權級的資料段。
即要求當前特權級CPL,請求特權級RPL都必須高於或等於目標資料段DPL(數值上CPL <= 目標資料段DPL,RPL <= 目標資料段DPL)。
棧段特權級校驗規則
CPU要求任何時候,訪問的棧段特權級必須和當前特權級CPL相一致。
即要求當前特權級CPL,請求特權級RPL都必須等於目標棧段DPL(數值上CPL = 目標棧段DPL,RPL = 目標棧段DPL)。
六、總結
在通過學習《x86組合語言 從真實模式到保護模式》以及有關內容的部落格,掌握了一定的80386硬體及彙編知識後,我才具備了閱讀、學習ucore作業系統原始碼的基礎。雖然《x86組合語言 從真實模式到保護模式》的作者在很多地方都很體貼的站在初學者的角度來講解原理,但一方面由於自己在閱讀時不夠專注,另一方面也和涉及到的知識點繁多且關係緊密有關。對我而言,單純通過閱讀學習的效果並不是特別理想。
《暗時間》一書中提到了兩個很有價值的觀點:書寫是為了更好的思考、教是更好的學。在寫部落格的過程中,會不自覺的反覆思考並總結所寫的內容,找到那些自以為了解但事實上卻理解不夠深刻的內容。同時假設有一個虛擬的初學者會閱讀所寫的部落格,換位思考這個虛擬的初學者可能遇到的問題,儘可能的將內容以淺顯易懂的方式說清楚。當然,如果部落格能幫助到和我一樣,對CPU硬體、作業系統原理感興趣的小夥伴就更好了。
對於學習基於x86保護模式的ucore乃至流行的Linux、Windows等作業系統來說,x86CPU的特權級保護機制是至關重要的一部分。有了硬體級別的特權級保護機制,才能實現多工的隔離以及各種軟體級別的許可權控制。後續的80386學習相關部落格將會包括門描述符的詳細介紹以及分頁機制、虛擬記憶體管理等相關的知識點,這些內容都是學習ucore作業系統公開課時所必須的。
作為一個初學者,部落格中如有錯誤或者理解不到位的地方,還請指正。