《自己动手写操作系统》第三章 pmtest5源码解析——特权级变换与堆栈切换实例详解

摘要:在pmtest4中,我们已经看到,对于非一致性代码,如何从低特权级转移向高特权级。但是我们该怎样从高特权级别,转移到低特权级别呢?本文,主要是为你解答这些疑问,展现实现这种特权级别转移的方法。另外,我们将在实践中探讨,为什么要设定RPL,是不是相对与DPL多此一举呢?在调试环节,我们将通过改动代码,排查相关错误来深入理解特权级检测机制。

一、预备理论

1.关于堆栈 我们都知道,可以通过calljmp实现长跳转或者短跳转(段内转移或者段间转移)。但是jmpcall毕竟是有分别的,其中call指令是会影响到堆栈的:对于短调用,call指令执行时,下一条指令的eip入栈,ret指令执行时候,这个eip就会从堆栈中弹出。 短调用call指令执行前后堆栈的变化如下图所示(注意带有参数的ret指令): 长调用的情况与此相似,不同的是call指令执行时,被压栈的还有cs


2.特权级变换
注意到:如果我们采用call指令进行长跳转,而且特权级别发生了变换,这个时候就发生了堆栈切换,比如从A切换到了B。但是我们的数据保存在A上面,如何从B中返回呢?原来,intel提供了一种机制,能够将A上的一些内容copyB堆栈中。同时,A堆栈要切换到B堆栈,但是它怎么知道B堆栈在哪里呢?答案:原来这个内容保存在任务的TSS结构中,TSS是任务状态段。每个任务都最多有四个堆栈段,但是只有一个espssTSS就解决了堆栈切换方面的数据保存问题。TSS的相关介绍,可以参考:保护模式及其编程——任务切换http://blog.csdn.net/trochiluses/article/details/19768579。

比如,我们目前是ring 3,需要转移到ring 1,那时,堆栈将自动切换到ss1esp1。(由于只是从外层到内层切换,才从TSS中取得堆栈信息,所以,TSS中没有ring 3相关的堆栈信息。那么从内层向外层切换,如何获得目的地的信息呢)。

切换过程中有关堆栈的处理,可以参考:保护模式及其编程——保护的详尽意义:通过调用门转移特权级http://blog.csdn.net/trochiluses/article/details/19573651

好了,我们已经知道call------ret分别实现特权级高低转化,可能我们平时一直先使用call,然后再使用ret,以至于你对它的印象好像ifelse,实际上不是这样的,我们通过retf指令,单独使用就可以实现特权级别从03,从高到低。

二、代码分析

pmtest5.a:我们需要增加一个ring 3的代码段,堆栈段,另外为了在rign 3下打印字符,需要将video段的DPL改成3.我们先来写一个测试版本a:程序执行流程和前面没有太大区别,在保护模式重打印字符串之后,我们让程序进入ring 3代码段,并且死循环,需要添加如下代码。
push     SelectorStack3
push     TopOfStack3
push     SelecorCodeRing3
push    0
        但是,如果我们去改动SelectorStack3和SelecorCodeRing3的描述符中RPL的字段,变成0,发现这个程序pmtest5.asm并没有像我们想象的那样,进入ring3.显然,retf的指令没有被正确执行。想一想,为什么?
      pmtest5.b:在a的基础上,我们让程序在ring3代码段,进入死循环之前,调用调用门。在pmtest4的和a的基础上,我们需要做如下改动:
改动调用门的描述符和选择符到ring3;添加TSS以便能够从低特权级别转移到高特权级,并进行相关初始化。
      最后,我们让程序在调用门的目标段,也就是ring 0中,跳入局部任务。
      总结一下,pmtest5的执行流程:实模式---保护模式32b代码段ring 0---通过retf进入ring 3的代码段——通过调用门,进入ring 0的目标代码段——通过jmp,跳转到LDT局部任务。

三、调试过程中发现的错误与分析

1)为什么要有RPL,我们都知道有“别名段”,那么别名段意味着什么呢?同一个段,可以对应着不同的selector,也就能够对应不同的RPL,这样就有利于实现自由的权限管理。 我们回忆,最开始的retf并没有生效,猜想到可能是权限的问题。我们从ring0 经过retf ring 3call,从ring 3--->ring 0,要求:CPL<=DPL_G,RPL<=RPL_G,CPL>=DPL_B;变化以后CPL=DPL_B=0 retf是相反的过程,ring 0--->ring 3,要求:CPL<DPL_BRPL<DPL_B!!!注意,retf只能用于不同特权级之间,所以没有等于,这也就是为什么开始转移失败了。
2)堆栈切换的特权级检查
    更改TSS段中,将ss对应部分改成selectorstack3,然后就能在调试bochs的时候收到如下信息:
00082857633e[CPU0  ] call_protected: SS selector.rpl != CS descr.dpl
    注明:开启出错信息的方法,在.bochsrc中添加对于log信息的注释#log
程序代码如下:
%include	"head.inc"
org 0100h
	jmp LABEL_BEGIN

[SECTION .gdt]
;GDT							base,	length,	attr

LABEL_GDT:      Descriptor         0,                0, 0 
LABEL_DESC_NORMAL:	Descriptor		0,	0ffffh,		DA_DRW
LABEL_DESC_CODE16:	Descriptor		0,	0FFFFH,		DA_C
LABEL_DESC_DATA:	Descriptor		0,	SegDataLen-1,	DA_DRW
LABEL_DESC_STACK:	Descriptor		0,	TopOfStack,		DA_DRWA+DA_32
LABEL_DESC_CODE32:	Descriptor		0,	SegCode32Len-1,	DA_C+DA_32
LABEL_DESC_CODE_DEST: Descriptor	0,	SegCodeDestLen-1,DA_C+DA_32
LABEL_DESC_LDT:		Descriptor		0,	LDTLen-1,	DA_LDT
LABEL_DESC_VIDEO:	Descriptor	0B8000h,	0ffffh,	DA_DRW + DA_DPL3
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
LABEL_DESC_TSS:			Descriptor	0,	TSSLen-1,	DA_386TSS

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

GdtLen	equ	$-LABEL_GDT
GdtPtr	dw	GdtLen-1;注意,长度都是实际长度减1
		dd	0	;段基地址,注意,这里之所以没有直接制定,是因为还没有确定保护模式下gdt的基地址

;选择子
SelectorData		equ		LABEL_DESC_DATA		-	LABEL_GDT	
SelectorCode16		equ		LABEL_DESC_CODE16	-	LABEL_GDT
SelectorCode32		equ		LABEL_DESC_CODE32	-	LABEL_GDT
SelectorStack		equ		LABEL_DESC_STACK	-	LABEL_GDT 
SelectorNormal		equ		LABEL_DESC_NORMAL	-	LABEL_GDT 
SelectorLDT			equ		LABEL_DESC_LDT		-	LABEL_GDT 
SelectorCodeDest 	equ		LABEL_DESC_CODE_DEST -	LABEL_GDT 
SelectorCallGateTest equ	LABEL_CALL_GATE_TEST -	LABEL_GDT +SA_RPL3
SelectorVideo		equ		LABEL_DESC_VIDEO	-	LABEL_GDT
SelectorCodeRing3	equ		LABEL_DESC_CODE_RING3 -	LABEL_GDT	+SA_RPL3 
SelectorStack3		equ		LABEL_DESC_STACK3	-	LABEL_GDT	+SA_RPL3 
SelectorTSS			equ		LABEL_DESC_TSS		-	LABEL_GDT 
;end of section gdt
;--------------------------------------------------------------
[SECTION .data]
[BITS 32]
ALIGN 32
LABEL_SEG_DATA:
	SPValueInRealModel	dw	0
PMMessage:		db		"Coming into protect mode now !",0
OffsetPMMessage	equ	PMMessage - $$
StrTest:		db	"ABCDEFGHIJKLMNOPQRSTUVWXYZ",	0
OffsetStrTest	equ	StrTest - $$
SegDataLen			equ		$ - LABEL_SEG_DATA 
;end of section data
;-----------------------------section:global stack------------------
[SECTION .gs]
ALIGN	32
[BITS 32]
LABEL_SEG_STACK:
	times	512	db	0
TopOfStack	equ	$-LABEL_SEG_STACK-1
;----------------------section:LDT----------------------------------
[SECTION .ldt]
align	32
[bits	32]
LABEL_SEG_LDT:

LABEL_DESC_CODEA:	Descriptor	0,CodeALen -1,DA_C+DA_32


LDTLen		equ		$- LABEL_SEG_LDT

SelectorCodeA 	equ		LABEL_DESC_CODEA - LABEL_SEG_LDT + SA_TIL

;end of ldt segment
;-------------------------------section:codeA------------------------
[section	.codeA]
align	32
[bits	32]	
LABEL_SEG_CODEA:
	mov		ax,SelectorVideo
	mov		gs,ax

	mov		ah,0ch
	mov		al,'L'
	mov		edi,(2*80+0)*2
	mov		[gs:edi],ax
	jmp		SelectorCode16:0
CodeALen equ	$-LABEL_SEG_CODEA 
;end of secion codeA
;------------------------------section:s16 begin---------------------
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
	xchg 	bx,bx
	mov		ax,cs
	mov		ds,ax
	mov		es,ax
	mov		ss,ax
	mov		sp,0100h
	
	mov		[LABEL_GO_BACK_TO_REAL+3],ax
	mov		[SPValueInRealModel],sp

	;for segment code32
	xor		eax,eax
	mov		eax,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

	;for segment code16
	xor		eax,eax
	mov		ax,cs
	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

	;for segment data
	xor		eax,eax
	mov		eax,ds
	shl		eax,4
	add		eax,LABEL_SEG_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

	;for segment ldt
	xor		eax,eax
	mov		eax,ds
	shl		eax,4
	add		eax,LABEL_SEG_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
	
	;for segment codeA
	xor		eax,eax
	mov		eax,ds
	shl		eax,4
	add		eax,LABEL_SEG_CODEA;
	mov		word	[LABEL_DESC_CODEA  +2],ax
	shr		eax,16
	mov		byte	[LABEL_DESC_CODEA + 4],al
	mov		byte	[LABEL_DESC_CODEA + 7],ah
	
	;for segment stack
	xor		eax,eax
	mov		eax,ds
	shl		eax,4
	add		eax,LABEL_SEG_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

	;no need for video base
	;for segment dstcode
	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

	;for segment code ring 3 
	xor		eax,eax
	mov		ax,cs
	shl		eax,4
	add		eax,LABEL_SEG_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

	;for segment code stack 3 
	xor		eax,eax
	mov		ax,cs
	shl		eax,4
	add		eax,LABEL_SEG_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

	;for segment code stack 3 
	xor		eax,eax
	mov		ax,cs
	shl		eax,4
	add		eax,LABEL_SEG_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

	xor 	eax,eax
	mov		ax,ds
	shl		eax,4
	add		eax, LABEL_GDT
	mov		dword	[GdtPtr +2 ],eax

	lgdt	[GdtPtr]

	cli

	in 		al,92h
	or		al,02h
	out		92h,al

	mov		eax,cr0
	or		eax,1
	mov		cr0,eax

	jmp		dword SelectorCode32:0
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LABEL_REAL_ENTRY:	;come here from protect model
	mov		ax,cs
	mov		ds,ax
	mov		es,ax
	mov		ss,ax

	mov		sp,[SPValueInRealModel]

	in		al,92h
	and		al,11111101b
	out		92h,al

	sti

	mov		ax,4c00h
	int		21h
;end of section .s16
;------------------------section:code32,start of protect model--------
[SECTION .s32]
[BITS	32]
LABEL_SEG_CODE32:
		mov		ax,SelectorVideo
		mov		gs,ax
		
		mov		ax,SelectorData
		mov		ds,ax


		mov		ax,SelectorStack
		mov		ss,ax
		
		mov		esp,TopOfStack

		mov		ah,0ch
		xor		esi,esi
		xor		edi,edi
		mov		esi,OffsetPMMessage
		mov		edi,(80*11+0)*2
		cld
.loopPmMessage:
		lodsb
		test	al,al
		jz	.end
		mov		[gs:edi],ax
		add		edi,2
		jmp	.loopPmMessage

.end:	;

		call	DispReturn

		mov		ax,SelectorTSS
		ltr		ax

		push	SelectorStack3
		push	TopOfStack3
		push	SelectorCodeRing3
		push	0
		
		xchg	bx,bx
		retf
		xchg	bx,bx
		call	SelectorCallGateTest:0

		;Load LDT
		mov		ax,SelectorLDT
		lldt	ax
			
		jmp		SelectorCodeA:0	


;function: read and print 8 byte from es:0
TestRead:
		xor		esi,esi
		mov		ecx,8
.loopForEightBype:
		mov		al,[es:esi]
		call	DispAL
		inc		esi
		loop	.loopForEightBype
		
		call	DispReturn
		
		ret; be sure of this

;funtion:
	;write 8byte to es:OffsetStrTest
	;input:es
TestWrite:
		push	esi
		push	edi
	
		xor		esi,esi
		xor		edi,edi
		mov		esi,OffsetStrTest
		cld

	.loopForEightBype:
		lodsb	;ds:si->al
		test	al,al
		jz	.end
		mov		[es:edi],al
		inc		edi
		jmp		.loopForEightBype
	.end:
		pop		edi
		pop		esi

		ret


;funtion DispAL
;	display the number in AL
;input: AL-the number
;		edi-the position to display
;modified:ax,edi
DispAL:
	push	ecx
	push	edx
	mov		ah,0ch
	mov		dl,al
	shr		al,4
	mov		ecx,2
.begin:
	and		al,01111b
	cmp		al,9
	ja		.moreThanNine
	add		al,'0'
	jmp		.end
.moreThanNine:
	sub		al,0ah
	add		al,'A'
.end:
	mov		[gs:edi],ax
	add		edi,2

	mov		al,dl
	loop	.begin
	add		edi,2

	pop		edx
	pop		ecx
		
	ret
;DispAL

;function DispReturn
;if edi=(a*80 + b)*2
;then edi=(a*80 + 80)*2
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
;end for function DispReturn
	SegCode32Len	equ		$-LABEL_SEG_CODE32
;end of section .s32

;---------------------section s16code,before return to real-----------

[SECTION .s16code]
ALIGN	32
[BITS 16]
LABEL_SEG_CODE16:
	;return to real model
	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
;---------------------section:sdest---------------------------------
[section	.sdest]
[bits	32]

LABEL_SEG_CODE_DEST:
	mov		ax,SelectorVideo
	mov		gs,ax

	mov		edi,(80*12+0)*2
	mov		ah,0ch
	mov		al,'C'
	mov		[gs:edi],ax
	
	;load LDT
	mov		ax,SelectorLDT
	lldt	ax

	jmp		SelectorCodeA:0

SegCodeDestLen	equ		$-LABEL_SEG_CODE_DEST
;-------------------section stack ring3-----------------------------
[section .s3]
align 32
[bits 32]
LABEL_SEG_STACK3:
	times	512	db	0
	TopOfStack3		equ		$-LABEL_SEG_STACK3		-	1


;----------------------section CodeRing3-----------------------------
[section .CodeRing3]
[bits	32]
LABEL_SEG_CODE_RING3:
		mov	ax,SelectorVideo
		mov	gs,ax

		mov		edi,(80*14+0)*2
		mov		ah,0ch
		mov		al,'3'
		mov		[gs:edi],ax
		
		call	SelectorCallGateTest:0
		jmp		$

SegCodeRing3Len		equ		$	-	LABEL_SEG_CODE_RING3

;------------------------section TSS-------------------------------
[section .tss]
[bits 32]
LABEL_SEG_TSS:
		dd		0
		dd		TopOfStack
		dd		SelectorStack
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dd		0
		dw		0
		dw		$	-	LABEL_SEG_TSS +2
		db		0ffh
TSSLen	equ		$ - LABEL_SEG_TSS 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值