全面剖析《自己動手寫作業系統》第三章—特權級 以及 不同特權級程式碼片段之間的跳轉規則

來源:互聯網
上載者:User

《自己動手寫作業系統》讀後感                    http://blog.csdn.net/zgh1988/article/details/7059936

全面剖析《自己動手寫作業系統》第一章       http://blog.csdn.net/zgh1988/article/details/7060032

全面剖析《自己動手寫作業系統》第二章       http://blog.csdn.net/zgh1988/article/details/7062065

全面剖析《自己動手寫作業系統》第三章1     http://blog.csdn.net/zgh1988/article/details/7098981

全面剖析《自己動手寫作業系統》--“實模式--保護模式--實模式”         
http://write.blog.csdn.net/postedit/7256254

全面剖析《自己動手寫作業系統》--堆棧段的工作方式   http://blog.csdn.net/zgh1988/article/details/7256254

 

        在這裡我會以pmtest4.asm和pmtest5.asm來敘述我對 特權級的理解 以及 不同特權級代碼之間的跳轉情況。

1、  特權級

2、  一致程式碼片段和非一致程式碼片段

3、  DPL、RPL、CPL分別代表的含義,儲存在什麼位置,以及它們之間的關係

4、  對資料區段的訪問規則

5、  不同特權級程式碼片段之間的轉移

6、  關於堆棧

7、  結合pmtest5.asm來描述具有不同特權級的跳轉過程

一、特權級

        在IA32的分段機制下,特權級總共有4個特權層級,從高到低分別是0、1、2、3。數字越小表示的特權級越大。

特權級如所示:

 

較為核心的代碼和資料,將被存放在特權級較高的層級中。處理器將用這樣的機制來避免低特權級的任務在不被允許的情況下訪問位於高特權級的段。

二、一致程式碼片段 和 非一致程式碼片段

        系統要安全,必須保證核心與使用者程式分離開,核心要安全,必須不能被使用者來打擾。但是有的時候,使用者程式也是需要訪問核心中的部分資料,那怎麼辦?

        於是作業系統就將核心中的段分為共用程式碼段非共用程式碼段兩部分。

其中一致程式碼片段就是作業系統拿出來被共用程式碼段,可以被低特權級的使用者直接存取的代碼。

一致程式碼片段的限制作用:

1)  特權級高的程式碼片段不允許存取權限級低的程式碼片段:即核心態不允許調用使用者態下的代碼。

2)  特權級低的程式碼片段可以存取權限級高的程式碼片段,但是當前的特權級不發生變化。即:使用者態可以訪問核心態的代碼,但是使用者態仍然是使用者態。

 

非一致程式碼片段:為了避免低特權級的訪問而被作業系統保護起來的系統代碼,也就是非共用代碼。

非一致程式碼片段的限制作用:

1)只允許同特權級間訪問

2)絕對禁止不同級間訪問,即:使用者態不能訪問核心態,核心態也不訪問使用者態。

 

為一致碼段與非一致碼段的訪問規則:

三、CPL、DPL、RPL分別代表的含義,儲存在什麼位置,以及它們之間的關係

         CPL(Current Privilege Level)是當前執行的程式或任務的特權級。它被儲存在CS和SS的第0位和第1位上。通常情況下,CPL等於代碼的段的特權級。在遇到一致程式碼片段時,一致程式碼片段可以被相同或者更低特權級的代碼訪問。當處理器訪問一個與CPL特權級不同的一致程式碼片段時,CPL不會被改變。

        DPL(Descriptor Privilege Level):DPL表示段或者門的特權級,它被儲存在段描述符或者門描述符的DPL欄位中。噹噹前程式碼片段試圖訪問一個段或者門時,DPL將會和CPL以及段或門選擇子的RPL相比較,根據段或者門類型的不同,DPL將會被區別對待,下面介紹一下各種類型的段或者門的情況。

資料區段:DPL規定了可以訪問此段的最低特權級。比如,一個資料區段的DPL是1,那麼只有運行在CPL為0或者1的程式才有權訪問它。

非一致程式碼片段(不使用調用門的情況下):DPL規定訪問此段的特權級。比如一個非一致程式碼片段的特權級為0,那麼只有CPL為0的程式才可以訪問它。

調用門:DPL規定了當前執行的程式或任務可以訪問此調用門的最低特權級(這與資料區段的規則是一致的)。

一致程式碼片段和通過調用門訪問的非一致程式碼片段:DPL規定了訪問此段的最高特權級。比如,一個一致程式碼片段的DPL是2,那麼CPL為0和1的程式將無法訪問此段。

        RPL(Requested Privilege Level):RPL是通過選擇子的第0位和第1位表現出來的。處理器通過檢查RPL和CPL來確認一個訪問請求是否合法。

四、對資料區段的訪問規則

           資料區段中DPL規定了可以訪問此段的最低特權級,因此,對資料的訪問,只要CPL和RPL都小於被訪問的資料區段的DPL就可以了,即CPL<=DPL和RPL<=DPL。

五、不同特權級程式碼片段之間的轉移

            使用jmp或call指令可以實現下列4種轉移

(1)目標運算元包含目標程式碼片段的段選擇子。

(2)目標運算元指向一個包含目標程式碼片段選擇子的調用門描述符。

(3)目標運算元指向一個包含目標程式碼片段選擇子的TSS。

(4)目標運算元指向一個任務門,這個任務門指向一個包含目標程式碼片段選擇子的TSS。

這4種方式可以看做是兩大類,一類是通過jmp和call的直接轉移(上述第一種),另一類是通過某個描述符的間接轉移(上述第2,3,4種)。

1、  通過jmp或call進行直接轉移

2、通過調用門進行轉移(1)調用門的使用方法

首先介紹門描述符:

 

        門描述符中包含著目標程式碼片段的選擇子。

        以pmtest4.asm為例,介紹門描述符的使用方法

目標程式碼片段的段描述符

LABEL_DESC_CODE_DEST:      Descriptor        0,         SegCodeDestLen-1,   DA_C + DA_32         

目標程式碼片段選擇子

SelectorCodeDest   equ            LABEL_DESC_CODE_DEST - LABEL_GDT

調用門的描述符

LABEL_CALL_GATE_TEST:        Gate         SelectorCodeDest,  0,      0,      DA_386CGate + DA_DPL0

調用門選擇子

SelectorCallGateTest       equ            LABEL_CALL_GATE_TEST - LABEL_GDT

如果通過調用門要訪問目標程式碼片段,則使用

Call  SelectorCallGateTest : 0

(2)使用調用門進行轉移時特權級檢驗的規則

          假設我們想由代碼A轉移到代碼B,運用一個調用門G,即調用門G中的目標選擇子指向代碼B的段。實際上,這個問題主要涉及這幾個元素:CPL、RPL、代碼B的DPL(記做DPL_B),調用門G的DPL(記做DPL_G)。

        調用門使用時特權級檢驗的規則如下:

 

也就是說,通過調用門和call指令,可以實現從低特權級到高特權級的轉移,無論目標程式碼片段時一致的還是非一致的。

通過調用門和jmp指令,如果目標程式碼片段是一致的,則可以實現從低特權級到高特權級的轉移。如果目標程式碼片段是非一致的,則只能實現相同特權級的轉移。

六、關於堆棧1、  “長”跳轉/調用 和 “短”跳轉/調用

        如果一個調用或跳轉指令時段間而不是段內進行的,那麼我們稱之為“長”的(Far jmp/call),反之,如果在段內則是“短”的(Near jmp/call)。

 

        那麼長的和短的jmp或call有什麼分別呢?

對於jmp而言,僅僅是結果不同罷了,短跳轉對應段內,長跳轉對應段間。

對於call來說,就比較複雜一些,因為call指令是會影響堆棧的,長調用和短調用對堆棧的影響是不同的。

 

        下面我們討論短調用對堆棧的影響,call指令執行時下一條指令的eip壓棧,到ret指令執行時,這個eip會被從堆棧中彈出,

如所示:

這是短調用的情況。

 

        下面我們討論長調用對堆棧的影響,call指令執行時會將調用者的cs和eip壓棧,到ret指令執行時,這個eip和cs會被從堆棧中彈出,如所示:

2、有特權級變換的轉移對堆棧的影響

          在不同特權級下的堆棧段不同,所以每一個任務最多可能在4個特權級間轉移,所以,每個任務實際上需要4個堆棧。可是我們只有一個ss和一個esp,那麼當發生堆棧切換,我們該從哪裡獲得其餘堆棧的ss和esp呢?

解決這個問題,需要一個資料結構TSS(Task-State Stack),

        當堆棧發生切換時,內層的ss和esp就是從這裡取得的,比如,我們當前所在的是ring3,當轉移至ring1時,堆棧將被自動切換到由ss1和esp1指定的位置。由於只是在由外層轉移到內層(低特權級到高特權級)切換時新堆棧才會從TSS中取得,所以TSS中沒有位於最外層的ring3的堆棧資訊。

 

下面讓我們來看看整個的轉移過程是怎麼樣的?

 

執行call前後堆棧段的變化:

(1)根據目標程式碼片段的DPL(新的CPL)從TSS中選擇應該切換至哪個ss和esp

(2)從TSS中讀取新的ss和esp。在這過程中如果發現ss、esp或者TSS界限錯誤都會導致無效TSS異常

(3)對ss描述符進行檢驗,如果發生錯誤,同樣產生#TS異常

(4)暫時性地儲存當前ss和esp的值

(5)載入新的ss和esp

(6)將剛剛儲存起來的ss和esp的值壓入新棧

(7)從調用者堆棧中將參數複製到被調用者堆棧(新堆棧)中,複製參數的數目由調用門中Param Count一項來決定。

(8)如果Param Count是零的話,將不會複製參數。

(9)將當前的cs和eip壓棧

(10)載入調用門中指定的新的cs和eip,開始執行被調用者過程。

 

執行ret前後堆棧段的變化:

(1)檢查儲存的cs中的RPL以判斷返回時是否要變換特權級

(2)載入被調用者堆棧上的cs和eip(此時會進行程式碼片段描述符和選擇子類型和特權級檢驗)

(3)如果ret指令含有參數,則增加esp的值以跳過參數,然後esp將指向被儲存過的調用者ss和esp。注意,ret的參數必須對應調用門中的Param Count的值

(4)載入ss和esp,切換到調用者堆棧,被調用者的ss和esp被丟棄。在這裡將會進行ss描述符、esp、以及ss段描述符的檢驗

(5)如果ret指令含有參數,增加esp的值以跳過參數(此時已經在調用者堆棧中)

(6)檢查ds、es、fs、gs的值,如果其中哪一個寄存器指向的段的DPL小於CPL(此規則不適合於一致程式碼片段),那麼一個空描述符會被載入到該寄存器中。

 

        綜上所述,使用調用門的過程實際上分為兩部分,一部分是從低特權級到高特權級,通過調用門和call指令來實現;另一部分則是從高特權級到低特權級,通過ret指令來實現。

 

七、結合pmtest5.asm來描述具有不同特權級的跳轉過程

 

; ==========================================; pmtest5.asm; 編譯方法:nasm pmtest5.asm -o pmtest5.com; ==========================================%include"pm.inc"; 常量, 宏, 以及一些說明org0100hjmpLABEL_BEGIN[SECTION .gdt]; GDT;                                         段基址,         段界限     , 屬性LABEL_GDT:Descriptor       0,                   0, 0; 空描述符LABEL_DESC_NORMAL:Descriptor       0,              0ffffh, DA_DRW; Normal 描述符LABEL_DESC_CODE32:Descriptor       0,    SegCode32Len - 1, DA_C + DA_32; 非一致程式碼片段, 32LABEL_DESC_CODE16:Descriptor       0,              0ffffh, DA_C; 非一致程式碼片段, 16LABEL_DESC_CODE_DEST:Descriptor       0,  SegCodeDestLen - 1, DA_C + DA_32; 非一致程式碼片段, 32LABEL_DESC_CODE_RING3:Descriptor       0, SegCodeRing3Len - 1, DA_C + DA_32 + DA_DPL3; 非一致程式碼片段, 32LABEL_DESC_DATA:Descriptor       0,  DataLen - 1, DA_DRW; DataLABEL_DESC_STACK:Descriptor       0,          TopOfStack, DA_DRWA + DA_32; Stack, 32 位LABEL_DESC_STACK3:Descriptor       0,         TopOfStack3, DA_DRWA + DA_32 + DA_DPL3; Stack, 32 位LABEL_DESC_TSS:Descriptor       0,          TSSLen - 1, DA_386TSS; TSSLABEL_DESC_VIDEO:Descriptor 0B8000h,              0ffffh, DA_DRW + DA_DPL3; 顯存首地址; 門                                            目標選擇子,       位移, DCount, 屬性LABEL_CALL_GATE_TEST:Gate  SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3; GDT 結束GdtLenequ$ - LABEL_GDT; GDT長度GdtPtrdwGdtLen - 1; GDT界限dd0; GDT基地址; GDT 選擇子SelectorNormalequLABEL_DESC_NORMAL- LABEL_GDTSelectorCode32equLABEL_DESC_CODE32- LABEL_GDTSelectorCode16equLABEL_DESC_CODE16- LABEL_GDTSelectorCodeDestequLABEL_DESC_CODE_DEST- LABEL_GDTSelectorCodeRing3equLABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3SelectorDataequLABEL_DESC_DATA- LABEL_GDTSelectorStackequLABEL_DESC_STACK- LABEL_GDTSelectorStack3equLABEL_DESC_STACK3- LABEL_GDT + SA_RPL3SelectorTSSequLABEL_DESC_TSS- LABEL_GDTSelectorVideoequLABEL_DESC_VIDEO- LABEL_GDTSelectorCallGateTestequLABEL_CALL_GATE_TEST- LABEL_GDT + SA_RPL3; END of [SECTION .gdt][SECTION .data1] ; 資料區段ALIGN32[BITS32]LABEL_DATA:SPValueInRealModedw0; 字串PMMessage:db"In Protect Mode now. ^-^", 0; 進入保護模式後顯示此字串OffsetPMMessageequPMMessage - $StrTest:db"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0OffsetStrTestequStrTest - $DataLenequ$ - LABEL_DATA; END of [SECTION .data1]; 全域堆棧段[SECTION .gs]ALIGN32[BITS32]LABEL_STACK:times 512 db 0TopOfStackequ$ - LABEL_STACK - 1; END of [SECTION .gs]; 堆棧段ring3[SECTION .s3]ALIGN32[BITS32]LABEL_STACK3:times 512 db 0TopOfStack3equ$ - LABEL_STACK3 - 1; END of [SECTION .s3]; TSS ---------------------------------------------------------------------------------------------[SECTION .tss]ALIGN32[BITS32]LABEL_TSS:DD0; BackDDTopOfStack; 0 級堆棧DDSelectorStack; DD0; 1 級堆棧DD0; DD0; 2 級堆棧DD0; DD0; CR3DD0; EIPDD0; EFLAGSDD0; EAXDD0; ECXDD0; EDXDD0; EBXDD0; ESPDD0; EBPDD0; ESIDD0; EDIDD0; ESDD0; CSDD0; SSDD0; DSDD0; FSDD0; GSDD0; LDTDW0; 調試陷阱標誌DW$ - LABEL_TSS + 2; I/O位元影像基址DB0ffh; I/O位元影像結束標誌TSSLenequ$ - LABEL_TSS; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[SECTION .s16][BITS16]LABEL_BEGIN:movax, csmovds, axmoves, axmovss, axmovsp, 0100hmov[LABEL_GO_BACK_TO_REAL+3], axmov[SPValueInRealMode], sp; 初始化 16 位程式碼片段描述符movax, csmovzxeax, axshleax, 4addeax, LABEL_SEG_CODE16movword [LABEL_DESC_CODE16 + 2], axshreax, 16movbyte [LABEL_DESC_CODE16 + 4], almovbyte [LABEL_DESC_CODE16 + 7], ah; 初始化 32 位程式碼片段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE32movword [LABEL_DESC_CODE32 + 2], axshreax, 16movbyte [LABEL_DESC_CODE32 + 4], almovbyte [LABEL_DESC_CODE32 + 7], ah; 初始化測試調用門的程式碼片段描述符xoreax, eaxmovax, csshleax, 4addeax, LABEL_SEG_CODE_DESTmovword [LABEL_DESC_CODE_DEST + 2], axshreax, 16movbyte [LABEL_DESC_CODE_DEST + 4], almovbyte [LABEL_DESC_CODE_DEST + 7], ah; 初始化資料區段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_DATAmovword [LABEL_DESC_DATA + 2], axshreax, 16movbyte [LABEL_DESC_DATA + 4], almovbyte [LABEL_DESC_DATA + 7], ah; 初始化堆棧段描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACKmovword [LABEL_DESC_STACK + 2], axshreax, 16movbyte [LABEL_DESC_STACK + 4], almovbyte [LABEL_DESC_STACK + 7], ah; 初始化堆棧段描述符(ring3)xoreax, eaxmovax, dsshleax, 4addeax, LABEL_STACK3movword [LABEL_DESC_STACK3 + 2], axshreax, 16movbyte [LABEL_DESC_STACK3 + 4], almovbyte [LABEL_DESC_STACK3 + 7], ah; 初始化Ring3描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_CODE_RING3movword [LABEL_DESC_CODE_RING3 + 2], axshreax, 16movbyte [LABEL_DESC_CODE_RING3 + 4], almovbyte [LABEL_DESC_CODE_RING3 + 7], ah; 初始化 TSS 描述符xoreax, eaxmovax, dsshleax, 4addeax, LABEL_TSSmovword [LABEL_DESC_TSS + 2], axshreax, 16movbyte [LABEL_DESC_TSS + 4], almovbyte [LABEL_DESC_TSS + 7], ah; 為載入 GDTR 作準備xoreax, eaxmovax, dsshleax, 4addeax, LABEL_GDT; eax <- gdt 基地址movdword [GdtPtr + 2], eax; [GdtPtr + 2] <- gdt 基地址; 載入 GDTRlgdt[GdtPtr]; 關中斷cli; 開啟地址線A20inal, 92horal, 00000010bout92h, al; 準備切換到保護模式moveax, cr0oreax, 1movcr0, eax; 真正進入保護模式jmpdword SelectorCode32:0; 執行這一句會把 SelectorCode32 裝入 cs, 並跳轉到 Code32Selector:0  處;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LABEL_REAL_ENTRY:; 從保護模式跳回到實模式就到了這裡movax, csmovds, axmoves, axmovss, axmovsp, [SPValueInRealMode]inal, 92h; ┓andal, 11111101b; ┣ 關閉 A20 地址線out92h, al; ┛sti; 開中斷movax, 4c00h; ┓int21h; ┛回到 DOS; END of [SECTION .s16][SECTION .s32]; 32 位程式碼片段. 由實模式跳入.[BITS32]LABEL_SEG_CODE32:movax, SelectorDatamovds, ax; 資料區段選擇子movax, SelectorVideomovgs, ax; 視頻段選擇子movax, SelectorStackmovss, ax; 堆棧段選擇子movesp, TopOfStack; 下面顯示一個字串movah, 0Ch; 0000: 黑底    1100: 紅字xoresi, esixoredi, edimovesi, OffsetPMMessage; 來源資料位移movedi, (80 * 10 + 0) * 2; 目的資料位移。螢幕第 10 行, 第 0 列。cld.1:lodsbtestal, aljz.2mov[gs:edi], axaddedi, 2jmp.1.2:; 顯示完畢callDispReturn; Load TSSmovax, SelectorTSSltrax; 在任務內發生特權級變換時要切換堆棧,而內層堆棧的指標存放在當前任務的TSS中,所以要設定任務狀態段寄存器 TR。pushSelectorStack3pushTopOfStack3pushSelectorCodeRing3push0retf; Ring0 -> Ring3,曆史性轉移!將列印數字 '3'。; ------------------------------------------------------------------------DispReturn:pusheaxpushebxmoveax, edimovbl, 160divblandeax, 0FFhinceaxmovbl, 160mulblmovedi, eaxpopebxpopeaxret; DispReturn 結束---------------------------------------------------------SegCode32Lenequ$ - LABEL_SEG_CODE32; END of [SECTION .s32][SECTION .sdest]; 調用門目標段[BITS32]LABEL_SEG_CODE_DEST:movax, SelectorVideomovgs, ax; 視頻段選擇子(目的)movedi, (80 * 12 + 0) * 2; 螢幕第 12 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 紅字moval, 'C'mov[gs:edi], axretfSegCodeDestLenequ$ - LABEL_SEG_CODE_DEST; END of [SECTION .sdest]; 16 位程式碼片段. 由 32 位程式碼片段跳入, 跳出後到實模式[SECTION .s16code]ALIGN32[BITS16]LABEL_SEG_CODE16:; 跳回實模式:movax, SelectorNormalmovds, axmoves, axmovfs, axmovgs, axmovss, axmoveax, cr0andal, 11111110bmovcr0, eaxLABEL_GO_BACK_TO_REAL:jmp0:LABEL_REAL_ENTRY; 段地址會在程式開始處被設定成正確的值Code16Lenequ$ - LABEL_SEG_CODE16; END of [SECTION .s16code]; CodeRing3[SECTION .ring3]ALIGN32[BITS32]LABEL_CODE_RING3:movax, SelectorVideomovgs, ax; 視頻段選擇子(目的)movedi, (80 * 14 + 0) * 2; 螢幕第 14 行, 第 0 列。movah, 0Ch; 0000: 黑底    1100: 紅字moval, '3'mov[gs:edi], axcallSelectorCallGateTest:0; 測試調用門(有特權級變換),將列印字母 'C'。jmp$SegCodeRing3Lenequ$ - LABEL_CODE_RING3; END of [SECTION .ring3]

 

        83—117初始化任務狀態堆棧段(TSS)

        292—300 通過使用retf完成從Ring0-->Ring3的跳轉,即高特權級跳轉到低特權級,跳轉到LABEL_CODE_RING3。

        380—380 通過調用門,完成從Ring3-->Ring0的跳轉,即低特權級跳轉到高特權級,跳轉到LABEL_SEG_CODE_DEST

        336—336 通過retf,完成從Ring0-->Ring3的跳轉,即高特權級跳轉到低特權級,跳轉到379。

 

        到目前為止,我們已經實現了兩次從高特權級到低特權級以及一次從低特權級到高特權級的轉移,最終在低特權級的代碼中讓程式停住。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.