Chinaunix首页 | 论坛 | 博客
  • 博客访问: 113046
  • 博文数量: 23
  • 博客积分: 2092
  • 博客等级: 大尉
  • 技术积分: 305
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-10 19:40
文章分类
文章存档

2011年(3)

2010年(17)

2009年(3)

我的朋友

分类:

2010-08-25 18:59:16

一、从保护模式返回

 

因为在准备结束保护模式回到实模式之前,需要加载一个合适的描述符选择子到有关的寄存器,以使对应的段描述符高速缓冲寄存器中含有合适的段界限和属性,而且,不能从32的代码返回到实模式,只能从16位代码段中返回。因此呢需新增一Normal描述符,在返回实模式之前把对应的SelectorNormal加载到DS,EC,SS中。

 

LABEL_DESC_NORMAL

; GDT

;                            段基址,        段界限 , 属性

LABEL_GDT:         Descriptor    0,              0, 0         ; 空描述符

LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW    ; Normal 描述符

; GDT 选择子

SelectorNormal             equ  LABEL_DESC_NORMAL     - LABEL_GDT

 

保护模式到实模式的前期准备工作

       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

 

 跳回实模式:

       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]

 

保护模式跳回到实模式

 

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]

 

 

关于 jmp       0:LABEL_REAL_ENTRY

 

jmp  0:LABEL_REAL_ENTRY

 

 

上图所示是第四个字节是SEGMENT的值,而开始的时候呢

mov [LABEL_GO_BACK_TO_REAL+3], ax 指令已经将CS设置为16位的代码。因此就保证了程序的正确返回。

 

二、LDT

LDT的使用

1、在GDT中增加LDT的描述符以及对应的LDT描述符选择子以及对这个LDT描述符的初始化代码。

 

GDT中增加LDT的描述符

[SECTION .gdt]

; GDT

;                                         段基址,       段界限     , 属性

LABEL_GDT:         Descriptor       0,                 0, 0        ; 空描述符

………………………………

LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT      ; LDT

………………………………

LDT GDT描述符选择子

 

; GDT 选择子

…………

SelectorLDT          equ  LABEL_DESC_LDT             - LABEL_GDT

…………

 

       初始化 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

2、增加一个LDT描述符表

 

; 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]

 

3、增加一个LDT得代码段

 

; CodeA (LDT, 32 位代码段)

[SECTION .la]

ALIGN    32

[BITS     32]

LABEL_CODE_A:

       mov ax, SelectorVideo

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

 

       mov edi, (80 * 12 + 0) * 2     ; 屏幕第 10 , 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]

 

4、使用前用LLDT指令加载ldtr,用jmp指令跳转方式运行。

 

三、特权级

1、通过jmp或者call进行直接转移

1)、如果目标代码是非一致代码段,要求CPL必须等于目标段的DPL,同时要求RPL小于等于DPL;

(2)、如果目标是一致代码段,则要求CPL大于或者等于目标代码段的DPLRPL此时不做检查。

2、调用门使用方法(通过调用门可以实现从低特权级到高特权级的转移)

1)、增加一个代码段作为通过调用门转移的目标代码

 

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

[BITS     32]

 

LABEL_SEG_CODE_DEST:

       ;jmp $

       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

 

       retf

 

SegCodeDestLen    equ  $ - LABEL_SEG_CODE_DEST

; END of [SECTION .sdest]

2)、加入该段的描述符,选择子以及初始化的代码

描述符:

LABEL_DESC_CODE_DEST: Descriptor 0,SegCodeDestLen-1, DA_C+DA_32; 非一致代码段,32

选择子:

SelectorCodeDest   equ  LABEL_DESC_CODE_DEST       - LABEL_GDT

初始化代码:

       ; 初始化测试调用门的代码段描述符

       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

3)、增加调用门以及选择子

;                                目标选择子,偏移,DCount, 属性

LABEL_CALL_GATE_TEST: Gate SelectorCodeDest,   0,     0, DA_386CGate+DA_DPL0

选择子:

SelectorCallGateTest      equ  LABEL_CALL_GATE_TEST - LABEL_GDT

4)、使用调用门:

 

       ; 测试调用门(无特权级变换),将打印字母 'C'

       call  SelectorCallGateTest:0

 

3ring0->ring3->ring0

使用ret指令可以实现有高特权级到低特权级的转移。

我不知道出现了什么问题,就是编译的时候一直往.com里面写东西写到G以上都没停!!!用光盘的abc历程也是出现这样的情况,不知道作者是什么编译出来的。没找到原因。只好跳过了,用pmtest5.asm了。

1)、添加ring3的代码段和ring3的堆栈段以及它们的描述符,选择子,初始化。

描述符

LABEL_DESC_CODE_RING3: Descriptor 0, SegCodeRing3Len-1, DA_C+DA_32+DA_DPL3

LABEL_DESC_STACK3:  Descriptor 0,       TopOfStack3, DA_DRWA+DA_32+DA_DPL3

 

选择子

SelectorCodeRing3 equ  LABEL_DESC_CODE_RING3     - LABEL_GDT + SA_RPL3

SelectorStack3              equ  LABEL_DESC_STACK3       - LABEL_GDT + SA_RPL3

 

 

初始化

 

       ; 初始化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

 

 

 

       ; 初始化堆栈段描述符(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

 

两个ring3目标段

; 堆栈段ring3

[SECTION .s3]

ALIGN    32

[BITS     32]

LABEL_STACK3:

       times 512 db 0

TopOfStack3  equ  $ - LABEL_STACK3 - 1

; END of [SECTION .s3]

 

 

; 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]

 

2)、ring0->ring3

 

       push       SelectorStack3

       push       TopOfStack3

       push       SelectorCodeRing3

       push       0

       retf         ; Ring0 -> Ring3,历史性转移!将打印数字 '3'

(3)以上完成了ring0->ring3的转移,在ring3又完成到ring0的转移,因为从低特权级到高特权级的转移需要TSS.因此呢还要完成TSS代码。

描述符

LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS        ;TSS

选择子

SelectorTSS          equ  LABEL_DESC_TSS             - LABEL_GDT

 

TSS段,这个是根据TSS的格式来填充的,因为在历程中只用到ring0的堆栈,因此只初始化ring0的堆栈。

; 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

初始化 TSS 描述符:

       ; 初始化 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

 

4)、加载TSS

       ; Load TSS

       mov ax, SelectorTSS

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

5)初始化调用门

ring3打印出‘3’之后,再使用调用门打印字母 'C',之后再跳入局部任务,将打印字母 'L'。因此还要初始化调用门。

描述符

LABEL_DESC_CODE_DEST:  Descriptor 0,  SegCodeDestLen-1, DA_C+DA_32;非一致,32

;                                             目标选择子,       偏移, DCount, 属性

LABEL_CALL_GATE_TEST: Gate          SelectorCodeDest,          0,      0, DA_386CGate + DA_DPL3

选择子:

SelectorCodeDest   equ  LABEL_DESC_CODE_DEST       - LABEL_GDT

SelectorCallGateTest      equ  LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3

初始化略。

同时因为是在ring3中显示所以要将LABEL_DESC_VIDEO改为ring3下。

LABEL_DESC_VIDEO:      Descriptor 0B8000h,      0ffffh, DA_DRW+DA_DPL3

程序的流程是:

先打印”In Protect Mode now”à到加载TSSàRing0 Ring3à打印数字 '3'à call  SelectorCallGateTest:0   ; 测试调用门(有特权级变换),将打印字母 'C'à       jmp  SelectorLDTCodeA:0     ; 跳入局部任务,将打印字母 'L'

这样按道理说输出的顺序是

In Protect Mode now

3

C

L

 

但是实际屏幕输出顺序是

In Protect Mode now

C

L

3

为什么是这样呢?

阅读(1897) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~