上篇文章僅介紹如何利用Call Gate,文中並未提及如何實現特權等級的轉換,也就是從低特權等級進入高特權等級,就如同Linux的user space (Privilege 3或稱Ring 3)進入kernel space (Privilege 0或稱Ring 0)。
簡介TSS (Task-State Segment)
本文僅簡單說明TSS在本文被使用的地方,如欲詳盡說明請參考[2]之第七章。
Task Structure
Figure 1為一個工作任務 (Task)的結構圖,一個工作任務分為兩大部份: 1. 工作任務的執行空間 (Task Execution Space),其中包含CS、SS以及若干個DS,也就是Figure 1右上角的三個區段。2. TSS: 由工作任務執行空間與一個儲存空間 (用來儲存工作任務的狀態)所組成。Figure 1又說明另一件事: TSS的Segment Selector必須儲存在Task Register。因此在使用TSS之前,必須使用LTR指令將TSS之Segment Selector載入至Task Register。
Figure 1 Structure of a Task
TSS Memory Layout
Figure 2為32位元TSS記憶體空間配置圖,如下所述:
- ESP0、SS0、ESP1、SS1、ESP2與SS2: 分別為特權等級0、1與2的堆疊區段與堆疊指標,也就是Figure 1右下角三個 'Stack Seg. Priv. Level x'。本文利用ESP0與SS0實現特權等級的轉換 (Ring 3轉換至Ring 0),因此作者僅設定TSS的這兩個欄位,其它欄位則設為0。
- 其它欄位在此不進一步探討,有興趣的網友可以參考[2]。
原始碼下載
由於篇幅的關係,往後該系列文章將不張貼程式碼,將提供連結下載方式,原始碼下載點。作業系統程式碼說明
Figure 3為作業系統程式碼解說圖,此圖說明GDT與LDT的記憶體配置圖與程式流程圖,其流程圖並非以傳統方式表示。取而代之,改以紅色圈圈的數字代表程式流程並搭配GDT與LDT記憶體配置圖,如此更能清楚地明白程式執行流程。底下將針對每一步驟 (紅色圈圈的數字)做詳盡的解釋:
Figure 3 High Level Perspective of OS code
- 使用ljmp指令由真實模式轉換至32位元保護模式 (Ring 0)。其片段程式碼如下所示:
/* Jump to protected-mode OS code * * ljmpl prototype: * * ljmpl segment_selector_of_CS offset */ ljmpl $SegSelectorCode32, $0
此區段程式碼將Msg1輸出至螢幕,並將LDT的segment selector載入至LDTR,接著使用lcall指令跳至LDT的CS,如下所示:
/* Load LDT selector */ mov $(SegSelectorLDT), %ax /* Load LDT selector in GDT to LDT register */ lldt %ax /* Jump to code segment in LDT */ lcall $(SegSelectorLDTCode32), $0
- 如第1點所述。
- 如第1點所述。
- 輸出Msg2,隨即使用lret指令返回LABEL_GDT_CODE。
- 通常,call與ret指令必須配合使用。執行call指令時,處理器會將SS、ESP、CS與EIP推入(Push) 該執行空間的堆疊 (Stack)。接著,執行ret指令時,處理器會將EIP、CS、ESP與SS從堆疊取出 (Pop)。為了實現從Ring 0返回Ring 3,底下程式碼模擬處理器的工作: 將Ring 3的SS、ESP、CS與EIP推入(Push) 該執行空間的堆疊 (Stack)並使用lret指令返回Ring 3。
pushl $(SegSelectorStackR3) pushl $(TopOfStackR3) pushl $(SegSelectorCodeR3) pushl $0 lret
- 從Ring 0 (LABEL_GDT_CODE)返回 Ring 3 (LABEL_GDT_CODE_R3)。
- 此Ring 3程式區段將Msg1_R3輸出至螢幕,利用call gate並搭配TSS成功地返回Ring 0程式區段。因此,必須設定TSS之ESP0與SS0欄位,如下所示:
在此,特別對特權等級轉換做進一步解釋,以便讓讀者了解為什麼只要設定ESP0與SS0欄位即可。當存取目的程式區段 (Destination Code Segment)是被允許的,處理器便會根據Call Gate Descriptor的Segment Selector欄位找出對應的程式區段,如果又涉及特權等級轉換,處理器會將原本使用中的堆疊切換至目標特權等級的堆疊。舉例來說,假設目前程式區段運行於Ring 3,處理器便使用Ring 3的堆疊,當使用Call Gate欲轉換至Ring 0的程式區段。如果此要求是被允許的,則處理器會跳至Ring 0的程式區段並使用Ring 0的堆疊,因此程式設計員必須先設定Ring 0堆疊 (ESP0與SS0),這就是為什麼必須設定TSS的ESP與SS0欄位的原因。LABEL_TSS: .4byte 0 /* Previous Task Link */ .4byte TopOfStackR0 /* ESP0 */ .4byte SegSelectorStackR0 /* SS0 */ .4byte 0 /* ESP1 */ .4byte 0 /* SS1 */ .4byte 0 /* ESP2 */ .4byte 0 /* SS2 */ .4byte 0 /* CR3 (PDBR) */ .4byte 0 /* EIP */ .4byte 0 /* EFLAGS */ .4byte 0 /* EAX */ .4byte 0 /* ECX */ .4byte 0 /* EDX */ .4byte 0 /* EBX */ .4byte 0 /* ESP */ .4byte 0 /* EBP */ .4byte 0 /* ESI */ .4byte 0 /* EDI */ .4byte 0 /* ES */ .4byte 0 /* CS */ .4byte 0 /* SS */ .4byte 0 /* DS */ .4byte 0 /* FS */ .4byte 0 /* GS */ .4byte 0 /* LDT Segment Selector */ .2byte 0 /* Reserved */ .2byte (. - LABEL_TSS + 2) /* I/O Map Base Address */ .set TSSLen, (. - LABEL_TSS)
- 跳至GDT之Call Gate的描述子 (Descriptor)。
- 根據Call Gate描述子的Segment Selector欄位找出對應的CS。
- 將Msg3輸出至螢幕並返回。
- 返回Ring 3程式區段,並執行一無窮迴圈。
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
沒有留言:
張貼留言