亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

《Orange’s 一個操作系統的實現》3.保護模式5-

系統 2212 0

學習過程中遇到一個對保護模式總結很好的Blog,轉來分享一下。

先說下特權級的概念,在保護模式下,系統依靠特權級來實施代碼和數據的保護,相當于權限啦。特權級共有4個級別,0,1,2,3,數字越小表示權限越高。如圖:?

保護模式 對CPL,RPL,DPL的總結 - xuejianxinokok - xuejianxinokok的博客
???
較為核心的代碼和數據放在較高(靠內)的層級中,處理器用此來防止較低特權的任務在不被允許的情況下訪問處于高特權級的段。為了防止概念混淆,我們不用特權級大小來說明,改為內層(高),外層(低)來講。

特權級有3種 CPL , DPL RPL ,每個都是有4個等級。?
我對他們的 關系 理解是這樣:一般來說, CPL 代表當前代碼段的權限,如果它想要去訪問一個段或門,首先要看看對方的權限如何,也就是檢查對方的 DPL ,如果滿足當前的權限比要訪問的權限高,則有可能允許去訪問,有些情況我們還要檢查?
選擇子的權限,即 RPL ,因為我們通過選擇子:偏移量的方式去訪問一個段,這算是一個訪問請求動作,因此?
稱為請求訪問權限 RPL (Requst Privilege Level)。當請求權限也滿足條件,那么訪問就被允許了。
?

CPL (Current Privilege Level)?
CPL 是當前執行的任務的特權等級,它存儲在CS和SS的第0位和第1位上。(兩位表示0~3四個等級)?
通常情況下, CPL 等于代碼所在段的特權等級,當程序轉移到不同的代碼段時,處理器將改變 CPL 。?
注意:在遇到 一致代碼 段時,情況特殊, 一致代碼 段的特點是:可以被 等級相同或者更低特權級 的代碼訪問,當處理器訪問一個與當前代碼段 CPL 特權級不同的 一致代碼 段時, CPL 不會改變。?

DPL (Descriptor Privilege Level) ?
表示門或者段的特權級,存儲在門或者段的描述符的 DPL 字段中。正如上面說的那樣,當當前代碼段試圖訪問一個段或者門時,其 DPL 將會和當前特權級 CPL 以及段或門的選擇子比較,根據段或者門的類型不同, DPL 的含義不同:?
??? 1.數據段的 DPL :規定了訪問此段的最低權限。比如一個數據段的 DPL 是1,那么只有運行在 CPL 為0或1的程序才可能訪問它。為什么說可能呢?因為還有一個比較的因素是 RPL 。訪問數據段要滿足有 效特權級別 (上述) 高于數據段的 DPL .?
??? 2.非 一致代碼 段的 DPL (不使用調用門的情況): DPL 規定訪問此段的特權,只有 CPL 與之相等才有可能訪問。?
??? 3.調用門的 DPL ,規定了程序或任務訪問該門的最低權限。與數據段同。?
??? 4. 一致代碼 段和通過調用門訪問的非 一致代碼 段, DPL 規定訪問此段的最高權限。?
???? 比如一個段的 DPL 為2,那么 CPL 為0或者1的程序都無法訪問。?
?? 5. TSS的 DPL ,同數據段。?

RPL (Rquest Privilege Level)
?
RPL 是通過選擇子的低兩位來表現出來的( 這么說來,CS和SS也是存放選擇子的,同時 CPL 存放在CS和SS的低兩位上,那么對CS和SS來說,選擇子的 RPL =當前段的 CPL )。處理器通過檢查 RPL CPL 來確認一個訪問是否合法。即提出訪問的段除了有足夠的特權級 CPL ,如果 RPL 不夠也是不行的(有些情況會忽略 RPL 檢查)。?
為什么要有 RPL ?
操作系統往往通過設置 RPL 的方法來避免低特權級的應用程序訪問高特權級的內層數據。?
例子情景:調用者調用操作系統的某過程去訪問一個段。?
當操作系統(被調用過程)從應用程序(調用者)接受一個選擇子時,會把選擇子的 RPL 設置稱調用者的權限等級,于是操作系統用這個選擇子去訪問相應的段時(這時 CPL 為操作系統的等級,因為正在運行操作系統的代碼),處理器會使用調用者的特權級去進行特權級檢查,而不是正在實施訪問動作的操作系統的特權級( CPL ),這樣操作系統就不用以自己的身份去訪問(就防止了應用去訪問需要高權限的內層數據,除非應用程序本身的權限就足夠高)。?
那么 RPL 的作用就比較明顯了:因為同一時刻只能有一個 CPL ,而當低權限的應用去調用擁有至高權限的操作系統的功能來訪問一個目標段時,進入操作系統代碼段時 CPL 變成了操作系統的 CPL ,如果沒有 RPL ,那么權限檢查的時候就會用 CPL ,而這個 CPL 權限比應用程序高,也就可能去訪問需要高權限才能訪問的數據,這就不安全了。所以引入 RPL ,讓它去代表訪問權限,因此在檢查 CPL 的同時,也會檢查 RPL .一般來說如果 RPL 的數字比 CPL 大(權限比 CPL 的低),那么 RPL 會起決定性作用。?
說這么多不明白都不行啦~真累?
下面是引用的一個超棒的關于權限控制的總結:?
引用地址 ?
還有一篇文章 在此 。?

■ 數據訪問時的權限check

一、 訪問data segment時(ds、es、fs 及gs)?
1、 程序指令要訪問數據時,data segment selector 被加載進 data segment register(ds、es、fs 和 gs)前,處理器會進行一系列的權限檢查,通過了才能被加載進入segment register。處理器分為兩步進行檢查:?
★? CPL (當前程序運行的權限級別)與 RPL (位于selector中的? RPL )作比較,并設置有效權限級別為低權限的一個。?
★ 得出的有效權限級別與 DPL (segment descriptor 中的 DPL )作比較,有效權限級別高于 DPL ,那么就通過。低于就不允許訪問。
?
2、舉個例子:?
如果: CPL ?= 3、 RPL ?= 2、 DPL ?= 2。那么
EPL =? CPL ?>? RPL ??? CPL ?:? RPL ;?
if (EPL <=? DPL ) {?
/* 允許訪問 */?
} else {?
/* 失敗,#GP 異常生產,執行異常 */
}
?
或者:?
if (( CPL ?<=? DPL ) && ( RPL ?<=? DPL )) {?
/* 允許訪問 */?
} else {?
/* 失敗,#GP 異常生產,執行異常 */?
}

?? 也就是要訪問目標data segment,那么必須要有足夠的權限,這個足夠的權限就是:當前運行的權限級別及選擇子的請求權限級別要高于等于目標data segment的權限級別。

?

二、 訪問stack segment時?
1、 該問stack 時的權限檢查更嚴格, CPL RPL DPL 三者必須相等才能通過該問請求。

2、 舉個例子:

?

?

if ( CPL ?==? RPL ?&&? RPL ?==? DPL ?&&? CPL ?==? DPL ) {?
????? /* 允許訪問 */?
} else {?
???? /* 失敗,#GP 異常生產,執行異常 */?
}

?

也就是說每個權限級別有相對應的statck segment。不能越權訪問,即使高權限訪問低權限也是被拒絕的

?

?

?

■ 控制權的轉移及權限檢查。

?? 權限檢查的4個要素:

★? CPL :處理器當前運行的級別,也就是:當前 CS 的級別,在 CS 的 BIT0 ~ Bit1

★? DPL :訪問目標代碼段所需的級別。定義在 segment descriptor 的? DPL ?域中

★? RPL : 通過 selector 進行訪問時,selector 內定義的級別。

★ conforming/nonconforming:目標代碼屬于 nonconforming 還是 conforming 定義在segment descritptor 的 C 標志位中

x86 的各方面檢查依賴于目標代碼段是 nonconforming(不一致) 還是 conforming (一致) ?類型


一、 直接轉移(far call 及 far jmp)?
??? 1、 直接轉移定義為不帶gate selector或 taskselector的遠調用。當執行一條 call cs:eip 或 jmp cs:eip 指令時,cs 是目標代碼段的selector,處理器在加載指令操作數中的cs進cs register前,要進行一系列的權限檢查,控制權的轉移權限分兩部分,根據目標代碼段descriptor定義的兩種情況:?
1)、nonconforming target code segment?
★ 直接轉移后的權限級別是不能必改變的。因此, CPL ?必須要等于目標代碼段的? DPL 。?
★ 要有足夠的請求權限進行訪問。因此,目標代碼段選擇子的 RPL ?<=? CPL

2)、conforming target code segment?
★ conforming code segment 允許訪問高權限級別的代碼。這里只需檢查? CPL ?>=? DPL ?即可, RPL ?忽略不檢查。?
★ 轉移后 CPL 不會改變。

?? 2、 以上兩步通過后,處理器加載目標代碼段的CS 進入CS register,但權限級別不改變,繼而 RPL 被忽略。

★ 處理器根據CS selector在相應的descriptor table 找到 code segment descriptor。CS 的Bit2(TI域) 指示在哪個descriptor table 表查找,CS.TI = 0 時在GDT查找,CS.TI = 1時在LDT 查找。?
★ CS的Bit15~Bit3 是selector index 值,處理器基于GDT或LDT來查找segment descriptor。具體是:GDTR.base 或 LDTR.base + CS.SI × 8 得出code segment descritpro。?
★ 處理器自動加載code segment descriptor 的 base address、segment limit及attribute 域進入 CS register的相應的隱藏域。?
★ 轉到CS.base + eip 處執行指令

總結:用代碼形式來說明直接轉移 call cs:eip 這條指令?
例: call 1a:804304c (即cs = 1a, eip = 804304c)

?

?

target_cs = 1a;?
target_eip = 0x0804304c;?
CPL ?= CS. RPL ;??????????? /* 當前執行的代碼段的權限級別就是 CPL ?*/?
RPL ?= target_cs. RPL ;???? /* 目標段 selector 的低3位是 RPL ?*/?
target_si = target_cs.SI;??? /* 目標段 selector 的索引是Bit15~Bit3 */?
target_ti = target_cs.TI;??? /* 目標段selector的描述符表索引是Bit2 */
CODESEG_DESCRIPTOR target_descriptor;

if (target_ti == 0) { /* target_cs.TI為0 就是參考到 GDT(全局描述符表) */?
/* 以GDTR寄存器的base 為基地址加上selector的索引乘以8即得出目標?
?????? 段描述符,目標描述符的 DPL 就是目標段所需的訪問權限 */?
??? target_descriptor = GDTR.base + target_si * 8

} else {?????????????? /* 否則就是參考 LDT (局部描述符表)*/?
??? /* 以 LDTR寄存器的base 為基地址得出目標段描述符 */

target_descriptor = LDTR.base + target_si * 8;?
}
?
DPL ?= target_descriptor. DPL ;???? /* 獲取 DPL ?*/
?
if (target_descriptor.type & 0x06) { /* conforming */?
if ( CPL ?>=? DPL ) {?? /* 允許執行高權限代碼 */?
???? /* go ahead */?
??? } else {?
???? /* 引發 #GP 異常 */?
??? goto DO_GP_EXCEPTION;?
}?

} else {????????????????? /* nonconforming */?
??? if ( CPL ?==? DPL ?&&? RPL ?<=? CPL ) {???
??????? /* go ahead */?
??? } else {?
??????? /* 引發 #GP 異常 */?
?????? goto DO_GP_EXCEPTION;?
??? }?
???
}
?
/****** go ahead … …******/?
CS = target_cs;?????????? /* 加載目標段CS 進入 CS 寄存器 */?
EIP = target_eip;??????? /* 加載目標指令EIP 進入 EIP 寄存器 */?
???????????????? /* 當前執行權限? CPL ?不改變 */

goto target_descriptor.base + target_eip; /* 跳轉到目標地址執行 */
?

DO_GP_EXCEPTION:???? /* 執行 #GP異常點 */?
?????????? … …

?

?

?

二、 使用call gate 進行控制權的轉移?
使用call gate進行轉移控制,目的是建立一個利用gate進行向高權限代碼轉移的一種保護機制。gate符相當一個進入高權限代碼的一個通道。


對于指令 call cs:eip 來說:?
★ 目標代碼的selector是一個門符的選擇子。用來獲取門描述符。?
★ 門描述符含目標代碼段的selector及目標代碼的偏移量。?
★ 目標代碼的ip值被忽略。因為門符已經提供了目標代碼的偏移量。?

1、 權限的檢查?
★ 首先,必須要有足夠的權限來訪問gate符,所以: CPL ?<= DPLg(門符的 DPL )且: RPL ?<= DPLg?
★ 進前代碼向高權限代碼轉移,所以,對于conforming類型的代碼段來說,必須 CPL ?>= DPLs(目標代碼段的 DPL )?
★ 對于nonconforming類型代碼段來說,必須 CPL ?= DPLs?
★ call 指令改變當前權限,而jmp指令不改變當前權限。

總結:

?

?

if (( CPL ?<= DPLg) && ( RPL ?<= DPLg)) { /* 足夠的權限訪問門符 */?
if (target.C == CONFORMING) {?? /* 目標代碼屬于 conforming類型 */?
??? /* 向高權限級別代碼轉移控制 */?
??? if ( CPL ?>= DPLs) {?
?????????? /* 通過訪問 */?
????? } else {?
??????????? /* 失敗,#Gp異常發生 */?
??? }?

??? } else {?????????????? /* 目標代碼屬于 nonconforming 類型 */?
?????? /* 平級轉移 */?
??? if ( CPL ?== DPLs) {?
??????????? /* 通過訪問 */?
??????? } else {?
???????????? /* 失敗,#GP 異常發生 */?
??????? }?
}

} else {?? /* 沒有足夠權限訪問門符 */?
/* #GP 異常發生 */?
}

?

?

2、 控制權的轉移?
指令:call 1a:804304c (其中1a是call gate selector)

?

?

gate_selector = 0x1a;?????????????? /* call gate selector */?
RPL ?= gate_selector. RPL ;??????????? /* 門符選擇子 RPL ?*/?
CPL ?= CS. RPL ;???????????????????? /* 當前代碼段低3位是 CPL */
CALLGATE_DESCRIPTOR call_gate_descriptor;????? /* 門符的描述符 */?
CODESEG_DESCRIPTOR target_cs_descritpor;????? /* 目標代碼段的描述符 */
call_gate_si = gate_selector.SI;????? /* 門符 selector 的索引 */?
call_gate_ti = gate_selector.TI;????? /* 門符selector的描述符表索引 */?
??????????????????????????????????????????????????????????????????????????
/* 獲取call gate descriptor */?
if (call_gate_ti == 0) {????? /* TI為0 就是參考到 GDT(全局描述符表) */?
/* 以GDTR寄存器的base 為基地址加上selector的索引乘以8即得出門符的描述符,門符的 DPL 就是門符的訪問權限 */?
??? call_gate_descriptor = GDTR.base + call_gate_si * 8;?
} else {?????????????? /* 否則就是參考 LDT (局部描述符表)*/?
???? /* 以 LDTR寄存器的base 為基地址得出目標段描述符 */?
call_gate_descriptor = LDTR.base + call_gate_si * 8;?
}

/* 獲取 target code segment descriptor */
target_cs = call_gate_descriptor.selector;??? /* 獲取門符的目標代碼段選擇子 */?
target_cs_si = target_cs.SI;???????? /* 目標代碼段的索引 */?
target_cs_ti = target_cs.TI;????????????????? /* 目標代碼描述符表索引 */
if (target_cs_ti == 0)?
??? target_cs_descriptor = GDTR.base + target_cs_si * 8;?
else?
??? target_cs_descriptor = LDTR.base + target_cs_si * 8;

DPLg = call_gate_descriptor. DPL ;???? /* 獲取門符的 DPL ?*/?
DPLs = target_cs_descriptor. DPL ;???? /* 獲取目標代碼段的 DPL ?*/

if ( CPL ?<= DPLg &&? RPL ?<= DPLg) {?? /* 有足夠權限訪問門符 */?
???? if (target_cs_descriptor.type & 0x06) { /* conforming */?
????????? if ( CPL ?>= DPLs) {?
???????????????? /* 允許訪問目標代碼段 */?
????????? } else {?
???????????????? /* #GP 異常產生 */?
????????? }?
???? } else if ( CPL ?== DPLs) { /* nonconforming */?
???????? /* 允許訪問目標代碼段 */?
???? } else {?
????????? /* 拒絕訪問,#GP 異常發生 */?
????????? goto DO_GP_EXCEPTION;?
???? }?
} else { /* 無權限訪問門符 */?
???? /* 拒絕訪問, #GP異常發生 */?
???? goto DO_GP_EXCEPTION;?
}
?
/* 允許訪問 */?
current_CS = target_cs;?????? /* 加載目標代碼段進入CS 寄存器 */?
current_CS. RPL ?= DPLs;??????? /* 改變當前執行段權限 */?
current_EIP = call_gate_descriptor.offset;?? /* 加載EIP */
?
/* 跳轉到目標代碼執行 */?
/* goto current_CS:current_EIP */?
goto target_cs_descriptor.base + call_gate_descriptor.offset;
?
return;
?
DO_GP_EXCEPTION:???????? /* 執行異常 */

?

?

?

三、 使用中斷門或陷井門進行轉移?
?? 中斷門符及陷井門必須存放在IDT中,IDT表也可以存放call gate。

1、 中斷調用時的權限檢查?
?? 用中斷門符進行轉移時,所作的權限檢查同call gate相同,區別在于intterrupt gate 轉移不需要檢查 RPL ,因為,沒有 RPL 需要檢查。?
★ 必須有足夠的權限訪問門符, CPL ?<= DPLg?
★ 向同級權限代碼轉移時, CPL ?== DPLs,向高權限代碼轉移時, CPL ?> DPLs

總結

?

?

if ( CPL ?<= DPLg) { /* 有足夠權限訪問門符 */?
??? if ( CPL ?>= DPLs) {?
??????? /* 允許訪問目標代碼頭 */?
??? } else {?
???????? /* 失敗,#GP異常發生 */?
??? }

} else {?
/* 失敗,#GP異常發生 */?
}

?

2、 控制權的轉移?
?? 發生異常或中斷調用時?
★ 用中斷向量在中斷描述符表查找描述符:中斷向量×8,然后加上IDT表基址得出描述符表。?
★ 從查找到的描述符中得到目標代碼段選擇子,并在相應的GDT或LDT中獲取目標代碼段描述符。?
★ 目標代碼段描述符的基址加上門符中的offset,確定最終執行入口點。


中斷或陷井門符轉移的總結:?
例: int 0x80 指令發生的情況

?

?

vector = 0x80;?
INTGATE_DESCRIPTOR gate_descriptor = IDTR.base + vector * 8;?
CODESEG_DESCRIPTOR target_descriptor;?
TSS tss = TR.base;?????????????? /* 得到TSS 內存塊 */?
DPLg = gate_descriptor. DPL ;?
target_cs = gate_descriptor.selector;
?
if ( CPL ?<= DPLg) {??????????? /* 允許訪問門符 */

if (target_cs.TI == 0) {?? /* index on GDT */?
??? target_descriptor = GDTR.base + target_cs.SI * 8;?
} else {????????????? /* index on LDT */?
target_descriptor = LDTR.base + target_cs.SI * 8;?
??? }

DPLs = target_descriptor. DPL ;?


if ( CPL ?> DPLs) {???? /* 向高權限代碼轉移 */?

??? /* 根據目標代碼段的 DPL 值來選取相應權限的stack結構 */?
??? switch (DPLs) {?
??? case 0 :???? /* 假如目標代碼處理0級,則選0級的stack結構 */?
???????????? SS = tss.ss0;?
???????????? ESP = tss.esp0;?
???????????? break;?
??????? case 1:?
???????????? SS = tss.ss1;?
??????????? ESP = tss.esp1;?
???????????? break;?
??????? case 2:?
???????????? SS = tss.ss2;?
???????????? ESP = tss.esp2;?
???????????? break;?
??? }

?????? /* 以下必須保護舊的stack結構,以便返回 */?
??? *--esp = SS;????????? /* 將當前SS入棧保護 */?
??? *--esp = ESP;???????? /* 將當前ESP入棧保護 */?

} else if ( CPL ?== DPLs) {?
???? /* 同級轉移,繼續向下執行 */?
} else {?
??? /* 失敗,#GP異常產生,轉去處理異常 */?
}?


*--esp = EFLAGS;????????? /* eflags 寄存器入棧 */?

???? /* 分別將 NT、NT、RF及VM標志位清0 */?
EFLAGS.TF = 0;????????????
EFLAGS.NT = 0;?
EFLAGS.RF = 0;?
EFLAGS.VM = 0;?

if (gate_descriptor.type == I_GATE32) { /* 假如是中斷門符 */?
EFLAGS.IF = 0;????????? /* 也將IF標志位清0,屏蔽響應中斷 */?
???? }?
?????????????
???? *--esp = CS;????????????? /* 當前段選擇子入棧 */?
???? *--esp = EIP;???????????? /* 當前EIP 入棧 */
CS = target_selector;????? /* 加載目標代碼段 */?
CS. RPL ?= DPLs;??????????? /* 改變當前執行權限級別 */?
EIP = gate_descriptor.offset; /* 加載進入EIP */?

/* 執行中斷例程 */?
goto target_descritptor.base + gate_descriptor.offset;?

} else {?
/* 失敗,#GP 異常產生,轉去處理異常 */?
}

?

?

?

■ 堆棧的切換


控制權發生轉移后,處理器自動進行相應的堆棧切換。?
1、 當轉向到同權限級別的代碼時,不會進行堆棧級別的調整,也就是不進行堆棧切換。?
2、 當轉向高權限級別時,將發生相應級別的堆棧切換。從TSS塊獲取相應級別的stack結構。

例:假如當前運行級別 CPL 為2時,發生了向0級代碼轉移時:

?

?

TSS tss = TR.base;???????? /* 從TR寄存器中獲取TSS 塊 */?
CPL ?= 2;???????????????? /* 當前運行級別為2 級*/?
DPL ?= 0;???????????????? /* 目標代碼需要級別為 0 級 */

/* 根據目標代碼需要的級別進行選取相應的權限級別的stack結構 */?
switch ( DPL ) {?
case 0:?
???? SS = tss.ss0;?
???? ESP = tss.esp0;?
???? break;?
case 1:?
???? SS = tss.ss1;?
???? ESP = tss.esp1;?
???? break;?
case 2:?
???? SS = tss.ss2;?
???? ESP = tss.esp2;?
???? break;?
}
?
*--esp = SS;????????? /* 保存舊的stack結構 */?
*--esp = ESP;
?
/* 然后再作相當的保存工作,如保存參數等 */
*--esp = CS;???????? /* 最后保存返回地址 */?
*--esp = EIP;

?

?

?

?

■ 控制權的返回


當目標代碼執行完畢,需要返回控制權給原代碼時,將產生返回控制權行為。返回控制權行為,比轉移控制權行為簡單得多。因為,一切條件已經在交出控制權之前準備完畢,返回時僅需出棧就行了。

1、 near call 的返回?
近調用情況下,段不改變,即CS不改變,權限級別不改變。僅需從棧中pop返回地址就可以了。


2、 直接控制權轉移的返回(far call或far jmp)?
?? 直接控制權的轉移是一種不改變當前運行級別的行為。只是發生跨段的轉移。這時,CS被從棧中pop出來的CS值加載進去,處理器會檢查 CPL 與這個pop出來的選擇子中的 RPL 進行檢查,相符則返回。不相符則發生 #GP異常。?
???
總結:假如當前運行的目標代碼執行完畢后,將要返回。這時 CPL 為2

?

?

CPL ?= 2;????? /* 當前代碼運行級別為 2 */?
… …?
EIP = *esp++;?? /* pop出原EIP 值 */?
CS = *esp++;??? /* pop出原CS值 */
?
if ( CPL ?== CS. RPL ) {???
/* CS. RPL ?代表是原來的運行級別,與 CPL 相符則返回 */?
return ;?
} else {?
/* #GP異常產生,執行異常處理 */?
}

?

?

3、 利用各種門符進行向高權限代碼轉移后的返回?
從高權限代碼返回低權限代碼,須從stack中pop出原來的stack結構。這個stack結構屬于低權限代碼的stack結構。然后直接pop 出原返回地址就可以了。?

總結:

CPL ?= 0;??????? /* 當前運行級別為 0 級 */?
… …?
EIP = *esp++;??? /* 恢復原地址 */?
CS = *esp++;???? /* 恢復原地址及運行級別 */

ESP = *esp ++;???? /* 恢復原stack結構 */?
SS = *esp++;???? /* 恢復原stack 結構,同時恢復了原stack訪問級別 */
?
return ;?????? /* 返回 */
?
************************************************************************************
3、DPL,RPL,CPL 之間的聯系和區別是什么?RPL和CPL是必須相同嗎?如果相同,為什么要釆用兩個而不改用一個呢??



答:特權級是保護模式下一個重要的概念,CPL,RPL和DPL是其中的核心概念,查閱資料無數,總結如下:?
? 簡單解釋:?

--------------------------------------------------------------------------------?

? CPL是當前進程的權限級別(Current Privilege Level),是當前正在執行的代碼所在的段的特權級,存在于cs寄存器的低兩位。?
? RPL說明的是進程對段訪問的請求權限(Request Privilege Level),是對于段選擇子而言的,每個段選擇子有自己的RPL,它說明的是進程對段訪問的請求權限,有點像函數參數。而且RPL對每個段來說不是固定的,兩次訪問同一段時的RPL可以不同。RPL可能會削弱CPL的作用,例如當前CPL=0的進程要訪問一個數據段,它把段選擇符中的RPL設為3,這樣雖然它對該段仍然只有特權為3的訪問權限。?
DPL存儲在段描述符中,規定訪問該段的權限級別(Descriptor Privilege Level),每個段的DPL固定。?
當進程訪問一個段時,需要進程特權級檢查,一般要求DPL >= max {CPL, RPL}?
下面打一個比方,中國官員分為6級國家主席1、總理2、省長3、市長4、縣長5、鄉長6,假設我是當前進程,級別總理(CPL=2),我去聊城市(DPL=4)考察(呵呵),我用省長的級別(RPL=3 這樣也能嚇死他們:-))去訪問,可以吧,如果我用縣長的級別,人家就不理咱了(你看看電視上的微服私訪,呵呵),明白了吧!為什么采用RPL,是考慮到安全的問題,就好像你明明對一個文件用有寫權限,為什么用只讀打開它呢,還不是為了安全!?


? 全面解釋:?
--------------------------------------------------------------------------------?

? RPL是段選擇子里面的bit 0和bit 1位組合所得的值,但這里要首先搞清楚什么是段選擇子,根據Intel 的文件(IA-32 IntelR Architecture Software Developer's Manual, Volume 3System Programming Guide)它是一個16Bit identifier (原文:A segment selector is a 16-bit identifier for a segment). 但 identifier 又是什么. identifier 可以是一個變數的名字( An identifier is a name for variables), 簡單的說它可以就是一般意義的變數. 這里 16-bit identifier for a segment 可以就是一個一般意義的16bit變數但同時要求對它的值解釋的時候必須跟據Intel定下的規則---也就是bit 0和bit 1位的組合值就是RPL等等… 因此在程序里如果有需要的話你可以聲明一個或者多個變數來代表這些段選擇子,這樣的話你的程序在某一時刻就可以有很多段選擇子,當然有那么多段選擇子就有那么多RPL.可以這樣說程序有多少個是RPL是你怎樣看待你自己聲明的變數. |?
? 程序的CPL(CS.RPL)是CS register 里bit 0和bit 1 位組合所得的值.在某一時刻就只有這個值唯一的代表程序的CPL.??

? 而DPL是段描述符中的特權級, 它的本意是用來代表它所描述的段的特權級. 一個程序可以使用很多段(Data,Code,Stack)也可以只用一個code段等.在正常的情況下當程序的環境建立好后,段描述符都不需要改變-----當然DPL也不需要改變.??


? 一、對數據段和堆棧段訪問時的特權級控制:?

要求訪問數據段或堆棧段的程序的CPL≤待訪問的數據段或堆棧段的DPL,同時選擇子的RPL≤待訪問的數據段或堆棧段的DPL,即程序訪問數據段或堆棧段要遵循一個準則:只有相同或更高特權級的代碼才能訪問相應的數據段。這里,RPL可能會削弱CPL的作用,訪問數據段或堆棧段時,默認用CPU和RPL中的最小特權級去訪問數據段,所以max {CPL, RPL} ≤ DPL,否則訪問失敗。?


? 二、對代碼段訪問的特權級控制(代碼執行權的特權轉移):?

? 讓我們先來記一些“定律”:?
所有的程序轉跳,CPU都不會把段選擇子的RPL賦給轉跳后程序的CS.RPL. .?

? 轉跳后程序的CPL(CS.RPL)只會有下面的倆種可能?
? 轉跳后程序的CPL(CS.RPL) = 轉跳前程序的CPL(CS.RPL)??
? 或?
? 轉跳后程序的CPL(CS.RPL) = 轉跳后程序的CodeDescriptor.DPL?

? 以 Call 為例(只能跳到等于當前特權級或比當前特權級更高的段):?
? 怎樣決定這兩種選擇,這就要首先知道轉跳后程序的段是一致代碼段還是非一致代碼段.其實也很簡單,規則如下:?
? 如果能成功轉跳到一致代碼段, 轉跳后程序的CPL(CS.RPL) = 轉跳前程序的CPL(CS.RPL),(轉跳后程序的CPL繼承了轉跳前程序的CPL)?
? 如果能成功轉跳到非一致代碼段, 轉跳后程序的CPL(CS.RPL) =轉跳后程序的Descriptor.DPL。(轉跳后程序的CPL變成了該代碼段的特權級.我在前面提到DPL是段描述符中的特權級, 它的本意是用來代表它所描述的段的特權級)怎樣才能成功轉跳啦??

? 這里有四個重要的概念:?

? 1).段的保護觀念是高特權級不找低特權級辦事,低特權級找高特權級幫忙,相同的一定沒問題.(這樣想邏輯是沒錯,事實對不對就不知道.)也就是縣長不找鄉長,鄉長不求農民,反過來農民求鄉長,鄉長找縣長.這個概念是最重要的。?
? 2) 一致代碼段的意義: 讓客人很方便的利用主人(一致代碼段)的東西為自己辦事.但客人這身份沒有改變NewCS.RPL=OldCS.RPL所以只能幫自己辦事。比方說鄉長有一頭牛,農民可以借來幫自己種田,但不能種別人的田.但是如果你是鄉長當然可以種鄉里所有的田。?
? 3) 非一致代碼段的意義:主人(非一致代碼段)可以幫客人但一定是用自己的身份NewCS.RPL= DestinationDescriptorCode.DPL這里可能有安全的問題, 搞不好很容易農民變縣長。主人太頑固了一定要堅持自己的身份,有什么方法變通一下,來個妥協好不好。好的,它就是RPL的用處。?
? 4) RPL: 它讓程序有需要的時候可以表示一個特權級更低的身份Max(RPL,CPL)而不會失去本身的特權級CPL(CS.RPL),有需要的時候是指要檢查身份的時候。事實上RPL跟段本身的特權級DPL和當前特權級CPL沒有什么關系,因為RPL的值在成功轉跳后并不賦給轉跳后的CS.RPL。?
? 還是要問怎樣才能成功轉跳啦?這里分兩種情況:?

? 普通轉跳(沒有經過Gate 這東西):即JMP或Call后跟著48位全指針(16位段選擇子+32位地址偏移),且其中的段選擇子指向代碼段描述符,這樣的跳轉稱為直接(普通)跳轉。普通跳轉不能使特權級發生躍遷,即不會引起CPL的變化,看下面的詳細描述:?


目標是一致代碼段:?
? 要求:CPL(CS.RPL)>=DestinationDescriptorCode.DPL ,其他RPL是不檢查的。?
? 轉跳后程序的CPL(NewCS.RPL) = 轉跳前程序的CPL( OldCS.RPL)?
? 上面的安排就是概念1,2的意思,此時,CPL沒有發生變化,縱使它執行了特權級(DPL)較高的代碼。若訪問時不滿足要求,則發生異常。?
目標是非一致代碼段:?
? 要求:CPL(CS.RPL)=DestinationDescriptorCode.DPL AND RPL≤CPL(CS.RPL)?
? 轉跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL?
? 上面的安排就是概念3的意思和部分1的意思----主人(一致代碼段)只幫相同特權級的幫客人做事。因為前提是CPL=DPL,所以轉跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL不會改變CPL的值,特權級(CPL)也沒有發生變化。如果訪問時不滿足前提CPL=DPL,則引發異常。


? 通過調用門的跳轉:當段間轉移指令JMP和段間轉移指令CALL后跟著的目標段選擇子指向一個調用門描述符時,該跳轉就是利用調用門的跳轉。這時如果選擇子后跟著32位的地址偏移,也不會被cpu使用,因為調用門描述符已經記錄了目標代碼的偏移。使用調門進行的跳轉比普通跳轉多一個步驟,即在訪問調用門描述符時要將描述符當作一個數據段來檢查訪問權限,要求指示調用門的選擇子的RPL≤門描述符DPL,同時當前代碼段CPL≤門描述符DPL,就如同訪問數據段一樣,要求訪問數據段的程序的CPL≤待訪問的數據段的DPL,同時選擇子的RPL≤待訪問的數據段或堆棧段的DPL。只有滿足了以上條件,CPU才會進一步從調用門描述符中讀取目標代碼段的選擇子和地址偏移,進行下一步的操作。?
? 從調用門中讀取到目標代碼的段選擇子和地址偏移后,我們當前掌握的信息又回到了先前,和普通跳轉站在了同一條起跑線上(普通跳轉一開始就得到了目標代碼的段選擇子和地址偏移),有所不同的是,此時,CPU會將讀到的目標代碼段選擇子中的RPL清0,即忽略了調用門中代碼段選擇子的RPL的作用。完成這一步后,CPU開始對當前程序的CPL,目標代碼段選擇子的RPL(事實上它被清0后總能滿足要求)以及由目標代碼選擇子指示的目標代碼段描述符中的DPL進行特權級檢查,并根據情況進行跳轉,具體情況如下:?


目標是一致代碼段:?
? 要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL ,RPL不檢查,因為RPL被清0,所以事實上永遠滿足RPL≤DPL,這一點與普通跳轉一致,適用于JMP和CALL。?
? 轉跳后程序的CPL(NewCS.RPL) = 轉跳前程序的CPL( OldCS.RPL),因此特權級沒有發生躍遷。?
???
目標是非一致代碼段:?
當用JMP指令跳轉時:?
? 要求:CPL(CS.RPL)=DestinationDescriptorCode.DPL AND RPL<= CPL(CS.RPL)(事實上因為RPL被清0,所以RPL≤CPL總能滿足,因此RPL與CPL的關系在此不檢查)。若不滿足要求則程序引起異常。?
? 轉跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL?
? 因為前提是CPL=DPL,所以轉跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL不會改變CPL的值,特權級也沒有發生變化。如果訪問時不滿足前提CPL=DPL,則引發異常。?
當用CALL指令跳轉時:?
要求:CPL(CS.RPL)≥DestinationDescriptorCode.DPL(RPL被清0,不檢查),若不滿足要求則程序引起異常。?
轉跳后程序的CPL(NewCS.RPL) = DestinationDescriptorCode.DPL?
當條件CPL=DPL時,程序跳轉后CPL=DPL,特權級不發生躍遷;當CPL>DPL時,程序跳轉后CPL=DPL,特權級發生躍遷,這是我們當目前位置唯一見到的使程序當前執行憂先級(CPL)發生變化的跳轉方法,即用CALL指令+調用門方式跳轉,且目標代碼段是非一致代碼段。?


? 總結:以上介紹了兩種情況的跳轉,分別是普通跳轉和使用調用門的跳轉,其中又可細分為JMP跳轉和CALL跳轉,跳轉成功已否是由CPL,RPL和DPL綜合決定的。所有跳轉都是從低特權級代碼向同級或更高特權級(DPL)跳轉,但保持當前執行特權級(CPL)不變,這里有點難于區別為什么說向高特權級跳轉,又說特權級沒變,這里“高特權級”是指目標代碼段描述符的DPL,它規定了可以跳轉到該段代碼的最高特權級;而后面的CPL不變才真正說明了特權級未發生躍遷。我們可以看到,只有用CALL指令+調用門方式跳轉,且目標代碼段是非一致代碼段時,才會引起CPL的變化,即引起代碼執行特權級的躍遷,這是目前得知的改變執行特權級的唯一辦法,如果各位讀者還知道其他方法請留言告訴我。

《Orange’s 一個操作系統的實現》3.保護模式5----特權級概述


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久热中文字幕在线精品首页 | 大ji吧快给我别停受不了视频 | 寂寞午夜影院 | 国产精品欧美一区二区三区不卡 | 国产精品高清视亚洲一区二区 | 国产国产成人人免费影院 | 国产精品午夜在线观看 | 亚洲精品国产福利片 | 天天久久综合 | 欧美 日产 国产精选 | 欧美一级a俄罗斯毛片 | 普通话对白国产情侣自啪 | 亚洲国产综合精品中文字幕 | 欧美日韩亚洲视频 | 手机看片国产永久1204 | 欧美成人综合视频 | 黄页网址在线免费观看 | 国产亚洲精品久久久久久午夜 | 免费观看成人毛片 | 久久亚洲国产成人影院 | 日日干天天射 | 久操免费在线 | 天天插天天操天天射 | 就草草在线观看视频 | 婷婷日韩| 国产九九 | 伊人网综合视频 | 日本特交大片免费观看 | 国内精品久久久久久久星辰影视 | 欧美高清亚洲欧美一区h | 天天射天天射天天射 | 色中文字幕 | jizz中国女人| 日本视频播放免费线上观看 | 日本aaaa级| 999精品视频这里只有精品 | 九九热视 | 久久免费网 | 久久r这里只有精品 | 看一级特黄a大一片 | 亚洲日本在线观看视频 |