忆杰的博客

忆杰的博客

保护模式10:输入输出保护敏感指令

为了支持多任务, X86体系不但需要实现任务的隔离与共享, 前面几篇我们已经有了一些体验, 但是仅仅任务隔离与保护还是不够的, 还需要对输入/输出进行保护, 这样一个没有权限的程序就不能随便的去访问一些端口,也不能够随便的执行一些指令. 本篇我们就来具体看看实现细节!

X86除了上一篇介绍的特权指令外, 还有这个敏感指令, 特权指令的话, 只能在特权模式下执行, 也就是0环, 其他环执行就异常. 敏感指令的话, 需要查看IOPL.

X86将一系列的指令归类为IO敏感指令, 这些指令想要得到执行, 必须在CPL <= IOPL的情况下. 在其他情况下, 则产生通用保护异常(#GP), 敏感指令有这么几条, CLI, STI, IN, INS, OUT, OUTS…除了CLI, STI以外这几条指令, 除了CPL <= IOPL不满足产生异常, IN, INS, OUT, OUTS, 在CPL > IOPL的情况下, 如果IO许可位图是允许的也是可以访问的.

这里又搞出一个 I/O许可位图, I/O许可位图处于当前TSS中. 所以可以每个任务有自己特别的I/O许可位图.这样可以有效的区分不同的任务实现高精度的控制, 不过好像Windows也没有利用这个特性, 据说是太慢了??

I/O许可位图由二进制串组成, 每一位对应一个IO地址, 从0开始计数. 和中断号一样. 如果位串的该位为0, 那么在CPL <= IOPL的情况下也允许执行, 否则#(GP). I/O许可位图有很多, 因为X86支持的I/O地址空间为64K,  最长可以到64K,所以I/O许可位图最长为8K(64K/8),但是一般任务根本用不了这么多的位图. 所以一般这样分配, 从TSS的104字节开始, 列出自己想填写的I/O许可位图, 对于不想列出的后面部分以0FF结束就可以了.

还有有可能在读取I/O许可位图的时候, 读取的位图位4位, 比如INSD, 一下操作4个地址空间, 需要4个位图, 那么很有可能在读取低位的在一个字节, 读取高位的时候又在一个字节. 所以X86为了避免这种情况, 每次读取I/O许可位图的时候是2个字节2个字节读取的, 这样. 不管多坏的情况也不可能超过2个字节. 这样在结尾加个0ffh也就比较好理解了, 如果不加那就越界了. 加其他的值也不好区分, 只有每个位加1才不会曲解原来的I/O许可位图.

除了上面说的, 还有就是对IOPL, Eflags中的标记位进行保护了, 如果这些标记位允许随意更改, 那么上面谈的就形同虚设了,所以只有特权级为0的程序才能够修改IOPL和VM位, 相对于IOPL更内层的特权级才能够修改IF位. 如果特权级不符合要求, 修改这些位的话, 也不会异常, CPU会忽略. OK差不多就是这么多东西了!

老套路,说完这个基本的概念, 就该说说这个演示代码的逻辑了. 这个演示代码的话,和以前都是一样首先初始化进入保护模式应该初始化的东西, 然后进入保护模式过渡段, 然后在保护模式的过渡段中跳到Demo段中,在这里装载了自己的TR和初始化了TSS中的LDTR, 初始化了演示代码段的 SS:ESP, CS:EIP. LDTR. DS, 等等.然后通过任务门切换到_TestCodeBegin中, 第一次切换的代码, 很显然在CPL == IOPL的情况下, 写一系列端口是没有问题的,所以不会异常, 第二次则因为违反了I/O许可位图规定而产生异常. (#GP), 第3次CPL >= IOPL, 可是使用了CLI, 还是要异常的, 第4次, CPL == IOPL 但是执行0环才可以执行的指令还是要产生异常!

试验完了. 就跳回过渡段, 然后回到实模式下. 整个世界清静了.. 还是和以前一样, 有图有真相!

http://www.joenchen.com/JoenTools/IOPL.rar

;============================================================================
	;演示保护模式下的IO保护
	;编译选项请参见 makefile  TAB = 8
;============================================================================
	.686p
	Include pm.inc
	option casemap:none
Stack_Len	equ	1024		;堆栈大小
;============================================================================
GdtSeg		Segment use16		;全局描述符表
;				;段基址	;段界限			;属性
Dummy:			Descriptor	0, 	0, 			0			;空的描述符
Normal:			Descriptor	0, 	0ffffh,			DA_DRW 			;规范段描述符
g_DataDesc:		Descriptor	0,     0fffffh, 		DA_DRWG	or DA_DPL3	;全局4G数据段
g_IdtCode32Desc:        Descriptor      0,      IdtCodeSegLen-1,	DA_CR or DA_32 or DA_DPL1		;中断处理函数描述符
g_CodeTempDesc:		Descriptor 	0,	0ffffh, 		DA_C			;非一致代码段16位
g_CodeSwitchDesc:	Descriptor	0,	DemoCodeSegLen-1,	DA_C or DA_32		;数据段
g_VideoDesc:		Descriptor	0b8000h,0ffffh, 		DA_DRW or DA_DPL3	;显存段(可读写)
g_DemoTssDesc:		Descriptor	0,	DemoTssSegLen-1, 	DA_386TSS  		;演示任务TSS段描述符
g_TestTssDesc:		Descriptor	0,	TestTssSegLen-1,	DA_386TSS 		;测试任务TSS段描述符
g_DemoLdtDesc:		Descriptor	0,	LdtDemoSegLen-1,	DA_LDT			;演示任务的LDT描述符
g_TestLdtDesc:		Descriptor	0,	LdtTestSegLen -1, 	DA_LDT			;测试代码段的LDT描述符
;----------------------------------------------------------------------------
;任务门			段选择子		入口	参数个数	属性
g_TestTask: 	Gate 	g_TestTssSelector,	0,	0, 	DA_TaskGate			;386任务门指向任务描述符
g_DemoTask: 	Gate 	g_DemoTssSelector,	0,	0, 	DA_TaskGate or DA_DPL2		;386任务门指向任务描述符
GDTLen			equ	$ - GdtSeg							;GDT长度
;----------------------------------------------------------------------------
GDT_Ptr		word	GDTLen-1				;VGDT
		dword	0
_IDT_Ptr        fword   0                       		;VIDT
_RegSp		word	?					;用于保存SS:SP
_RegSs		word	?
;----------------------------------------------------------------------------
NormalSelector		equ	Normal 			- GdtSeg			;规范段选择子
g_DataSelector		equ	g_DataDesc		- GdtSeg or SA_RPL3		;全局数据段
g_CodeTempSelector	equ	g_CodeTempDesc 		- GdtSeg			;临时代码段选择子
g_DemoCodeSelector	equ	g_CodeSwitchDesc	- GdtSeg			;任务切换代码段
g_VideoSelector		equ	g_VideoDesc  		- GdtSeg 			;LDT视频段选择子
g_DemoTssSelector	equ	g_DemoTssDesc		- GdtSeg			;TSS演示段描述符选择子
g_DemoLdtSelector	equ	g_DemoLdtDesc		- GdtSeg			;演示代码段LDT描述符
g_TestTssSelector	equ	g_TestTssDesc		- GdtSeg			;TSS测试段描述符选择子
g_TestLdtSelector	equ	g_TestLdtDesc		- GdtSeg			;测试代码段的LDT
g_TestTaskSelector	equ	g_TestTask		- GdtSeg			;测试代码任务门
g_DemoTaskSelector	equ	g_DemoTask		- GdtSeg or SA_RPL2		;演示任务任务门
g_IdtCodeSelector	equ	g_IdtCode32Desc		- GdtSeg or SA_RPL1		;中断处理代码
GdtSeg		Ends
;============================================================================
LdtTestSeg		Segment use32			;测试任务段的LDT
;				  ;段基址	   ;段界限	;属性
L_TestStack1Desc:	Descriptor	0,     Stack_Len-1, DA_DRW or DA_DPL1 or DA_32	;1环堆栈段
L_TestStack2Desc:	Descriptor	0,     Stack_Len-1, DA_DRW or DA_DPL2 or DA_32	;1环堆栈段
L_TestStack3Desc:	Descriptor	0,     Stack_Len-1, DA_DRW or DA_DPL3 or DA_32	;1环堆栈段
L_TestCode1Desc:	Descriptor	0,TestCodeSegLen-1, DA_C or DA_32 or DA_DPL1	;32位测试代码段
L_TestCode2Desc:	Descriptor	0,TestCodeSegLen-1, DA_C or DA_32 or DA_DPL2	;32位测试代码段
L_TestCode3Desc:	Descriptor	0,TestCodeSegLen-1, DA_C or DA_32 or DA_DPL3	;32位测试代码段
;----------------------------------------------------------------------------
L_TestCode1Selector	equ	L_TestCode1Desc		- LdtTestSeg + SA_RPL1 + SA_TIL	;32位测试代码段选择子
L_TestCode2Selector	equ	L_TestCode2Desc		- LdtTestSeg + SA_RPL2 + SA_TIL	;32位测试代码段选择子
L_TestCode3Selector	equ	L_TestCode3Desc		- LdtTestSeg + SA_RPL3 + SA_TIL	;32位测试代码段选择子
L_TestStack1Selector	equ	L_TestStack1Desc	- LdtTestSeg + SA_RPL1 + SA_TIL	;测试任务段的1环选择子
L_TestStack2Selector	equ	L_TestStack2Desc	- LdtTestSeg + SA_RPL2 + SA_TIL	;测试任务段的1环选择子
L_TestStack3Selector	equ	L_TestStack3Desc	- LdtTestSeg + SA_RPL3 + SA_TIL	;测试任务段的1环选择子
LdtTestSegLen		equ	$ - LdtTestSeg
LdtTestSeg		Ends
;============================================================================
LdtDemoSeg		Segment use32			;演示代码段LDT
;				  ;段基址	   ;段界限	;属性
L_DemoCodeDesc:		Descriptor	0,DemoCodeSegLen-1, DA_C or DA_32		;32位代码段
L_DemoStackDesc:	Descriptor	0,     Stack_Len-1, DA_DRW or DA_32		;演示代码段选择子

L_DemoStackSelector	equ	L_DemoStackDesc - LdtDemoSeg + SA_TIL			;演示代码堆栈选择子
L_DemoCodeSelector	equ	L_DemoCodeDesc	- LdtDemoSeg + SA_TIL			;演示代码代码段选择子
LdtDemoSegLen		equ	$ - LdtDemoSeg
LdtDemoSeg		Ends
;============================================================================
IdtSeg		Segment use32				;中断描述符表

repeat          13
        Gate    g_IdtCodeSelector,  _IdtOther,0,  DA_386TGate		;0-Ch陷阱门处理地址
endm
	Gate    g_IdtCodeSelector,  _IdtGp,   0,  DA_386TGate		;d通用故障处理
repeat          242
        Gate    g_IdtCodeSelector,  _IdtOther,0,  DA_386TGate		;e-256h陷阱门处理地址
endm
IdtSegLen		equ	$ - IdtSeg
IdtSeg		Ends
;============================================================================
IdtCodeSeg	Segment use32				;中断处理代码

_IdtOther		equ	$ - IdtCodeSeg
IdtOther	Proc					;其他类型的错误处理

;----------------------------------------------------------------------------
	;edi, 已经被初始化了
	lea	esi, SzOther
	mov	ecx, sizeof SzOther			;显示其他错误字符串
	cld

@@:	lodsb
	mov	ah, 0ch
	stosw
	loop	@b	

	iretd
IdtOther 	Endp

;----------------------------------------------------------------------------
_IdtGp			equ	$ - IdtCodeSeg		;通用故障处理
IdtGp		Proc

;----------------------------------------------------------------------------
	;显示错误字符串
	mov	esi, TestDataSeg
	shl	esi, 4
	lea	ecx, SzEroor
	add	esi, ecx				;esi-->字符串
	mov	ecx, sizeof SzEroor
	cld
@@:	lodsb
	mov	ah, 0ah
	stosw
	loop	@b
;----------------------------------------------------------------------------
	;将任务从忙置为闲
	mov	esi, GdtSeg
	shl	esi, 4
	lea	ecx, g_DemoTssDesc
	add	esi, ecx

	mov	al, byte ptr ds:[esi+5]
	and	al, 0f0h
	or	al, 9h					;将任务置为可用
	mov	byte ptr ds:[esi+5], al
;----------------------------------------------------------------------------
	mov	esp, Stack_Len				;跳过堆栈
	;因为iretd中不能够作为任务返回, 所以这里使用JMP
	Jmp32	g_DemoTaskSelector, 0

IdtGp 		Endp

IdtCodeSegLen		equ	$ - IdtCodeSeg
IdtCodeSeg	Ends
;============================================================================
DemoTssSeg	Segment use32				;演示任务TSS段
	DemoTss	TSS <0>
	byte	0ffh
DemoTssSegLen		equ	$ - DemoTssSeg
DemoTssSeg	Ends
;============================================================================
DemoStackSeg	Segment use32				;演示代码段堆栈
	byte	Stack_Len dup (0)
DemoStackSeg	Ends
;============================================================================
DemoCodeSeg	Segment use32				;演示代码段

_DemoBegin		equ	$ - DemoCodeSeg
DemoBegin	Proc					;32位代码段入口

;----------------------------------------------------------------------------
	;初始化自己的TSS, 用于在任务门中返回
	mov	ax, g_DataSelector			;全局数据段
	mov	ds, ax

	mov	ax, g_DemoLdtSelector
	lldt	ax					;装载LDTR

	mov	ax, L_DemoStackSelector
	mov	ss, ax
	mov	esp, Stack_Len				;置SS:ESP

	xor	edi, edi
	mov	edi, DemoTssSeg
	shl	edi, 4
	lea	ecx, DemoTss
	add	edi, ecx
	mov	word ptr ds:[edi+TSS.regLdtr], g_DemoLdtSelector;写入自己的LDTR

	mov	ax, g_DemoTssSelector
	ltr	ax					;装载演示段TR
;----------------------------------------------------------------------------
	;初始化演示代码的TSS, 用于进行任务门转移
	mov	edi, TestTssSeg
	shl	edi, 4
	lea	ecx, StTestTss
	add	edi, ecx

	;初始化TSS的SS:ESP
	mov	word ptr ds:[edi+TSS.regSs], L_TestStack1Selector
	mov	dword ptr ds:[edi+TSS.regEsp], Stack_Len
	mov	word ptr ds:[edi+TSS.regDs], g_DataSelector

	;ds:esi-->>数据段--指向ds:SzCpl1, 5行5列
	mov	esi, TestDataSeg
	shl	esi, 4
	lea	eax, SzCpl1
	add	esi, eax
	mov	dword ptr ds:[edi+TSS.regEsi], esi
	mov	dword ptr ds:[edi+TSS.regEbx], 5 * 80 * 2 + 5 * 2
	mov	dword ptr ds:[edi+TSS.regEdi], 5 * 80 * 2 + (5 + sizeof SzCpl1 ) * 2

	mov	word ptr ds:[edi+TSS.regCs], L_TestCode1Selector
	mov	dword ptr ds:[edi+TSS.regEip], _TestCodeBegin
	mov	dword ptr ds:[edi+TSS.regEflags], IOPL1
	mov	word ptr ds:[edi+TSS.regLdtr], g_TestLdtSelector

	;通过任务门转移到 测试任务TestCodeSeg-->_TestCodeBegin
	CALL32	g_TestTaskSelector, 0
;----------------------------------------------------------------------------
	;中断处理是1环的, 这里会异常, 所以填写1环的堆栈
	mov	word ptr ds:[edi+TSS.regSs], L_TestStack2Selector
	mov	dword ptr ds:[edi+TSS.regEsp], Stack_Len;初始化TSS的SS:ESP

	mov	word ptr ds:[edi+TSS.regSs1], L_TestStack1Selector
	mov	dword ptr ds:[edi+TSS.regEsp1], Stack_Len

	;ds:esi-->>数据段--指向ds:SzCpl1, 5行5列
	mov	esi, TestDataSeg
	shl	esi, 4
	lea	eax, SzCpl2
	add	esi, eax
	mov	dword ptr ds:[edi+TSS.regEsi], esi
	mov	dword ptr ds:[edi+TSS.regEbx], 6 * 80 * 2 + 5 * 2
	mov	dword ptr ds:[edi+TSS.regEdi], 6 * 80 * 2 + ( 5 + sizeof SzCpl2 ) * 2
	mov	word ptr ds:[edi+TSS.regDs], g_DataSelector

	mov	word ptr ds:[edi+TSS.regCs], L_TestCode2Selector
	mov	dword ptr ds:[edi+TSS.regEip], _TestCodeBegin
	mov	dword ptr ds:[edi+TSS.regEflags], IOPL1	;TSS的IOPL值
	mov	word ptr ds:[edi+TSS.regLdtr], g_TestLdtSelector

	;通过任务门转移到 测试任务TestCodeSeg-->_TestCodeBegin
	CALL32	g_TestTaskSelector, 0
;----------------------------------------------------------------------------
	;这里再次测试敏感指令, 特权级位2 IOPL=1 会异常
	mov	word ptr ds:[edi+TSS.regSs], L_TestStack2Selector
	mov	dword ptr ds:[edi+TSS.regEsp], Stack_Len;初始化TSS的SS:ESP

	mov	word ptr ds:[edi+TSS.regSs1], L_TestStack1Selector
	mov	dword ptr ds:[edi+TSS.regEsp1], Stack_Len

	;ds:esi-->>数据段--指向ds:SzCpl1, 5行5列
	mov	esi, TestDataSeg
	shl	esi, 4
	lea	eax, SzCpl3
	add	esi, eax
	mov	dword ptr ds:[edi+TSS.regEsi], esi
	mov	dword ptr ds:[edi+TSS.regEbx], 7 * 80 * 2 + 5 * 2
	mov	dword ptr ds:[edi+TSS.regEdi], 7 * 80 * 2 + ( 5 + sizeof SzCpl3 ) * 2
	mov	word ptr ds:[edi+TSS.regDs], g_DataSelector

	mov	word ptr ds:[edi+TSS.regCs], L_TestCode2Selector
	mov	dword ptr ds:[edi+TSS.regEip], _TestCode2Begin
	mov	dword ptr ds:[edi+TSS.regEflags], IOPL1	;TSS的IOPL值
	mov	word ptr ds:[edi+TSS.regLdtr], g_TestLdtSelector

	;通过任务门转移到 测试任务TestCodeSeg-->_TestCodeBegin
	CALL32	g_TestTaskSelector, 0
;----------------------------------------------------------------------------
;这里再次测试敏感指令, 特权级位2 IOPL=2 但是执行了只有在0环才能够执行的特权指令
	mov	word ptr ds:[edi+TSS.regSs], L_TestStack2Selector
	mov	dword ptr ds:[edi+TSS.regEsp], Stack_Len;初始化TSS的SS:ESP

	mov	word ptr ds:[edi+TSS.regSs1], L_TestStack1Selector
	mov	dword ptr ds:[edi+TSS.regEsp1], Stack_Len

	;ds:esi-->>数据段--指向ds:SzCpl1, 5行5列
	mov	esi, TestDataSeg
	shl	esi, 4
	lea	eax, SzCpl4
	add	esi, eax
	mov	dword ptr ds:[edi+TSS.regEsi], esi
	mov	dword ptr ds:[edi+TSS.regEbx], 8 * 80 * 2 + 5 * 2
	mov	dword ptr ds:[edi+TSS.regEdi], 8 * 80 * 2 + ( 5 + sizeof SzCpl4 ) * 2
	mov	word ptr ds:[edi+TSS.regDs], g_DataSelector

	mov	word ptr ds:[edi+TSS.regCs], L_TestCode2Selector
	mov	dword ptr ds:[edi+TSS.regEip], _TestCode2Begin
	mov	dword ptr ds:[edi+TSS.regEflags], IOPL2	;TSS的IOPL值
	mov	word ptr ds:[edi+TSS.regLdtr], g_TestLdtSelector

	;通过任务门转移到 测试任务TestCodeSeg-->_TestCodeBegin
	CALL32	g_TestTaskSelector, 0
;----------------------------------------------------------------------------
	;返回实模式
	Jmp32	g_CodeTempSelector, _GoToProtect
DemoBegin 	Endp
;----------------------------------------------------------------------------
DemoCodeSegLen		equ	$ - DemoCodeSeg
DemoCodeSeg	Ends
;============================================================================
TestStackSeg	Segment use32
	byte	Stack_Len dup (0)
TestStackSeg	Ends
;============================================================================
TestDataSeg	Segment use32				;测试代码段的数据段
	SzCpl1		byte	"Test Task1, CPL = 1; IOPL = 1!", 0
	SzCpl2		byte	"Test Task2, CPL = 2; IOPL = 1!", 0
	SzCpl3		byte	"Test Task3, CPL = 2; IOPL = 1!", 0
	SzCpl4		byte	"Test Task4, CPL = 2; IOPL = 2!", 0
	SzEroor		byte	"#GP Error !", 0
	SzOther		byte	"Other Error!", 0
TestDataSegLen	equ	$ - TestDataSeg
TestDataSeg	Ends
;============================================================================
TestTssSeg	Segment use32				;测试任务TSS段
	StTestTss	TSS <0>
IoMap		label	byte				;IO许可位图
		byte	8 dup ( 0ffh )			;端口00h-3fh
		byte	11111011b			;端口40h-47h
		byte	3 dup ( 0ffh )			;端口48h-5fh
		byte	11111101b			;端口60h-67h
		byte	0				;端口68h-6fh
		byte	0ffh				;端口结束标记
TestTssSegLen		equ	$ - TestTssSeg
TestTssSeg	Ends
;============================================================================
TestCodeSeg	Segment use32				;测试代码段
;----------------------------------------------------------------------------
_MakeBeep 	Proc uses ebx esi edi ecx _dwPitch:dword;直接操作端口发出声音

	;初始化定时器, 是向端口43H输出数据0B6H即可
	mov 	al, 10110110y				;b6h
	out 	43h, al         			; Timer 8253-5 (AT: 8254.2).

	;向硬件定时器2(42h)端口写入1193167控制发声频率
	mov 	eax, _dwPitch
	out 	42h, al
	mov 	al, ah
	out 	42h, al

	;打开扬声器
	in 	al, 61h
	or  	al, 11y
	out 	61h, al
	DO_DELAY 	20000h				;Bochs上面这个延时就可用了
	;关闭扬声器
	in 	al, 61h
	and 	al, 11111100y
	out 	61h, al
	ret
_MakeBeep 	Endp
;----------------------------------------------------------------------------
        ;显示一条信息_lpStr:字符串首地址
        ;_dwXY开始显示地址
_PrintMessage   Proc    uses esi edi  _lpStr:dword, _dwXY:dword

        mov     esi, _lpStr
        xor     ecx, ecx
;----------------------------------------------------------------------------
@@:     mov     al, byte ptr ds:[esi]
        inc     esi
        inc     ecx
        or      al, al
        jnz     @b                                      ;ecx == 字符串长度
        dec     ecx
;----------------------------------------------------------------------------
        mov     esi, _lpStr
        mov     edi, _dwXY
@@:     lodsb
        mov     ah, 0ch                                 ;属性红色字
        stosw
        loop    @b
        ret
_PrintMessage   Endp
;----------------------------------------------------------------------------
	;测试代码段入口, 测试一系列敏感指令
_TestCodeBegin		equ	$ - TestCodeSeg
TestCodeBegin	Proc

	mov	ax, g_VideoSelector
	mov	es, ax

	Invoke	_PrintMessage, esi,ebx 

	Invoke	_MakeBeep, TONE_1			;发出个声音
;----------------------------------------------------------------------------
	;此处证明了通过任务门, 可以用iretd返回, 或使用jmp32跳回去
	iretd
	;通过任务门切换回演示任务
	;Jmp32	g_DemoTaskSelector, 0
;----------------------------------------------------------------------------
	jmp	TestCodeBegin
TestCodeBegin 	Endp
;----------------------------------------------------------------------------
	;测试代码2入口, 测试一系列敏感指令
_TestCode2Begin		equ	$ - TestCodeSeg
TestCode2Begin	Proc

	mov	ax, g_VideoSelector
	mov	es, ax

	Invoke	_PrintMessage, esi,ebx 			;打印当前CPL和IOPL

	cli
	clts
	iretd
	jmp	TestCode2Begin
TestCode2Begin Endp
TestCodeSegLen		equ	$ - TestCodeSeg
TestCodeSeg	Ends

;============================================================================
	;16位段, 由实模式跳入
;============================================================================
g_Code16Seg	Segment	use16
_GoToProtect	Proc					;返回实模式
	mov	ax, NormalSelector
	mov	fs, ax					;规范选择子
	mov	es, ax
	mov	ds, ax
	mov	ss, ax
	clts						;清除任务标记

	mov	eax, cr0				;关PE位, 进入实模式
	and	al, 0feh
	mov	cr0, eax

	;刷新段选择子缓冲区, 退回实模式
	Jmp16	<seg StartCodeSeg >, < offset _RealProtect >
_GoToProtect 	Endp
;----------------------------------------------------------------------------
_ProtectEntry	Proc					;实模式跳入入口

	;16位转32位代码段
	Jmp16	g_DemoCodeSelector, <_DemoBegin >
;----------------------------------------------------------------------------
_ProtectEntry 	Endp
g_Code16Seg	Ends
;============================================================================
	;起始代码段初始化保护模式的各个结构, 然后跳入保护模式
;============================================================================
StartCodeSeg	Segment use16
_InitGdt	Proc	uses es				;初始化全局描述符表

	xor	eax, eax
	mov	ax, GdtSeg
	mov	es, ax					;es-->全局描述符表
;----------------------------------------------------------------------------
	shl	eax, 4
	mov	dword ptr es:[GDT_Ptr+2], eax		;初始化VGDT描述符
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, g_Code16Seg				;初始化十六位的代码段
	shl	eax, 4
	mov	word ptr es:[g_CodeTempDesc+2], ax	;段基址低位
	shr	eax, 16
	mov	byte ptr es:[g_CodeTempDesc+4], al	;段基址高地址低位
	mov	byte ptr es:[g_CodeTempDesc+7], ah	;段基址高地址高位
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, DemoTssSeg				;初始化演示TSS段描述符
	shl	eax, 4
	mov	word ptr es:[g_DemoTssDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[g_DemoTssDesc+4], al
	mov	byte ptr es:[g_DemoTssDesc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, TestTssSeg				;初始化测试TSS段描述符
	shl	eax, 4
	mov	word ptr es:[g_TestTssDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[g_TestTssDesc+4], al
	mov	byte ptr es:[g_TestTssDesc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, DemoCodeSeg				;初始化任务切换段
	shl	eax, 4
	mov	word ptr es:[g_CodeSwitchDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[g_CodeSwitchDesc+4], al
	mov	byte ptr es:[g_CodeSwitchDesc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, LdtTestSeg				;初始化测试代码段的LDT
	shl	eax, 4
	mov	word ptr es:[g_TestLdtDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[g_TestLdtDesc+4], al
	mov	byte ptr es:[g_TestLdtDesc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, LdtDemoSeg				;初始化测试代码段的LDT
	shl	eax, 4
	mov	word ptr es:[g_DemoLdtDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[g_DemoLdtDesc+4], al
	mov	byte ptr es:[g_DemoLdtDesc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, IdtCodeSeg				;初始化测试代码段的LDT
	shl	eax, 4
	mov	word ptr es:[g_IdtCode32Desc+2], ax
	shr	eax, 16
	mov	byte ptr es:[g_IdtCode32Desc+4], al
	mov	byte ptr es:[g_IdtCode32Desc+7], ah
;----------------------------------------------------------------------------
	lgdt	fword ptr es:[GDT_Ptr]			;装载GDT
;----------------------------------------------------------------------------
	ret
_InitGdt 	Endp
;----------------------------------------------------------------------------
_InitDemoLdt	Proc	uses es				;初始化演示代码段LDT

	mov	ax, LdtDemoSeg
	mov	es, ax
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, DemoStackSeg			;初始化演示代码段堆栈
	shl	eax, 4
	mov	word ptr es:[L_DemoStackDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_DemoStackDesc+4], al
	mov	byte ptr es:[L_DemoStackDesc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, DemoCodeSeg				;初始化32位代码段
	shl	eax, 4
	mov	word ptr es:[L_DemoCodeDesc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_DemoCodeDesc+4], al
	mov	byte ptr es:[L_DemoCodeDesc+7], ah
;----------------------------------------------------------------------------
	ret
_InitDemoLdt 	Endp
;----------------------------------------------------------------------------
_InitTestLdt	Proc	 uses es			;初始化测试代码段LDT

	mov	ax, LdtTestSeg
	mov	es, ax
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, TestStackSeg			;初始化1环堆栈段
	shl	eax, 4
	mov	word ptr es:[L_TestStack1Desc+2], ax
	mov	word ptr es:[L_TestStack2Desc+2], ax
	mov	word ptr es:[L_TestStack3Desc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_TestStack1Desc+4], al
	mov	byte ptr es:[L_TestStack2Desc+4], al
	mov	byte ptr es:[L_TestStack3Desc+4], al

	mov	byte ptr es:[L_TestStack1Desc+7], ah
	mov	byte ptr es:[L_TestStack2Desc+7], ah
	mov	byte ptr es:[L_TestStack3Desc+7], ah
;----------------------------------------------------------------------------
	xor	eax, eax
	mov	ax, TestCodeSeg				;初始化测试代码段
	shl	eax, 4
	mov	word ptr es:[L_TestCode1Desc+2], ax
	mov	word ptr es:[L_TestCode2Desc+2], ax
	mov	word ptr es:[L_TestCode3Desc+2], ax
	shr	eax, 16
	mov	byte ptr es:[L_TestCode1Desc+4], al
	mov	byte ptr es:[L_TestCode2Desc+4], al
	mov	byte ptr es:[L_TestCode3Desc+4], al

	mov	byte ptr es:[L_TestCode1Desc+7], ah
	mov	byte ptr es:[L_TestCode2Desc+7], ah
	mov	byte ptr es:[L_TestCode3Desc+7], ah
;----------------------------------------------------------------------------

	ret
_InitTestLdt 	Endp
;----------------------------------------------------------------------------
_InitIdt	Proc	uses ds 			;初始化中断描述符表
	local	_Vidt:fword

	xor	eax,eax
	mov	ax, GdtSeg
	mov	ds, ax
;----------------------------------------------------------------------------
	sidt	fword ptr ds:[_IDT_Ptr]			;保存IDT

	mov	word ptr ss:[_Vidt], IdtSegLen		;IDT长度
	mov	eax, IdtSeg
	shl	eax, 4
	mov	dword ptr ss:[_Vidt+2], eax
	cli
	lidt	fword ptr ss:[_Vidt]			;装载IDT
;----------------------------------------------------------------------------
	ret
_InitIdt 	Endp
;----------------------------------------------------------------------------
Jmain		Proc

	call	_InitGdt				;初始化GDT全局描述符
	call	_InitDemoLdt				;初始化演示代码段LDT
	call	_InitTestLdt				;初始化测试代码段LDT
	call	_InitIdt				;初始化IDT
;----------------------------------------------------------------------------
	mov	ax, GdtSeg
	mov	ds, ax

	mov	ds:[_RegSs], ss
	mov	ds:[_RegSp], sp				;保存SS:SP

	_EnableA20					;开A20地址线
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax				;开启分段, 进入保护模式

	xor	ax, ax
	mov	es, ax
;----------------------------------------------------------------------------
	Jmp16	g_CodeTempSelector, <offset _ProtectEntry>;跳入保护模式

Jmain 		Endp
;----------------------------------------------------------------------------
_RealProtect	Proc                                    ;返回保护模式

        mov     ax, GdtSeg
        mov     ds, ax
        lss	sp, dword ptr ds:[_RegSp]		;恢复SS:SP
        lidt	fword ptr ds:[_IDT_Ptr]			;恢复IDT

        _DisableA20                                     ;关A20地址线, 开中断
        sti
        mov     ax, 4c00h
        int     21h
_RealProtect	Endp
StartCodeSeg	Ends

End	Jmain

网友评论:

  1. 飘雪-冬夜 说:

    学习了

发表评论


Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/agan/comments.php on line 66

您必须登录 才能进行评论。