Orange'S:一个操作系统的实现

保护模式-调用门特权级转移实践

Posted by BINBIN Blog on October 30, 2019

调用门特权级转移实践

前一章节增加了ring3的代码,这个是低特权级。该章实验调用门的使用,将调用门的描述符和选择子以及[SECTION .ring3]的代码修改一下!

代码对比如下:

可以看出,将调用门的描述符和段选择子都设置权限3!

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

SelectorCallGateTest equ LABEL_CALL_GATE_TEST - LABEL_GDT + SA_RPL3


379	; CodeRing3
380	[SECTION .ring3]
381	ALIGN	32
382	[BITS	32]
383	LABEL_CODE_RING3:
384		mov	ax, SelectorVideo
385		mov	gs, ax
386		mov	edi, (80 * 14 + 0) * 2
387		mov	ah, 0Ch
388		mov	al, '3'
389		mov	[gs:edi], ax
390
391		call	SelectorCallGateTest:0
392
393		jmp	$
394	SegCodeRing3Len	equ	$ - LABEL_CODE_RING3

在第393行 死循环之前,增加了使用调用门的指令!修改描述符和选择子是为了满足CPL 和 RPL 都小于等于调用门DPL 的条件!

低特权级到高特权级转移的时候,需要用到TSS!

增加TSS


LABEL_DESC_TSS:        Descriptor 0,          TSSLen-1, DA_386TSS
....
SelectorTSS		equ	LABEL_DESC_TSS		- LABEL_GDT
....

; 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 描述符
	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

.....


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


.....

    retf ;Ring0 -> Ring3,历史性转移!将打印数字 '3'。
	ud2	; should never arrive here

.....



目前我们只初始化了0级堆栈,其他的没有初始化,因为没有用到!

需要在特权级变化之前加载它!


	mov	ax, SelectorTSS
	ltr	ax

	push	SelectorStack3
	push	TopOfStack3
	push	SelectorCodeRing3
	push	0
	retf

	mov	ax, SelectorTSS
	ltr	ax

这两条指令装载任务状态段寄存器TR,使其指向已预置好的任务的TSS!

LTR指令是专门用于装载任务状态段寄存器TR的指令。该指令的操作数是对应TSS段描述符的选择子。LTR指令从GDT中取出相应的TSS段描述符,把TSS段描述符的基地址和界限等信息装入TR的高速缓冲寄存器中。

编译运行后,运行成功!不但看到数字3 , 也看到字母C! 这表明我们在ring3下对调用门的使用也是成功的!

所以我们目前实现了两次从高特权级到低特权级别以及依次从低特权级到高特权级的转移,最终在低特权级的代码中让程序停住!

为了顺利返回到实模式,将调用局部任务的代码加入到调用门的目标代码([SECTION .sdest]),程序进入局部任务,然后由原路返回实模式!

新增代码如下:


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

	; Load LDT
	mov	ax, SelectorLDT
	lldt	ax

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


...

	; Load LDT
	mov	ax, SelectorLDT
	lldt	ax

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

	;retf

...



编译运行,屏幕同时出现C、L、3。这个是各个程序段的输出!