1. 程式人生 > >第18課 - 深入特權級轉移(上)

第18課 - 深入特權級轉移(上)

初識任務狀態段(Task State Segment)

    處理器所提供的硬體資料結構用於實現多工解決方案     TSS中儲存了 關鍵暫存器的值以及 不同特權級使用的棧     參考:紫陌  https://www.cnblogs.com/guanlaiy/archive/2012/10/25/2738355.html     參考:--Allen--  https://blog.csdn.net/q1007729991/article/details/52650822     

  

TSS中不同特權級的棧資訊

    在TSS中只儲存了 3個棧的資訊,因為它只記錄許可權升級的棧,不可能升級到最低許可權級別     棧備份的是升入的特權級棧資料,退出時再恢復,例如2級升0級就備份還原0級         特權級0:ss0, esp0         特權級1:ss1, esp1         特權級2:ss2, esp2         

特權級轉移時代棧變化

    低特權級->高特權級(呼叫門)         從TSS獲取高特權級目標棧段         將底特權級棧資訊壓入高特權級棧中(ss和esp)     高特權級->底特權級(retf)         將低特權級棧資訊從高特權級中取出並恢復到ss和esp

目標實驗(作業系統雛形)

    底特權級<-->高特權級         定義32位核心程式碼段和資料段(Privilege = 0)
        定義32位任務程式碼段和資料段(Privilege = 3)         由核心程式碼段跳轉到任務程式碼段執行(高特權->低特權)         在任務程式碼段中呼叫高特權級程式碼段列印字串(Call Gate)     

注意事項

    特權級轉移時會發生棧的變換( 如何變換?)     棧段變化需要在TSS結構體中預先定義     TSS結構體作為一個獨立段定義(描述符,選擇子)     在核心程式碼段中載入具體的TSS結構體( ltr TSSSelector) RPL的意義     位置意義         選擇子或段暫存器的最低2位     請求意義         資源請求的特權級(不同於當前特權級CPL)          當需要請求獲取某種資源時,處理器通過CPL,RPL和DPL共同確定請求是否合法!     

小結

    TSS是通過呼叫門轉移到高特權級執行的關鍵     TSS是處理器的 硬體資料結構,用於實現多工     TSS結構體 遵循保護模式下記憶體使用的規則     RPL在請求資源時是合法性判斷的依據之一     處理器使用 CPLRPLDPL共同確定合法性

程式碼

// loader.asm
%include "inc.asm"                            ; 載入標頭檔案,一些常量、設定函式
 
org 0x9000                                    ; 記憶體載入地址
 
jmp ENTRY_SEGMENT                            ; 跳轉到ENTRY_SEGMENT入口處
 
[section .gdt]                                ; 全域性描述符表,部分段基址未知基址需使用時再調節(InitDescItem)
; GDT definition
;                        "函式名"    段基址        段界限                    段屬性
GDT_ENTRY      :        Descriptor    0,            0,                        0                ; 全域性段描述符表第0項不使用
CODE32_DESC     :        Descriptor    0,            Code32SegLen - 1,         DA_C     + DA_32    + DA_DPL0
VIDEO_DESC     :        Descriptor    0xB8000,     0x07FFF,                 DA_DRWA  + DA_32   + DA_DPL3    ; 視訊段描述符表設在正確無須初始化
DATA32_DESC        :        Descriptor    0,            Data32SegLen - 1,        DA_DR    + DA_32   + DA_DPL0
STACK32_DESC    :        Descriptor    0,            TopOfStack32,            DA_DRW   + DA_32    + DA_DPL0
FUNCTION_DESC    :        Descriptor    0,            FunctionSegLen -1,        DA_C    + DA_32    + DA_DPL0
TASK_A_LDT_DESC  :        Descriptor    0,            TaskALdtLen   - 1,        DA_LDT  +            DA_DPL0
TSS_DESC      :        Descriptor    0,            TSSLen - 1,               DA_386TSS +         DA_DPL0
; Call Gate
;                                        選擇子                偏移            引數個數        屬性
FUNC_PRINTSTRING_DESC    :    Gate    FunctionSelector,    PrintString,    0,            DA_386CGate + DA_DPL3
; GDT end
 
GdtLen    equ    $ - GDT_ENTRY
 
GdtPtr:                                        ; 全域性描述符表指標
        dw    GdtLen - 1    ; 偏移,記錄描述符數量
        dd    0            ; 全域性描述符起始地址,先設定為0
 
 
; GDT Selector
;                                TI:全域性、區域性    RPL:請求許可權級別
Code32Selector      equ (0x0001 << 3) + SA_TIG + SA_RPL0    ; 0x0001==第二個選擇子
VideoSelector       equ (0x0002 << 3) + SA_TIG + SA_RPL3    ; 視訊記憶體特權級低只會影響顯示,對系統安全無影響
Data32Selector      equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector     equ (0x0004 << 3) + SA_TIG + SA_RPL0
FunctionSelector    equ (0x0005 << 3) + SA_TIG + SA_RPL0
TaskALdtSelector    equ (0x0006 << 3) + SA_TIG + SA_RPL0
TSSSelector         equ (0x0007 << 3) + SA_TIG + SA_RPL0
; Gate Selector
FuncPrintStringSelector        equ (0x0008 << 3) + SA_TIG + SA_RPL3
 
; end of [section .gdt]
 
[section .tss]                    ; TSS任務段104位元組,另可附加額外資訊
[bits 32]
TSS_SEGMENT:
        dd    0                    ; 保留前一個TSS段選擇子,由CPU填寫,高位填0
        dd    TopOfStack32        ; 0特權級棧指標
        dd    Stack32Selector        ; 0特權級棧段選擇子,只能用低2個位元組,高位填0
        dd    0                    ; 1特權級
        dd    0                    ;
        dd    0                    ; 2特權級
        dd    0                    ;
        times 4 * 18 dd 0        ; 用於切換暫存器的值,由CPU填寫,共18個dd型別
        dw    0                    ; 最低位為除錯陷阱標誌T,其餘為0
        dw    $ - TSS_SEGMENT + 2    ; I/O Map Base Address
        db    0xFF                ; 結束標記,屬於額外資訊
        
TSSLen    equ    $ - TSS_SEGMENT
 
TopOfStack16 equ 0x7c00
 
[section .s16]    ; 真實模式程式碼段(16bit)
[bits 16]        ; 使用16位編譯
ENTRY_SEGMENT:                                ; 16位保護模式入口段
    mov ax, cs                    ; 初始化相關暫存器
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, TopOfStack16
 
            ; initialize GDT for 32 bits code segment
    mov esi, CODE32_SEGMENT        ; 初始化32位程式碼段、資料段、棧段描述符
    mov edi, CODE32_DESC
 
    call InitDescItem
    
    mov esi, DATA32_SEGMENT
    mov edi, DATA32_DESC
 
    call InitDescItem
        
    mov esi, STACK32_SEGMENT
    mov edi, STACK32_DESC
 
    call InitDescItem
    ; 視訊段描述符表設在正確無須初始化
    mov esi, FUNCTION_SEGMENT    ; 初始化函式段描述符
    mov edi, FUNCTION_DESC
    
    call InitDescItem
    
    mov esi, TASK_A_LDT_ENTRY    ; 初始化任務A區域性描述符表
    mov edi, TASK_A_LDT_DESC
 
    call InitDescItem
    
    mov esi, TASK_A_CODE32_SEGMENT    ; 初始化任務A程式碼段、資料段、棧段區域性描述符
    mov edi, TASK_A_CODE32_DESC
 
    call InitDescItem
    
    mov esi, TASK_A_DATA32_SEGMENT
    mov edi, TASK_A_DATA32_DESC
 
    call InitDescItem
    
    mov esi, TASK_A_STACK32_SEGMENT
    mov edi, TASK_A_STACK32_DESC
 
    call InitDescItem
    
    mov esi, TSS_SEGMENT        ; 初始化TSS任務段
    mov edi, TSS_DESC
 
    call InitDescItem
 
            ; initialize GDT pointer struct
    mov eax, 0                    ; 程式碼段地址左移4位
    mov ax, ds
    shl eax, 4
    add eax, GDT_ENTRY            ; 程式碼段偏移地址==> 左移過後的程式碼段+全域性描述符表入口地址偏移量
    mov dword [GdtPtr + 2], eax    ; 寫入全域性描述符表指標
 
            ; 1. load GDT
    lgdt [GdtPtr]                ; 載入全域性描述符表
 
            ; 2. close interrupt
    cli                            ; 關閉中斷
 
            ; 3. open A20
    in al, 0x92                    ; 通過0x92埠開啟A20地址線開關
    or al, 00000010b
    out 0x92, al
 
            ; 4. enter protect mode
    mov eax, cr0                ; 設定cr0暫存器,進入保護模式
    or eax, 0x01
    mov cr0, eax
 
            ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0 ; 使用jmp跳轉到32位程式碼段選擇子的0偏移處
    
 
; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:                                ; 初始化描述符專案
    push eax
    
    mov eax, 0                        ; 程式碼段地址左移4位
    mov ax, cs
    shl eax, 4                        ; 實地址=段暫存器地址左移4位+偏移地址
    add eax, esi
    mov word [edi + 2], ax            ; 將段基址寫入描述符2個位元組(16位暫存器),低32位的16-31bit(偏移2位元組)
    shr eax, 16                        ; 移除eax實地址中已經寫入段基址的2位元組資料
    mov byte [edi + 4], al            ; 將段基址寫入描述符1個位元組(8位暫存器),高32位的0-7bit(偏移4+0=4位元組)
    mov byte [edi + 7], ah            ; 將段基址寫入描述符1個位元組(8位暫存器),高32位的24-31bit(偏移4+3=7位元組)
 
    pop eax
    
    ret
 
[section .dat]
[bits 32]
DATA32_SEGMENT:
    DTOS            db    "D.T.OS!", 0
    DTOS_OFFSET        equ    DTOS - $$
    
Data32SegLen equ $ - DATA32_SEGMENT
 
    
[section .s32]    ; 32位程式碼段
[bits 32]        ; 使用32位編譯
CODE32_SEGMENT:                                ; 32位程式碼段資料
    mov ax, VideoSelector            ; 把視訊段選擇子放到gs全域性段暫存器
    mov gs, ax
 
    mov ax, Data32Selector            ; 設定資料段地址
    mov ds, ax
    
    mov ax, Stack32Selector            ; 設定棧段地址
    mov ss, ax
 
    mov eax, TopOfStack32            ; 設定32位棧頂地址
    mov esp, eax
 
    mov ebp, DTOS_OFFSET            ; 全域性函式列印字串,使用選擇子:偏移量呼叫
    mov bx, 0x0c
    mov dh, 12
    mov dl, 33
    
    call FunctionSelector : PrintString
    
    mov ax, TSSSelector                ; 載入TSS任務狀態段
    
    ltr ax
    
    mov ax, TaskALdtSelector        ; 載入區域性描述符表
    
    lldt ax
    
    push TaskAStack32Selector        ; retf遠呼叫,將棧段選擇子、棧頂指標、程式碼段選擇子、程式碼段偏移量壓入棧中
    push TaskATopOfStack32
    push TaskACode32Selector
    push 0
    retf                            ; pop IP(EIP),POP CS << == >> retf CS:IP(EIP)
    
    jmp $
 
Code32SegLen    equ        $ - CODE32_SEGMENT
    
[section .gs]
[bits 32]
STACK32_SEGMENT:                            ; 32位棧段定義
    times 1024 * 4 db 0
 
Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32 equ Stack32SegLen - 1
 
 
; ==========================================
;
;        Global Function Segment
;
; ==========================================
 
[section .func]
[bits 32]
FUNCTION_SEGMENT:
 
; ds:ebp        --> string address
; bx            --> attribute
; dx            --> dh : row, dl : col
PrintStringFunc:
    push ebp
    push eax
    push edi
    push cx
    push dx
    
Print:
    mov cl, [ds:ebp]
    cmp cl, 0
    je end
    mov eax, 80
    mul dh
    add al, dl
    shl eax, 1
    mov edi, eax
    mov ah, bl
    mov al, cl
    mov [gs:edi], ax
    inc ebp
    inc dl
    jmp Print
    
end:
    pop dx
    pop cx
    pop edi
    pop eax
    pop ebp
 
    retf
    
PrintString        equ PrintStringFunc - $$
 
FunctionSegLen    equ $ - FUNCTION_SEGMENT
 
; =======================================
;
;            Task A code Segment
;
; =======================================
 
[section .task-a-ldt]
; Task A LDT definition
;                                段基址,            段界限,                段屬性
TASK_A_LDT_ENTRY:
TASK_A_CODE32_DESC        :    Descriptor    0,        TaskACode32SegLen - 1,        DA_C   + DA_32 + DA_DPL3
TASK_A_DATA32_DESC        :    Descriptor    0,        TaskAData32SegLen - 1,        DA_DR  + DA_32 + DA_DPL3
TASK_A_STACK32_DESC        :    Descriptor    0,        TaskAStack32SegLen - 1,        DA_DRW + DA_32 + DA_DPL3
 
TaskALdtLen    equ $ - TASK_A_LDT_ENTRY
 
; Task A LDT Selector
TaskACode32Selector        equ    (0x0000 << 3) + SA_TIL + SA_RPL3
TaskAData32Selector        equ (0x0001 << 3) + SA_TIL + SA_RPL3
TaskAStack32Selector    equ (0x0002 << 3) + SA_TIL + SA_RPL3
 
[section .task-a-dat]
[bits 32]
TASK_A_DATA32_SEGMENT:
    TASK_A_STRING            db "This is Task A!", 0
    TASK_A_STRING_OFFSET    equ TASK_A_STRING - $$
 
TaskAData32SegLen equ $ - TASK_A_DATA32_SEGMENT
 
[section .task-a-gs]
[bits 32]
TASK_A_STACK32_SEGMENT:
    times 1024 db 0
 
TaskAStack32SegLen equ $ - TASK_A_STACK32_SEGMENT
TaskATopOfStack32 equ TaskAStack32SegLen - 1
 
[section .task-a-s32]
[bits 32]
TASK_A_CODE32_SEGMENT:
    mov ax, TaskAData32Selector
    mov ds, ax
    
    mov ebp, TASK_A_STRING_OFFSET    ; 使用呼叫門列印字串,使用呼叫門:0呼叫
    mov bx, 0x0c
    mov dh, 14
    mov dl, 29
    
    call FuncPrintStringSelector : 0
    
    jmp $
 
 
TaskACode32SegLen    equ $ - TASK_A_CODE32_SEGMENT
 

輸出

    ndisasm -b 32 -o 0x9000 loader > loader.txt反彙編成32位彙編碼     bochs設定斷點到lldt處,然後檢視執行遠返回前的暫存器獲取執行級別               DS執行在低特權的使用者級,下圖顯示要向高特權的GS視訊記憶體寫顯示字元,所以報錯