保护模式中的特权级(中)

门描述符

通过门描述符可以在不同特权级的代码间进行跳转。根据应用场景的不同,门描述符可以分为:调用门(Call Gates)、中断门(Interrupt Gate)、陷阱门(Trap Gate)、任务门(Task Gate)。

门描述符的内存结构

1. 每一个门描述符占用8字节内存。

2. 不同类型的门描述符的内存含义不同。

通过调用门选择子可以得到门描述符里段选择子,通过段选择子我们可以得到这个段的段基地址,段基地址加上偏移地址就是函数的入口地址。

 

当我们需要执行该段之外的函数调用时,需要 call 段基址 : 偏移地址 这样调用。 

代码如下

%include "inc.asm"

org 0x9000

jmp CODE16_SEGMENT

[section .gdt]
; GDT definition
;                                 段基址,       段界限,         段属性
GDT_ENTRY		: Descriptor		0, 			    0,		        0
CODE32_DES		: Descriptor		0, 	   Code32SegLen - 1,    DA_32 + DA_C
VIDEO_DES	    : Descriptor     0xb8000,       0x7fff,         DA_32 + DA_DRWA
STACK32_DESC	: Descriptor        0,      TopOfStack32,       DA_32 + DA_DRW
FUNCTION_DESC   : Descriptor        0,     FunctionSegLen - 1,  DA_32 + DA_C
; Gate Descriptor
; Call Gate            			  选择子,			偏移地址,			参数个数,			属性
FUNC_CG_ADD_DESC    Gate      FunctionSelector,      CG_Add,               0,           DA_386CGate
FUNC_CG_SUB_DESC    Gate      FunctionSelector,      CG_Sub,               0,           DA_386CGate
; GDT end  

GdtLen	  equ	   $ - GDT_ENTRY
GdtPtr:    
		dw	GdtLen - 1
		dd	0

; GDT Selector
Code32Selector	  equ    (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector	  equ    (0x0002 << 3) + SA_TIG + SA_RPL0
Stack32Selector   equ    (0x0003 << 3) + SA_TIG + SA_RPL0
FunctionSelector  equ    (0x0004 << 3) + SA_TIG + SA_RPL0
FuncCGAddSelector equ    (0x0005 << 3) + SA_TIG + SA_RPL0
FuncCGSubSelector equ    (0x0006 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]

TopOfStackInit	equ    0x7c00

[section .s16]
[bits 16]
CODE16_SEGMENT:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, TopOfStackInit

	; initialize GDT for 32 bits code segment
	mov esi, CODE32_SEGMENT
	mov edi, CODE32_DES
	call InitDescItem

	mov esi, STACK32_SEGMENT
	mov edi, STACK32_DESC
	call InitDescItem

	mov esi, FUNCTION_SEGMENT
	mov edi, FUNCTION_DESC
	call InitDescItem

	; initalize GDT pointer struct
	mov eax, 0
	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
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0

; esi    --> code segment label
; edi    --> descriptor label
InitDescItem:
	push eax

	mov eax, 0
	mov ax, cs
	shl eax, 4
	add eax, esi
	mov word [edi + 2], ax
	shr eax, 16
	mov byte [edi + 4], al
	mov byte [edi + 7], ah
	
	pop eax

	ret

[section .s32]
[bits 32]
CODE32_SEGMENT:
	mov ax, VideoSelector
	mov gs, ax

	mov ax, Stack32Selector
	mov ss, ax

	mov eax, TopOfStack32
	mov esp, eax

	mov ax, 3
	mov bx, 1

	call FuncCGAddSelector : 0
	call FuncCGSubSelector : 0

	jmp $


Code32SegLen	equ    $ - CODE32_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:
	times 1024 * 4 db 0

Stack32SegLen	equ    $ - STACK32_SEGMENT
TopOfStack32    equ    Stack32SegLen - 1

[section .func]
[bits 32]
FUNCTION_SEGMENT:

; ax    --> a
; bx    --> b
; return
;       cx = a + b
AddFunc:
	mov cx, ax
	add cx, bx
	retf

CG_Add		equ		AddFunc - $$

; ax    --> a
; bx    --> b
; return
;       cx = a - b
SubFunc:
	mov cx, ax
	sub cx, bx
	retf

CG_Sub		equ		SubFunc - $$

FunctionSegLen		equ		$ - FUNCTION_SEGMENT

134-160行,我们新增的一个func段,这个段我们定义了2个函数。第15行,我们指定了这个段的段描述符。第30行,我们制定了这个段的选择子。

17-18行,我们定义了func段里2个函数的门描述符。31-32行定义它们的选择子。

118行我们调用了func段的加法函数,119行我们调用了func段的减法函数。

我们通过断点调试,查看相关寄存器的值来查看函数是否调用成功。

 在函数调用之前eax寄存器的值为3,ebx寄存器的值为1,ecx的值为15。

 我们成功跳转到了AddFunc处,在函数调用之后eax寄存器的值为3,ebx寄存器的值为1,ecx寄存器的值为4,说明了加法函数调用成功,我们继续单步调试,来查看下一个减法函数的调用情况。

 我们成功跳转到了SubFunc处,函数执行完以后,ecx寄存器的值为2,则说明减法函数也执行成功了。

我们将118-119行改为以下2行代码

call FunctionSelector : CG_Add
call FunctionSelector : CG_Sub

 执行结果是和上面的结果是一样的。

实验结论

1. 门描述符是一种特殊的描述符,需要注册于段描述符表

2. 调用门可以看作一个函数指针(保存具体函数的入口地址)

3. 通过调用门选择子对相应的函数进行远调用(call far)

4. 可以直接使用选择子 : 偏移地址的方式调用其他段的函数

5. 使用调用门时偏移地址无意义,仅仅是语法需要。

在14节课中,保护模式在不同的段中调用相同的函数,我们是通过复制粘贴来进行的,那么如何在不同的段中进行代码复用呢?

解决方案

1. 将不同的代码段需要复用的函数定义到独立的段中(retf)。

2. 计算每一个可复用函数的偏移量(FuncName - $$)。

3. 通过段选择子 : 偏移地址的方式对目标函数进行远调用。

下面贴出代码

%include "inc.asm"

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]
; GDT definition
;                                 段基址,       段界限,         段属性
GDT_ENTRY		: Descriptor		0, 			    0,		        0
CODE32_DESC		: Descriptor		0, 	   Code32SegLen - 1,    DA_32 + DA_C
VIDEO_DESC	    : Descriptor     0xb8000,       0x7fff,         DA_32 + DA_DRWA
DATA32_DESC		: Descriptor	    0,     Data32SegLen - 1,    DA_32 + DA_DR
STACK32_DESC	: Descriptor        0,      TopOfStack32,       DA_32 + DA_DRW
CODE16_DESC     : Descriptor        0,          0xffff,         DA_C
UPDATE_DESC     : Descriptor        0,          0xffff,         DA_DRW
TASK_A_LDT_DESC : Descriptor        0,     TaskALdtLen - 1,     DA_LDT
FUNCTION_DESC   : Descriptor        0,    FunctionSegLen - 1,   DA_32 + DA_C
; GDT end  

GdtLen	  equ	   $ - GDT_ENTRY
GdtPtr:    
		dw	GdtLen - 1
		dd	0

; GDT Selector
Code32Selector	    equ    (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector	    equ    (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector      equ    (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector     equ    (0x0004 << 3) + SA_TIG + SA_RPL0
Code16Selector      equ    (0x0005 << 3) + SA_TIG + SA_RPL0
UpdateSelector      equ    (0x0006 << 3) + SA_TIG + SA_RPL0
TaskALdtSelector	equ    (0x0007 << 3) + SA_TIG + SA_RPL0
FunctionSelector	equ    (0x0008 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]

TopOfStack16	equ    0x7c00

[section .dat]
[bits 32]
DATA32_SEGMENT:
	DTOS		        db    "D.T.OS!", 0
	DTOS_Offset	        equ	  DTOS - $$ 

	HELLOWORLD		    db    "Hello, World!", 0
	HELLOWORLD_Offset   equ   HELLOWORLD - $$

Data32SegLen	equ   $ - DATA32_SEGMENT

[section .s16]
[bits 16]
ENTRY_SEGMENT:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, TopOfStack16

	mov [BACK_TO_REAL_MODE + 3], ax

	; initialize GDT for 32 bits code segment
	mov esi, CODE32_SEGMENT
	mov edi, CODE32_DESC
	call InitDescItem

	mov esi, DATA32_SEGMENT
	mov edi, DATA32_DESC
	call InitDescItem

	mov esi, STACK32_SEGMENT
	mov edi, STACK32_DESC
	call InitDescItem

	mov esi, CODE16_SEGMENT
	mov edi, CODE16_DESC
	call InitDescItem

	mov esi, TASK_A_LDT_ENTRY
	mov edi, TASK_A_LDT_DESC
	call InitDescItem

	mov esi, TASK_A_CODE32_SEGMENT
	mov edi, TASK_A_CODE32_DESC
	call InitDescItem

	mov esi, TASK_A_DATA32_SEGMENT
	mov edi, TASK_A_DATA32_DESC
	call InitDescItem

	mov esi, TASK_A_STACK32_SEGMENT
	mov edi, TASK_A_STACK32_DESC
	call InitDescItem

	mov esi, FUNCTION_SEGMENT
	mov edi, FUNCTION_DESC
	call InitDescItem
	

	; initalize GDT pointer struct
	mov eax, 0
	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
    or al, 00000010b
    out 0x92, al
    
    ; 4. enter protect mode
    mov eax, cr0
    or eax, 0x01
    mov cr0, eax
    
    ; 5. jump to 32 bits code
    jmp dword Code32Selector : 0

BACK_ENTRY_SEGMENT:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, TopOfStack16

	in al, 0x92
    and al, 11111101b
    out 0x92, al
    
    sti

	mov bp, HELLOWORLD
	mov cx, 13
	mov dx, 0
	mov ax, 0x1301
	mov bx, 0007
	int 0x10

	jmp $
	

; esi    --> code segment labelBACK_ENTRY_SEGMENT
; edi    --> descriptor label
InitDescItem:
	push eax

	mov eax, 0
	mov ax, cs
	shl eax, 4
	add eax, esi
	mov word [edi + 2], ax
	shr eax, 16
	mov byte [edi + 4], al
	mov byte [edi + 7], ah
	
	pop eax

	ret

[section .s32]
[bits 32]
CODE32_SEGMENT:
	mov ax, VideoSelector
	mov gs, ax

	mov ax, Stack32Selector
	mov ss, ax

	mov eax, TopOfStack32
	mov esp, eax

	mov ax, Data32Selector
	mov ds, ax

	mov ebp, DTOS_Offset
	mov bx, 0x0c
	mov dh, 13
	mov dl, 33
	call FunctionSelector : PrintStringOffset

	mov ebp, HELLOWORLD_Offset
	mov bx, 0x0c
	mov dh, 14
	mov dl, 30
	call FunctionSelector : PrintStringOffset
	
	mov ax, TaskALdtSelector
	lldt ax
	jmp TaskACode32Selector : 0

	; jmp Code16Selector : 0


Code32SegLen	equ    $ - CODE32_SEGMENT

[section .s16]
[bits 16]
CODE16_SEGMENT:
	mov ax, UpdateSelector
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov gs, ax
	mov fs, ax
	
	mov eax, cr0
    and al, 11111110b
    mov cr0, eax

BACK_TO_REAL_MODE:
	jmp 0 : BACK_ENTRY_SEGMENT

Code16SegLen	equ    $ - CODE16_SEGMENT

[section .gs]
[bits 32]
STACK32_SEGMENT:
	times 1024 * 4 db 0

Stack32SegLen	equ    $ - STACK32_SEGMENT
TopOfStack32    equ    Stack32SegLen - 1

[section .func]
[bits 32]
FUNCTION_SEGMENT:

; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintStringFunc:
	push ebp
	push cx
	push eax
	push dx
	push edi

print:
	mov cl, [ds:ebp]
	cmp cl, 0
	je end
	mov eax, 80
	mul dh
	add al, dl
	shl eax, 1
	mov edi, eax
	mov ah, bl
	mov al, cl
	mov [gs:edi], ax
	inc ebp
	inc dl
	jmp print
	
end:
	pop edi
	pop dx
	pop eax
	pop cx
	pop ebp

	retf

PrintStringOffset	equ		PrintStringFunc - $$
FunctionSegLen		equ		$ - FUNCTION_SEGMENT

; ===================================================
;
;				TASK A CODE SEGMENT
;
; ===================================================

;Task A LDT definition
;									段基址				 段界限				   段属性
TASK_A_LDT_ENTRY:
TASK_A_CODE32_DESC	 Descriptor		  0,            TaskACode32SegLen - 1,    DA_32 + DA_C
TASK_A_DATA32_DESC	 Descriptor       0,            TaskAData32SegLen - 1,    DA_32 + DA_DR
TASK_A_STACK32_DESC  Descriptor       0,            TaskAStack32SegLen - 1,   DA_32 + DA_DRW

TaskALdtLen		equ		$ - TASK_A_LDT_ENTRY

;Task A LDT Selector
TaskACode32Selector		equ		(0x0000 << 3) + SA_TIL + SA_RPL0
TaskAData32Selector		equ		(0x0001 << 3) + SA_TIL + SA_RPL0
TaskAStack32Selector	equ		(0x0002 << 3) + SA_TIL + SA_RPL0

[section .task-a-data]
[bits 32]
TASK_A_DATA32_SEGMENT:
	TaskAString		    db		"This is Task A!", 0
	TaskAStringOffset   equ     TaskAString - $$
TaskAData32SegLen    equ    $ - TASK_A_DATA32_SEGMENT

[section .task-a-gs]
[bits 32]
TASK_A_STACK32_SEGMENT:
	times 1024 db 0
TaskAStack32SegLen		equ		$ - TASK_A_STACK32_SEGMENT
TaskATopOfStack32       equ     TaskAStack32SegLen - 1

[section .task-a-s32]
[bits 32]
TASK_A_CODE32_SEGMENT:
	mov ax, VideoSelector
	mov gs, ax

	mov ax, TaskAData32Selector
	mov ds, ax

	mov ax, TaskAStack32Selector
	mov ss, ax

	mov esp, TaskATopOfStack32	

	mov ebp, TaskAStringOffset
	mov bx, 0x0c
	mov dh, 15
	mov dl, 30
	call FunctionSelector : PrintStringOffset

	jmp Code16Selector : 0


TaskACode32SegLen		equ		$ - TASK_A_CODE32_SEGMENT


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值