从保护模式返回实模式

上节课我们从实模式进入了保护模式,并在保护模式下打印了2个字符串。

 我们并没有设置esp寄存器,却能够正常的进行函数调用。我们需要通过反编译来查看esp寄存器的值。我们通过 ndisasm -o 0x9000 loader > loader.txt 命令来进行反编译。

 我们在66行这条指令的地址处打上断点,这条指令是从16位实模式跳转到32位保护模式所对应的指令,我们查看16位实模式下的sp寄存器和32位保护模式下esp寄存器的值。

通过下图我们观察到16位实模式下esp寄存器的值为0x7c00

进入了32位保护模式后esp寄存器的值依然为0x7c00

而我们设置的32位保护模式下栈的基地址为0,最大偏移量为0x7c00,0x0000 - 0x7c00这段地址空间作为我们32位保护模式下的栈空间,所以在进入32位保护模式后,我们依然可以正常的进行函数调用。

定义保护模式下的栈

 在实模式和保护模式下我们所使用的栈空间是相同的,我们需要进行改进,在保护模式我们需要使用另外的内存空间来当作栈空间。我们需要指定一段内存空间,为其定义段描述符,根据段描述符表的位置定义选择子,最后初始化栈段寄存器和栈顶寄存器。

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

Stack32SegLen	equ    $ - STACK32_SEGMENT
TopOfStack32    equ    Stack32SegLen - 1

这是我们重新添加的保护模式下的栈段,通过重新设置选择子,ss和esp寄存器,程序正常的打印了字符串,但保护模式下的栈使用的是我们新定义的内存空间,大小为4k,和实模式下的栈区不同。我们通过反编译来查看esp寄存器的值。

 在进入保护模式前esp寄存器的值为0x7c00

进入保护模式后,esp寄存器的值变为0xfff 

从32位保护模式跳转到实模式

80x86有一个神秘的限制,无法从32位代码段回到实模式,只能从16位代码段间接返回实模式,在返回前必须用合适的选择子对段寄存器赋值。

80286之后的处理器都提供兼容8086的实模式,然而,绝大多数处理器都运行于保护模式,处理器在保护模式下,需要频繁的在内存中访问相应段的描述信息,处理器的速度是远大于访问内存的速度的,为了提高效率,可以利用高速缓冲器。当选用选择子来设置段寄存器时,根据选择子访问内存中的段描述符,将段描述符加载到段寄存器的高速缓冲器中,需要段描述信息时,直接从高速缓冲器中获得。

当处理器运行于实模式时,段寄存器的高速缓冲器是否会用到?

在实模式下,高速缓冲器仍然发挥着作用。段基址是32位,其值是相应段寄存器的值乘以16。实模式下段基址有效位为20位,段界限固定为0xFFFF(64k)。段属性的值不可设置,只能继续沿用保护模式下所设置的值。因此,从保护模式返回实模式时,通过加载一个合适的描述符选择子到有关段寄存器,以使得对应描述符高速缓冲器中含有合适的段界限和属性。

%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
; 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
; 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

	; 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 PrintString

	mov ebp, HELLOWORLD_Offset
	mov bx, 0x0c
	mov dh, 14
	mov dl, 30
	call PrintString
	
	jmp Code16Selector : 0

; ds:ebp    --> string address
; bx        --> attribute
; dx        --> dh : row, dl : col
PrintString:
	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

	ret

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

在15、16行,我们添加了16位保护模式的段描述符和返回实模式时所需要的段描述符。在15行,我们设置16位保护模式的段描述符的段界限位0xffff,是因为在此模式下我们需要跳转到实模式,如果我们把段界限设置为这个段的长度 - 1,那么跳转后的ip值很可能大于保护模式下的段界限,从而cpu产生异常指令。在16行,我们设置了实模式下的段界限和段属性。在29行和30行,我们设置了对应的选择子。第70-72行,设置了16位保护模式下的段基址。

210-215行,我们把实模式对应选择子的值赋值给相应的段寄存器,是为了刷新各个段寄存器的高速缓冲存储器,使得段寄存器里的高速缓存存储器存储的是实模式下的段描述信息。217-220行是为了关闭保护模式,我们并没有将实模式对应选择子的值赋值给cs寄存器是因为此时只是关闭了保护模式,还没有进入实模式。221-222行,跳转到实模式,我们给出的段地址为0,后面会把它动态的改变为实模式下cs寄存器的值。

在55行,我们动态的改变了222行的段地址。jmp指令在段间的跳转时,基地址存放在jmp指令地址偏移3字节处。

跳转到100行后,就进入了实模式。101-105行,我们重新恢复了实模式下各个段寄存器的值。107-111行,我们关闭了A20地址总线,并打开了中断。113-118行,我们通过BIOS的0x10号中断打印一个字符串。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值