由pmtest5研究特权级切换

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

org 0100h
jmp LABEL_BEGIN

[SECTION .gdt]
; GDT
;                                         段基址,         段界限     , 属性
LABEL_GDT: Descriptor      0,                   0, 0; 空描述符
LABEL_DESC_CODE_RING3:Descriptor      0, SegCodeRing3Len - 1, DA_C + DA_32 + DA_DPL3; 非一致代码段, 32
LABEL_DESC_STACK: Descriptor       0,          TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位;没有标记的stack,实际上是ring 0
LABEL_DESC_STACK3: Descriptor      0,         TopOfStack3, DA_DRWA + DA_32 + DA_DPL3; Stack, 32 位;ring 3
LABEL_DESC_LDT: Descriptor      0,          LDTLen - 1, DA_LDT; LDT
LABEL_DESC_TSS: Descriptor      0,          TSSLen - 1, DA_386TSS; TSS
LABEL_DESC_VIDEO: Descriptor0B8000h,              0ffffh, DA_DRW + DA_DPL3; 显存首地址,为了能在ring 3中读写显存,我们改变了显存段的特权级别


; 门                                            目标选择子,       偏移, DCount, 属性
LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3;门描述符的特权级别也是ring 3,不然没法访问
; GDT 结束

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

; GDT 选择子
SelectorNormal equLABEL_DESC_NORMAL- LABEL_GDT
SelectorCodeRing3equLABEL_DESC_CODE_RING3- LABEL_GDT + SA_RPL3
SelectorStack3equLABEL_DESC_STACK3- LABEL_GDT + SA_RPL3
SelectorTSSequLABEL_DESC_TSS- LABEL_GDT
SelectorCallGateTestequLABEL_CALL_GATE_TEST- LABEL_GDT + SA_RPL3
; END of [SECTION .gdt]
;========================================================================================================

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

===============================================================================================
; 全局堆栈段
[SECTION .gs]
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
; 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
;********************
mov  byte [LABEL_DESC_CODE16 + 7], ah

; 初始化 32 位代码段描述符
; 初始化测试调用门的代码段描述符
; 初始化数据段描述符
; 初始化堆栈段描述符
; 初始化堆栈段描述符(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 中的描述符
; 初始化 LDT 中的描述符
; 初始化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 描述符
xoreax, 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 作准备
; 加载 GDTR
; 关中断
; 打开地址线A20
; 准备切换到保护模式
; 真正进入保护模式
jmp dword SelectorCode32:0; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY: ; 从保护模式跳回到实模式就到了这里
mov ax, cs
关闭 A20 地址线
; 开中断

; ┛回到 DOS
; END of [SECTION .s16]
==============================================================================================

[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS
32]
LABEL_SEG_CODE32:
movax, SelectorData
movds, ax; 数据段选择子
; 视频段选择子
         ; 堆栈段选择子
movesp, TopOfStack

; 下面显示一个字符串
; 显示完毕
call DispReturn
; Load TSS
movax, SelectorTSS

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


pushSelectorStack3 ;ss3
push TopOfStack3 ;sp3
push SelectorCodeRing3 ;cs3
push 0 ;Eip
retf ; Ring0 -> Ring3,历史性转移!将打印数字 ‘3‘。

注意:这一个关键的retf指令,它弹出两个值,一个给EIP,另一个给ECS;通过这种方式,转移到ring 3;这里要特别关注retf指令,它用于长返回,而且可以用于带有特权级别变换的长返回。上面之所以要push 4个参数,是因为retf要用到这四个参数。之所以要loadTSS,是因为待会还要从ring3回到ring 0.


另外,需要特别注意的是此处的TSS;如果没有TSS,我们只能从高特权级别转向低特权级;没法从低特权级别转向高特权级别。也就是说,如果涉及到堆栈切换,那么必须要用到TSS。我们可以将ltr ax这句注释掉,然后查看结果(见最后一副图)。


; ------------------------------------------------------------------------

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


;=======================================================================================================

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

LABEL_SEG_CODE_DEST:
mov ax, SelectorVideo
mov gs, ax; 视频段选择子(目的)
movedi, (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
jmpSelectorLDTCodeA:0; 跳入局部任务,将打印字母 ‘L‘。
;retf
SegCodeDestLenequ$ - LABEL_SEG_CODE_DEST
; END of [SECTION .sdest]
======================================================================================

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN 32
[BITS 16]
LABEL_SEG_CODE16:
; 跳回实模式:
mov ax, SelectorNormal
;******************************************
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 equLABEL_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; 视频段选择子(目的)
movedi, (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; 视频段选择子(目的)
movedi, (80 * 14 + 0) * 2; 屏幕第 14 行, 第 0 列。
mov ah, 0Ch; 0000: 黑底    1100: 红字
mov al, ‘3‘
mov [gs:edi], ax

callSelectorCallGateTest:0; 测试调用门(有特权级变换),将打印字母 ‘C‘。

jmp$ ;注意:程序将停在这里


SegCodeRing3Lenequ$ - LABEL_CODE_RING3
; END of [SECTION .ring3]


程序的大致执行过程如下:

保护模式下进入16b的代码段,ring=0,初始化相关段描述符、门描述符、ldt描述符、数据段描述符、堆栈段描述符、ldt中的描述符、ring 3的代码段描述符TSS描述符;加载gdt,通过长jmp,进入保护模式,进入32b的代码段,ring =0:初始化数据段、堆栈段、视频段的选择子寄存器;显示一个字符串;load TSS;利用retf指令,从ring 0转移到ring 3的代码段:在代码段中显示‘3’,然后通过调用门进入ring 0的代码段:打印字母C,然后通过jmp跳转到局部代码段:打印字母‘L’;程序回到ring 3,成为循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值