(第三章 7)LDT

先展示一下效果图:

直接看代码:

      注意到这段代码是在P38  “3.2.1海阔凭鱼跃”那段代码基础上加上LDT得到的,新添加的代码会着重标注出来的^_^

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

; pmtest3.asm

; 编译方法:nasm pmtest3.asm -o pmtest3.com

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

%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_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1 ; Data

LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位

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

LABEL_DESC_VIDEO:  Descriptor 0B8000h,            0ffffh, DA_DRW ; 显存首地址

; 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

SelectorData equ LABEL_DESC_DATA - LABEL_GDT

SelectorStack equ LABEL_DESC_STACK - LABEL_GDT

SelectorLDT equ LABEL_DESC_LDT - LABEL_GDT

SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT

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

[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, 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

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

; 为加载 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 LDT                              ; 即是,将选择子SelectorLDT加载到寄存器LDTR中

mov ax, SelectorLDT

lldt ax

jmp SelectorLDTCodeA:0 ; 跳入局部任务

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

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]

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

***********************************************************************************************************************************

代码说明及注意:

一、LDT段描述符属性——DA_LDT

DA_LDT      EQU   82h   ; 局部描述符表段类型值

 98h=0000,0000,1000,0010b,也就是将

     TYPE设置为0010,表示为LDT。见P36

     S设置为0,表示是系统段/门描述符(如果S=1,表示是数据段/代码段描述符)。见P36

     P设置为1,表示在内存中存在。见P35

 

二、通过比较LDT和GDT来深入理解LDT

 

1. “LDT的描述符被包含在GDT中”

 

     “LDT段在GDT段中的地位”和“其他段在GDT段中的地位”平等,只不过其特定的属性表明了他的身份——DA_LDT。他就好像一个混迹市井的贤士高人,不显山露水,但深藏绝技;而这个绝技就是LDT段中可以完成和GDT段类似的功能——定义若干段描述符,只不过LDT中定义的段是局部的!

     关键代码:

LABEL_DESC_DATA:   Descriptor       0,       DataLen - 1, DA_DRW+DA_DPL1	; Data
LABEL_DESC_STACK:  Descriptor       0,        TopOfStack, DA_DRWA + DA_32; Stack, 32 位
LABEL_DESC_LDT:    Descriptor       0,        LDTLen - 1, DA_LDT	; LDT
LABEL_DESC_VIDEO:  Descriptor 0B8000h,            0ffffh, DA_DRW	; 显存首地址

     下面在LDT中定义一个局部段描述符,和在GDT中定义段描述符有两点区别:

1)在LDT中,不用定义第一个段为空段(全0);

2)在LDT中,所有段的选择子都需要在后面再“或操作”(“+”) SA_STL。注意到SA_STL  equ  4,故实际上是将LDT中定义的局部段的选择子第2位TI置1(表示从LDT中查找段描述符;TI=0表示从GDT中找)处理!

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

2. “GDT和LDT不干扰程序流程”

    GDT和LDT存在的价值只是负责被查找(查找GDT直接根据GDTR——一步;而查找LDT之前由先要由GDT中LDT的描述符找到LDT的信息——两步),从中找到想要的段的信息。例如,在执行下面代码时,就要查找GDT:

...
[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			; 堆栈段选择子
...
 

三、LDT的价值

     在上例的LDT只有一个代码段。不难想象,我们还可以在其中增加更多的段,例如数据段、堆栈段等,这样一来,我们可以把一个单独的任务所用到的东西封装在一个LDT中,这种思想是我们在后面章节中的多任务处理的一个雏形。

猜你喜欢

转载自chuanwang66.iteye.com/blog/1070763