一个操作系统的实现-4_保护模式3


特权级:
IA32总共4个特权级,0.1,2,3.数字越小特权级越高
CPL(Current Previlege Level)
DPL(Descriptor Previlege Level)
RPL(Requested Previlege Level)

对于数据段的访问,只要CPL和RPL都小于(也就是特权级高于)被访问的数据段的DPL就可以了.通过修改描述符的属性可以修改DPL,通过修改选择子属性可以修改RPL.

门描述符分为4种:
    调用门
    中断门
    陷阱门
    任务门
其中,中断门和陷阱门是特殊的调用门.

ret和retf:
ret相当于函数return,call短调用返回时用ret,不涉及堆栈的切换.
retf不仅仅弹出EIP,还弹出cs,要恢复cpu运行环境,call长调用实现断间转移时要用retf来返回,retf在保护模式里经常用在系统软件中断调用的返回时,因为涉及到代码执行权限的变化.

call相当于push+jmp,jmp只是实现跳转,但不将cs,ip等压栈,call会有个压栈操作.
jmp的长跳转与短跳转只是结果不同,段内跳转和段间跳转.而call的段内跳转保持eip,段间跳转不仅保存eip还要保存cs.

特权级转换的实现:
call指令结合调用门可实现低特权级到高特权级的转换,但在特权级转换的过程中涉及到不同特权级任务堆栈的切换,高特权级的任务堆栈ss和esp保持在TR任务状态寄存器中(TSS结构,Task-state-stack).
retf指令可实现高特权级到低特权级的转换.
注意:
在call和retf指令执行过程中,CPU自动完成一系列操作完成切换,只要设置合适的TSS结构即可.通过ltr xx指令,将TSS结构数据存到TR任务状态段寄存器中,CPU就可以自动完成堆栈切换,代码段转移等操作.
call指令执行中,CPU完成将ss,sp,cs,ip存入被调用中堆栈中的操作,被调用者堆栈由TR寄存器提供.
retf指令执行中,CPU根据当前任务堆栈中存放的内容(ss,sp,(可能的参数)cs,ip)完成特权级转移的任务.
一般情况下call和ret是成对使用,实现特权级转移我们需要做的只是设置TR寄存器,要是没有call指令,通过下面指令也可以实现高到低特权级的转移:
push SelectorStack3
push TopOfStack3
push SelectorCodeRing3
push 0
retf
模拟call指令时CPU的操作即可.



代码和书中一样,运行结果如下:

一个操作系统的实现-4_保护模式3_第1张图片

pmtest5.asm:


%include    "pm.inc"    ; 常量, 宏, 以及一些说明

org    0100h
    jmp    LABEL_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       ;非一致,32
LABEL_DESC_CODE16:     Descriptor 0,            0ffffh, DA_C           ;非一致,16
LABEL_DESC_CODE_DEST:  Descriptor 0,  SegCodeDestLen-1, DA_C+DA_32       ;非一致,32
LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3
LABEL_DESC_DATA:       Descriptor 0,         DataLen-1, DA_DRW             ;Data
LABEL_DESC_STACK:      Descriptor 0,        TopOfStack, DA_DRWA+DA_32       ;Stack,32
LABEL_DESC_STACK3:     Descriptor 0,       TopOfStack3, DA_DRWA+DA_32+DA_DPL3
LABEL_DESC_LDT:        Descriptor 0,          LDTLen-1, DA_LDT           ;LDT
LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS       ;TSS
LABEL_DESC_VIDEO:      Descriptor 0B8000h,      0ffffh, DA_DRW+DA_DPL3

; 门                                            目标选择子,       偏移, DCount, 属性
LABEL_CALL_GATE_TEST:    Gate          SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3
; GDT 结束

GdtLen        equ    $ - LABEL_GDT    ; GDT长度
GdtPtr        dw    GdtLen - 1    ; GDT界限
        dd    0        ; GDT基地址

; GDT 选择子
SelectorNormal        equ    LABEL_DESC_NORMAL    - LABEL_GDT
SelectorCode32        equ    LABEL_DESC_CODE32    - LABEL_GDT
SelectorCode16        equ    LABEL_DESC_CODE16    - LABEL_GDT
SelectorCodeDest    equ    LABEL_DESC_CODE_DEST    - LABEL_GDT
SelectorCodeRing3    equ    LABEL_DESC_CODE_RING3    - LABEL_GDT + SA_RPL3
SelectorData        equ    LABEL_DESC_DATA        - LABEL_GDT
SelectorStack        equ    LABEL_DESC_STACK    - LABEL_GDT
SelectorStack3        equ    LABEL_DESC_STACK3    - LABEL_GDT + SA_RPL3
SelectorLDT        equ    LABEL_DESC_LDT        - LABEL_GDT
SelectorTSS        equ    LABEL_DESC_TSS        - LABEL_GDT
SelectorVideo        equ    LABEL_DESC_VIDEO    - LABEL_GDT

SelectorCallGateTest    equ    LABEL_CALL_GATE_TEST    - LABEL_GDT + SA_RPL3
; END of [SECTION .gdt]

[SECTION .data1]     ; 数据段
ALIGN    32
[BITS    32]
LABEL_DATA:
SPValueInRealMode    dw    0
; 字符串
PMMessage:        db    "In Protect Mode now. ^-^", 0    ; 进入保护模式后显示此字符串
OffsetPMMessage        equ    PMMessage - $$
StrTest:        db    "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0
OffsetStrTest        equ    StrTest - $$
DataLen            equ    $ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆栈段
[SECTION .gs]
ALIGN    32
[BITS    32]
LABEL_STACK:
    times 512 db 0
TopOfStack    equ    $ - LABEL_STACK - 1
; END of [SECTION .gs]


; 堆栈段ring3
[SECTION .s3]
ALIGN    32
[BITS    32]
LABEL_STACK3:
    times 512 db 0
TopOfStack3    equ    $ - LABEL_STACK3 - 1
; END of [SECTION .s3]


; TSS ---------------------------------------------------------------------------------------------
[SECTION .tss]
ALIGN    32
[BITS    32]
LABEL_TSS:
        DD    0            ; Back
        DD    TopOfStack        ; 0 级堆栈
        DD    SelectorStack        ;
        DD    0            ; 1 级堆栈
        DD    0            ;
        DD    0            ; 2 级堆栈
        DD    0            ;
        DD    0            ; CR3
        DD    0            ; EIP
        DD    0            ; EFLAGS
        DD    0            ; EAX
        DD    0            ; ECX
        DD    0            ; EDX
        DD    0            ; EBX
        DD    0            ; ESP
        DD    0            ; EBP
        DD    0            ; ESI
        DD    0            ; EDI
        DD    0            ; ES
        DD    0            ; CS
        DD    0            ; SS
        DD    0            ; DS
        DD    0            ; FS
        DD    0            ; GS
        DD    0            ; LDT
        DW    0            ; 调试陷阱标志
        DW    $ - LABEL_TSS + 2    ; I/O位图基址
        DB    0ffh            ; I/O位图结束标志
TSSLen        equ    $ - LABEL_TSS
; TSS ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


[SECTION .s16]
[BITS    16]
LABEL_BEGIN:
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax
    mov    sp, 0100h

    mov    [LABEL_GO_BACK_TO_REAL+3], ax
    mov    [SPValueInRealMode], sp

    ; 初始化 16 位代码段描述符
    mov    ax, cs
    movzx    eax, ax
    shl    eax, 4
    add    eax, LABEL_SEG_CODE16
    mov    word [LABEL_DESC_CODE16 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE16 + 4], al
    mov    byte [LABEL_DESC_CODE16 + 7], ah

    ; 初始化 32 位代码段描述符
    xor    eax, eax
    mov    ax, cs
    shl    eax, 4
    add    eax, LABEL_SEG_CODE32
    mov    word [LABEL_DESC_CODE32 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE32 + 4], al
    mov    byte [LABEL_DESC_CODE32 + 7], ah

    ; 初始化测试调用门的代码段描述符
    xor    eax, eax
    mov    ax, cs
    shl    eax, 4
    add    eax, LABEL_SEG_CODE_DEST
    mov    word [LABEL_DESC_CODE_DEST + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE_DEST + 4], al
    mov    byte [LABEL_DESC_CODE_DEST + 7], ah

    ; 初始化数据段描述符
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_DATA
    mov    word [LABEL_DESC_DATA + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_DATA + 4], al
    mov    byte [LABEL_DESC_DATA + 7], ah

    ; 初始化堆栈段描述符
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_STACK
    mov    word [LABEL_DESC_STACK + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_STACK + 4], al
    mov    byte [LABEL_DESC_STACK + 7], ah

    ; 初始化堆栈段描述符(ring3)
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_STACK3
    mov    word [LABEL_DESC_STACK3 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_STACK3 + 4], al
    mov    byte [LABEL_DESC_STACK3 + 7], ah

    ; 初始化 LDT 在 GDT 中的描述符
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_LDT
    mov    word [LABEL_DESC_LDT + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_LDT + 4], al
    mov    byte [LABEL_DESC_LDT + 7], ah

    ; 初始化 LDT 中的描述符
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_CODE_A
    mov    word [LABEL_LDT_DESC_CODEA + 2], ax
    shr    eax, 16
    mov    byte [LABEL_LDT_DESC_CODEA + 4], al
    mov    byte [LABEL_LDT_DESC_CODEA + 7], ah

    ; 初始化Ring3描述符
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_CODE_RING3
    mov    word [LABEL_DESC_CODE_RING3 + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_CODE_RING3 + 4], al
    mov    byte [LABEL_DESC_CODE_RING3 + 7], ah

    ; 初始化 TSS 描述符
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_TSS
    mov    word [LABEL_DESC_TSS + 2], ax
    shr    eax, 16
    mov    byte [LABEL_DESC_TSS + 4], al
    mov    byte [LABEL_DESC_TSS + 7], ah

    ; 为加载 GDTR 作准备
    xor    eax, eax
    mov    ax, ds
    shl    eax, 4
    add    eax, LABEL_GDT        ; eax <- gdt 基地址
    mov    dword [GdtPtr + 2], eax    ; [GdtPtr + 2] <- gdt 基地址

    ; 加载 GDTR
    lgdt    [GdtPtr]

    ; 关中断
    cli

    ; 打开地址线A20
    in    al, 92h
    or    al, 00000010b
    out    92h, al

    ; 准备切换到保护模式
    mov    eax, cr0
    or    eax, 1
    mov    cr0, eax

    ; 真正进入保护模式
    jmp    dword SelectorCode32:0    ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:        ; 从保护模式跳回到实模式就到了这里
    mov    ax, cs
    mov    ds, ax
    mov    es, ax
    mov    ss, ax

    mov    sp, [SPValueInRealMode]

    in    al, 92h        ; ┓
    and    al, 11111101b    ; ┣ 关闭 A20 地址线
    out    92h, al        ; ┛

    sti            ; 开中断

    mov    ax, 4c00h    ; ┓
    int    21h        ; ┛回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS    32]

LABEL_SEG_CODE32:
    mov    ax, SelectorData
    mov    ds, ax            ; 数据段选择子
    mov    ax, SelectorVideo
    mov    gs, ax            ; 视频段选择子

    mov    ax, SelectorStack
    mov    ss, ax            ; 堆栈段选择子

    mov    esp, TopOfStack


    ; 下面显示一个字符串
    mov    ah, 0Ch            ; 0000: 黑底    1100: 红字
    xor    esi, esi
    xor    edi, edi
    mov    esi, OffsetPMMessage    ; 源数据偏移
    mov    edi, (80 * 10 + 0) * 2    ; 目的数据偏移。屏幕第 10 行, 第 0 列。
    cld
.1:
    lodsb
    test    al, al
    jz    .2
    mov    [gs:edi], ax
    add    edi, 2
    jmp    .1
.2:    ; 显示完毕

    call    DispReturn

    ; Load TSS
    mov    ax, SelectorTSS
    ltr    ax    ; 在任务内发生特权级变换时要切换堆栈,而内层堆栈的指针存放在当前任务的TSS中,所以要设置任务状态段寄存器 TR。

    push    SelectorStack3
    push    TopOfStack3
    push    SelectorCodeRing3
    push    0
    retf        ; Ring0 -> Ring3,历史性转移!将打印数字 '3'。

; ------------------------------------------------------------------------
DispReturn:
    push    eax
    push    ebx
    mov    eax, edi
    mov    bl, 160
    div    bl
    and    eax, 0FFh
    inc    eax
    mov    bl, 160
    mul    bl
    mov    edi, eax
    pop    ebx
    pop    eax

    ret
; DispReturn 结束---------------------------------------------------------

SegCode32Len    equ    $ - LABEL_SEG_CODE32
; END of [SECTION .s32]


[SECTION .sdest]; 调用门目标段
[BITS    32]

LABEL_SEG_CODE_DEST:
    mov    ax, SelectorVideo
    mov    gs, ax            ; 视频段选择子(目的)

    mov    edi, (80 * 12 + 0) * 2    ; 屏幕第 12 行, 第 0 列。
    mov    ah, 0Ch            ; 0000: 黑底    1100: 红字
    mov    al, 'C'
    mov    [gs:edi], ax

    ; Load LDT
    mov    ax, SelectorLDT
    lldt    ax

    jmp    SelectorLDTCodeA:0    ; 跳入局部任务,将打印字母 'L'。

    ;retf

SegCodeDestLen    equ    $ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]


; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN    32
[BITS    16]
LABEL_SEG_CODE16:
    ; 跳回实模式:
    mov    ax, SelectorNormal
    mov    ds, ax
    mov    es, ax
    mov    fs, ax
    mov    gs, ax
    mov    ss, ax

    mov    eax, cr0
    and    al, 11111110b
    mov    cr0, eax

LABEL_GO_BACK_TO_REAL:
    jmp    0:LABEL_REAL_ENTRY    ; 段地址会在程序开始处被设置成正确的值

Code16Len    equ    $ - LABEL_SEG_CODE16

; END of [SECTION .s16code]


; LDT
[SECTION .ldt]
ALIGN    32
LABEL_LDT:
;                                         段基址       段界限     ,   属性
LABEL_LDT_DESC_CODEA:    Descriptor           0,     CodeALen - 1,   DA_C + DA_32    ; Code, 32 位

LDTLen        equ    $ - LABEL_LDT

; LDT 选择子
SelectorLDTCodeA    equ    LABEL_LDT_DESC_CODEA    - LABEL_LDT + SA_TIL
; END of [SECTION .ldt]


; CodeA (LDT, 32 位代码段)
[SECTION .la]
ALIGN    32
[BITS    32]
LABEL_CODE_A:
    mov    ax, SelectorVideo
    mov    gs, ax            ; 视频段选择子(目的)

    mov    edi, (80 * 13 + 0) * 2    ; 屏幕第 13 行, 第 0 列。
    mov    ah, 0Ch            ; 0000: 黑底    1100: 红字
    mov    al, 'L'
    mov    [gs:edi], ax

    ; 准备经由16位代码段跳回实模式
    jmp    SelectorCode16:0
CodeALen    equ    $ - LABEL_CODE_A
; END of [SECTION .la]


; CodeRing3
[SECTION .ring3]
ALIGN    32
[BITS    32]
LABEL_CODE_RING3:
    mov    ax, SelectorVideo
    mov    gs, ax            ; 视频段选择子(目的)

    mov    edi, (80 * 14 + 0) * 2    ; 屏幕第 14 行, 第 0 列。
    mov    ah, 0Ch            ; 0000: 黑底    1100: 红字
    mov    al, '3'
    mov    [gs:edi], ax

    call    SelectorCallGateTest:0    ; 测试调用门(有特权级变换),将打印字母 'C'。
    jmp    $
SegCodeRing3Len    equ    $ - LABEL_CODE_RING3
; END of [SECTION .ring3]


你可能感兴趣的:(一个操作系统的实现)