第20课 - 深入特权级转移(下)

深入理解调用门

    调用门用于 向高特权级的代码段转移(CPL >= DPLobject)
    调用门描述符的特权级 低于当前特权级(CPL <= DPLgate)
    

关于调用门的注意事项之一

    调用门支持特权级 同级转移
    调用门 同级转移被处理为普通函数调用或直接跳转
    call通过调用门能提升特权级, jmp通过调用门只能同级转移
    通过调用门降特权级返回( retf)时
        对目标代码段以及栈段进行 特权级检查
        对相关段寄存器 强制清零(指向高特权级数据的段寄存器)

小结

    调用门的使用(数值上): DPLobject <= CPL <= DPLgate
    调用门支持同级转移( jmp指令只能同级转移)
    retf对栈的检查: CS.RPL == SS.RPL == SS.DPL
    特权级降低转移时,相关段寄存器的值将被清零

代码

// loader.asm
%include "inc.asm"                            ; 加载头文件,一些常量、设置函数
 
org 0x9000                                    ; 内存加载地址
 
jmp ENTRY_SEGMENT                            ; 跳转到ENTRY_SEGMENT入口处
 
[section .gdt]                                ; 全局描述符表,部分段基址未知基址需使用时再调节(InitDescItem)
; GDT definition        8Byte 64bit
;                        "函数名"    段基址        段界限                    段属性
GDT_ENTRY        :        Descriptor    0,            0,                        0                ; 全局段描述符表第0项不使用
CODE32_DESC        :        Descriptor    0,            Code32SegLen - 1,        DA_C    + DA_32    + DA_DPL2
VIDEO_DESC        :        Descriptor    0xB8000,    0x07FFF,                DA_DRWA + DA_32    + DA_DPL2; 视频段描述符表设置正确无须初始化
DATA32_DESC_0    :        Descriptor    0,            Data32SegLen0 - 1,        DA_DR    + DA_32    + DA_DPL0
DATA32_DESC_2    :        Descriptor    0,            Data32SegLen2 - 1,        DA_DR    + DA_32    + DA_DPL2
STACK32_DESC_0    :        Descriptor    0,            TopOfStack320,            DA_DRW  + DA_32    + DA_DPL0
STACK32_DESC_2    :        Descriptor    0,            TopOfStack322,            DA_DRW  + DA_32    + DA_DPL2
TSS_DESC        :        Descriptor    0,            TSSLen     -         1,        DA_386TSS        + DA_DPL0
FUNCTION_DESC    :        Descriptor    0,            FunctionSegLen -1,        DA_C    + DA_32 + 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        2Byte 16bit    TI:全局、局部    RPL:请求权限级别
Code32Selector        equ    (0x0001 << 3) + SA_TIG + SA_RPL2    ; 0x0001==第二个选择子
VideoSelector        equ (0x0002 << 3) + SA_TIG + SA_RPL2    ; 显存特权级低只会影响显示,对系统安全无影响
Data32Selector0        equ (0x0003 << 3) + SA_TIG + SA_RPL0
Data32Selector2        equ (0x0004 << 3) + SA_TIG + SA_RPL2
Stack32Selector0    equ (0x0005 << 3) + SA_TIG + SA_RPL0
Stack32Selector2    equ (0x0006 << 3) + SA_TIG + SA_RPL2
TSSSelector            equ (0x0007 << 3) + SA_TIG + SA_RPL0
FunctionSelector    equ (0x0008 << 3) + SA_TIG + SA_RPL0
; Gate Selector
FuncPrintStringSelector    equ    (0x0009 << 3) + SA_TIG + SA_RPL3
; end of [section .gdt]
 
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_0
    mov edi, DATA32_DESC_0
 
    call InitDescItem
    
    mov esi, DATA32_SEGMENT_2
    mov edi, DATA32_DESC_2
    
    call InitDescItem
        
    mov esi, STACK32_SEGMENT_0
    mov edi, STACK32_DESC_0
 
    call InitDescItem
 
    mov esi, STACK32_SEGMENT_2
    mov edi, STACK32_DESC_2
 
    call InitDescItem
    
    mov esi, FUNCTION_SEGMENT
    mov edi, FUNCTION_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. load TSS
    mov ax, TSSSelector
    ltr ax
 
            ; 6. jump to 32 bits code
    push Stack32Selector2        ;jmp dword Code32Selector : 0 ; 使用jmp跳转到32位代码段选择子的0偏移处
    push TopOfStack322
    push Code32Selector
    push 0
    retf                        ; 弹出2个栈,分别给IP、CS寄存器
    
    
 
; 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 .dat0]
[bits 32]
DATA32_SEGMENT_0:
    DPL0            db    "DPL = 0", 0
    DPL0_OFFSET        equ    DPL0 - $$
    
Data32SegLen0 equ $ - DATA32_SEGMENT_0
 
[section .dat2]
[bits 32]
DATA32_SEGMENT_2:
    DPL2            db    "DPL = 2", 0
    DPL2_OFFSET        equ    DPL2 - $$
    
Data32SegLen2 equ $ - DATA32_SEGMENT_2
 
[section .tss]                    ; TSS任务段104字节,另可附加额外信息
[bits 32]
TSS_SEGMENT:
        dd    0                    ; 保留前一个TSS段选择子,由CPU填写,高位填0
        dd    TopOfStack320        ; 0特权级栈指针
        dd    Stack32Selector0    ; 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
 
[section .s32]    ; 32位代码段
[bits 32]        ; 使用32位编译
CODE32_SEGMENT:                                ; 32位代码段数据
    mov ax, VideoSelector            ; 把视频段选择子放到gs全局段寄存器
    mov gs, ax
 
    mov ax, Data32Selector2            ; 设置数据段地址
    mov ds, ax
    
    mov ebp, DPL2_OFFSET            ; 全局函数打印字符串,使用选择子:偏移量调用
    mov bx, 0x0c
    mov dh, 12
    mov dl, 33
    
    call FuncPrintStringSelector : 0
    
    jmp $
 
Code32SegLen    equ        $ - CODE32_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             ; 总偏移字符数,al太小超过255会溢出(3*80+16==3行16列)
    push dx                 ; 备份dx(dh),供循环调用避免丢失行号
    mov dh, 0               ; dx高位置0,确保dx=dl
    add ax, dx              ; 总偏移字符数(65536个字符)
    pop dx
    shl eax, 1                ; 每个字符占2字节
    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
    
    mov ax, Data32Selector0
    mov ds, ax
    mov gs, ax
 
    retf
    
PrintString        equ PrintStringFunc - $$
 
FunctionSegLen    equ $ - FUNCTION_SEGMENT
    
[section .gs0]
[bits 32]
STACK32_SEGMENT_0:                            ; 32位栈段定义
    times 1024 * 4 db 0
 
Stack32SegLen0 equ $ - STACK32_SEGMENT_0
TopOfStack320 equ Stack32SegLen0 - 1
 
[section .gs2]
[bits 32]
STACK32_SEGMENT_2:                            ; 32位栈段定义
    times 1024 * 4 db 0
 
Stack32SegLen2 equ $ - STACK32_SEGMENT_2
TopOfStack322 equ Stack32SegLen2 - 1
 
 

猜你喜欢

转载自www.cnblogs.com/Dua677/p/10054928.html