玩转保护模式

这几天还是在学习《自己动手写操作系统》,终于将保护模式了了。

上来贴上代码:

%include "pm.inc"
org		0100h
xchg	bx,bx
jmp		START
[SECTION .gdt]
GDT_DEC:		Descriptor		0	,	0	,	0	
CODE32_DEC:		Descriptor		0	,Code32SegLen-1,DA_32+DA_CE
VIDEO_DEC:		Descriptor	0b8000h	,	0ffffh,DA_DRW
DATA32_DEC:		Descriptor		0	,Data32SegLen-1,DA_DRW
STACK32_DEC:	Descriptor		0	,StackTop	,DA_32+DA_DRW
EXTRA_DEC:		Descriptor	050000h	,	0ffffh	,DA_DRW
CODE16_DEC:		Descriptor		0	,	0ffffh	,DA_CE
NORMAL_DEC:		Descriptor		0	,	0ffffh	,DA_DRW

GdtLen		equ		$ - GDT_DEC

Gdtptr		dw		GdtLen-1	;Length of GDT
			dd		0			;base of GDT

SelectorCode32	equ		CODE32_DEC	- GDT_DEC		
SelectorVideo	equ		VIDEO_DEC	- GDT_DEC
SelectorData	equ		DATA32_DEC  - GDT_DEC
SelectorStack	equ		STACK32_DEC	- GDT_DEC
SelectorExtra	equ		EXTRA_DEC	- GDT_DEC
SelectorCode16	equ		CODE16_DEC	- GDT_DEC
SelectorNormal	equ		NORMAL_DEC	- GDT_DEC

[SECTION .s16]
[BITS 16]
START:
	mov		ax,cs
	mov		ds,ax
	mov		ss,ax
	mov		es,ax
	mov		sp,100h
	
	mov		[BACK_TO_REAL+3],ax
	mov		[RealModeSp],sp

	;load Code32Segment Base
	xor		eax,eax
	mov		ax,cs
	shl		eax,4
	add		eax,CODE32_SEG
	mov 	[CODE32_DEC+2],ax
	shr		eax,16
	mov		[CODE32_DEC+4],al
	mov		[CODE32_DEC+7],ah

	;load data segment
	xor		eax,eax
	mov		ax,ds
	shl		eax,4
	add		eax,DATA32_SEG
	mov		[DATA32_DEC+2],ax
	shr		eax,16
	mov		[DATA32_DEC+4],al
	mov		[DATA32_DEC+7],ah

	;load stack segment base
	xor		eax,eax
	mov		ax,ss
	shl		eax,4
	add		eax,STACK_SEG
	mov		[STACK32_DEC+2],ax
	shr		eax,16
	mov		[STACK32_DEC+4],al
	mov		[STACK32_DEC+7],ah
	
	;load code16 segment base
	xor		eax,eax
	mov		ax,cs
	shl		eax,4
	add		eax,CODE16_SEG
	mov		[CODE16_DEC+2],ax
	shr		eax,16
	mov		[CODE16_DEC+4],al
	mov		[CODE16_DEC+7],ah

	;load GDT Base
	xor		eax,eax
	mov		ax,cs
	shl		eax,4
	add		eax,GDT_DEC
	mov 	[Gdtptr+2],eax
	lgdt	[Gdtptr]

	cli

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

	mov		eax,cr0
	or		eax,01h
	mov		cr0,eax
	jmp	dword	SelectorCode32:0   ;Enter to Protected mode!

ENTER_REAL_MODE:
	mov		ax,cs
	mov		ds,ax
	mov		es,ax
	mov		ss,ax
	mov		fs,ax
	mov		gs,ax
	mov		sp,[RealModeSp]

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

	sti
	;call BIOS int 10h to test if back to real
	mov		cx,22
	mov		bh,0
	mov		bl,89h
	mov		dx,1000h
	mov		bp,BackRealMeg
	mov		ah,13h
	mov		al,01h
	int		10h
	
	;back to dos
	mov		ax,4c00h
	int		21h

[SECTION .p16]
ALIGN 32
[BITS 16]
CODE16_SEG:
	mov		ax,SelectorNormal
	mov		ds,ax
	mov		ss,ax
	mov		gs,ax
	mov		fs,ax
	mov		es,ax

	mov		eax,cr0
	and		eax,0feh
	mov		cr0,eax
BACK_TO_REAL:
	jmp		0:ENTER_REAL_MODE
Code16SegLen	equ		$ - $$

[SECTION .s32]
ALIGN	32
[BITS 32]
DATA32_SEG:
Message:	db "Welcome to Protected Mode!",0
MessageLen	equ	 $ - Message
offMessage  equ	 Message - $$
RealModeSp	dw	0
Letters:	db "ABCDEFGHIJKLMNOPQRSTUVWXYZ",0
offLetters	equ	Letters  -$$

BackRealMeg: db "Welcome back to real!\n"

Data32SegLen	equ  $ - $$ 

[SECTION .stack]
ALIGN	32
[BITS 32]
STACK_SEG:
times 512 db 0
StackTop equ	$ - STACK_SEG - 1

[SECTION .s32]
[BITS 32]
CODE32_SEG:
	mov		ax,SelectorVideoto 
	mov		gs,ax

	mov		ax,SelectorData
	mov		ds,ax
	
	mov		ax,SelectorStack
	mov		ss,ax
	mov		esp,StackTop

	mov		ax,SelectorExtra
	mov		es,ax

	call	DispMessage
	call	DispReturn
	call	ReadFromExtraSeg
	call	WriteToExtraSeg
	call	ReadFromExtraSeg

	jmp		SelectorCode16:0

DispMessage:
	mov		ah,0ch
	mov		esi,offMessage
	mov		edi,(80*12+20)*2
	mov		ecx,MessageLen
	.loop:
		mov		al,[esi]	
		mov		[gs:edi],ax
		add		edi,2
		inc		esi
		loop .loop
	ret

ReadFromExtraSeg:
	xor		esi,esi
	mov		ecx,08h
	.loop:
		mov		al,[es:esi]
		call	DispAlAsNum
		inc		esi
		loop .loop
		call	DispReturn
		ret

DispAlAsNum:
	push	ecx
	push	edx
	mov		ecx,02h
	mov		dl,al
	shr		al,4
	mov		ah,0ch
	.loop:
		and		al,0fh
		cmp		al,09h
		ja		.letter
		add		al,'0'
		jmp		.Disp
		.letter:
		sub		al,0ah
		add		al,'A'
		.Disp:
		mov		[gs:edi],ax
		mov		al,dl
		add		edi,2
		loop	.loop
		add		edi,2
		pop		edx
		pop		ecx
		ret

DispReturn:
	push	eax
	push	ebx
	mov		eax,edi
	mov		bl,160
	div		bl
	and		eax,0ffh
	inc		eax
	mul		bl
	mov		edi,eax
	pop		ebx
	pop		eax
	ret

WriteToExtraSeg:
	push	esi
	push	edi
	xor		esi,esi
	xor		edi,edi
	mov		esi,offLetters
	cld
	.loop:
		lodsb
		test	al,al
		jz		.ok
		mov		[es:edi],al	
		inc		edi
		loop	.loop
		.ok:
		pop		edi
		pop		esi
		ret

Code32SegLen	equ		$ - $$

然后运行结果图:


这个程序先从实模式进入保护模式,然后从附加段中读数据并输出,再从数据段向附加段写数据,然后输出。最后返回实模式。

刚开始时,没注意,还是利用org 0x7c00开头,直接从该软驱启动,但是提示找不到启动设备。然后利用Hex Edit查看,发现 510 511 单元不是55  aa而是00 00 ,再查看源代码,可以发现这块已经被Stack占用,也就是说,我们得程序已经超过了512B,那么,只好利用其他东西(如Freedos)来做引导.最后将改程序放入其一个软驱中。在DOS中运行这个程序。

还有就是,在DOS中如果程序出问题了该怎么办,我一开始想到得是Debug,但是遗憾得是没有这个命令。所以只好另图他径。首先在Bochs配置文件中添加magic_break : enabled=1,然后在程序中利用xchg bx,bx来设置断点。当你运行改程序时,在这里会自动将控制权交给Bochs,然后你就可以在Bochs中调试了。

扫描二维码关注公众号,回复: 1296738 查看本文章

再后呢就是关于从保护模式跳入实模式得步骤:

1. 关中断, 因为一开始中断就关掉了, 这步可以省略 
2. 将程序从一个32位的代码段转移到16位的代码段, 这个代码段的段界限必须是64KB, 即FFFFh, 就是程序中的 
SelectorCode16描述的那个段 
  这个的作用是刷新CS寄存器, 使它符合实模式下cs寄存器的要求 
3. 刷新数据段的寄存器, 包括DS, ES, FS, GS, SS, 使得它们也符合实模式下的要求, 就是mov ax, 
SelectorNormal的作用, SelectorNormal的要求是: 
  Limit = 64KB ( 0FFFFh ) 
  Byte granular = 0 ( G = 0 ) 
  Expand up ( E = 0 ) 
  Writable ( W = 1 ) 
  Present ( P = 1 ) 
  Base = any value 
  也就是说, SelectorNormal描述的段要完全符合实模式下的数据段的要求 
4. 清Cr0的PE位, 真正的转换到实模式, 需要注意的是SelectorCode16描述的那个段中的代码仍然是保护模式下的, 只不过是16 
位的代码 
  只有当PE位清0的时候, 才真正的转换到实模式, 这个时候, 需要有一个跳转, 真正的跳转到实模式下的代码段, 而这个跳转的寻址方式是实 
模式寻址方式 
5. 经过第4步的关键跳转, CS被更新成了真正的实模式下的段地址, 这个时候, 需要重新刷新数据段寄存器, 使他们工作在实模式下, 
  这些数据寄存器包括上述提到的所有的段寄存器, 即DS, ES, FS, GS, SS, 如果你不用它们的话, 就直接赋值为0也是可以的 
6. 开中断, 这步完成之后, 就完全完成了保护模式到实模式的切换 

  此时,你可以调用dos中断返回dos操作系统, 书上就是这么做的 


猜你喜欢

转载自tviker.iteye.com/blog/1478019