您的位置:首页 > 产品设计 > 产品经理

【PM复习】进入保护模式

2010-01-23 16:52 183 查看
我们知道,现在大部分的CPU都是32位的,我们编写的OS当然也想在运行在32位的模式下,因为32位的CPU功能自然比16位的强大许多,32位的CPU在硬件给予了OS很大的支持。但是当机器刚刚启动时,CPU是运行在16位模式下的,所以我们必须学习怎么样进入32位模式,也即保护模式。

16位模式也就是实模式,是我们最开始学习汇编时CPU运行的模式,是用段寄存器左移4位+偏移地址来寻址的;而在保护模式下则大不一样,是先分段再分页寻址的,分页是可选的,分段是必须的。所以想进入保护模式的第一步就是建立一张段表,也就是GDT,这个表中的每一个项就是一个段描述符Descriptor,有8个字节,记录了一个段的起始地址,段界限,以及段属性,从而对每一个段进行保护。

Code:

[section .gdt]

LABEL_GDT:

LABEL_DESC_DUMMY:

times 8 db 0

LABEL_DESC_CODE32:

dw 0ffffh

times 3 db 0

db 9ch

db 4fh

db 0

LABEL_DESC_VIDEO:

dw 0ffffh

dw 8000h

db 0bh

db 92h

db 4fh

db 0

如上面的代码所示,GDT中有3个描述符,第1个是哑描述符,没什么意义;第2个表示1个32位的可执行非一致代码段,段界限为0xfffff,段基址没有填写,日后会填充;第3个是1个可读写的数据段,段界限位0xfffff,段基址为0xb8000,此段指示了显存。描述符的结构在这里就不细说了,查查书就好了。

有了GDT,我们寻址的时候只要把想访问的段的相对于GDT的索引送到段寄存器就OK了,那么怎么知道GDT在内存中的位置呢?在CPU有一个48位的寄存器,就存放着GDT在内存的地址和GDT的长度。所以在进入保护模式必须给这个寄存器填上正确的地址和长度。如下代码所示:

Code:

GDT_Len equ $ - LABEL_GDT

GDT_Ptr:

dw GDT_Len - 1

dd 0

我们看到,GDT的长度已填上,GDT实际的物理地址还不知道,因为我们的测试程序是在DOS下动态加载的,所以在日后再填写,这个以后填充段描述符的段基地址是一样的道理。

还有一个概念是选择子selector,是一个16位的结构,它的高13位是相应段在GDT中的索引,为什么只用高13位表示呢?简单啊,因为一个描述符是8个字节,所以索引肯定是8个倍数,低3位就用不着,可以拿来做别的事,至于是什么事呢,日后再说。下面就是定义选择子的代码:

Code:

Selector_Code32 equ LABEL_DESC_CODE32 - LABEL_DESC_DUMMY

Selector_Video equ LABEL_DESC_VIDEO - LABEL_DESC_DUMMY

准备好了这些,还不够啊,还有一些东东没有填充好呢。先不理它,先明确我们进入保护模式后要干嘛,那就在屏幕上打印个字符吧,这样可以测试下我们的GDT有没有设置正确,下面就是这个32位的代码段:

Code:

[section .s32]

[bits 32]

LABEL_CODE32:

mov ax,Selector_Video

mov gs,ax

mov ah,0ch

mov al,'x'

mov [gs:80 * 10],ax

jmp $

为了简单起见,打印完后进入死循环。

最后只剩16位代码了,这段代码当然是做与进入保护模式有关的事情了,下面是代码:

Code:

[section .s16]

[bits 16]

LABEL_BEGIN:

mov ax,cs

mov ds,ax

mov es,ax



xor eax,eax

mov ax,cs

shl eax,4

add eax,LABEL_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



xor eax,eax

mov ax,ds

shl eax,4

add eax,LABEL_GDT

mov dword [GDT_Ptr + 2],eax



lgdt [GDT_Ptr]



cli



in al,92h

or al,00000010b

out 92h,al



mov eax,cr0

or eax,1

mov cr0,eax



jmp dword Selector_Code32:0

8到15行是填充32位代码段的段基址,17到21行是填充GDT的地址,接着加载到GDTR寄存器中,然后是关掉中断,因为在保护模式下中断的机制跟实模式不太一样,先关掉日后再说。接着打开A20地址线使得能够寻址到更大的范围,接着打开cr0寄存器的最低位,值得让CPU进入保护模式,最后是历史性的跳转正式跳转到保护模式。

下面是完整的代码:

Code:

org 0100h

jmp LABEL_BEGIN



[section .gdt]

LABEL_DESC_DUMMY:

times 8 db 0

LABEL_DESC_CODE32:

dw 0ffffh

times 3 db 0

db 9ch

db 4fh

db 0

LABEL_DESC_VIDEO:

dw 0ffffh

dw 8000h

db 0bh

db 92h

db 4fh

db 0



GDT_Len equ $ - LABEL_DESC_DUMMY

GDT_Ptr:

dw GDT_Len - 1

dd 0



Selector_Code32 equ LABEL_DESC_CODE32 - LABEL_DESC_DUMMY

Selector_Video equ LABEL_DESC_VIDEO - LABEL_DESC_DUMMY



[section .s16]

[bits 16]

LABEL_BEGIN:

mov ax,cs

mov ds,ax

mov es,ax



xor eax,eax

mov ax,cs

shl eax,4

add eax,LABEL_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



xor eax,eax

mov ax,ds

shl eax,4

add eax,LABEL_DESC_DUMMY

mov dword [GDT_Ptr + 2],eax



lgdt [GDT_Ptr]



cli



in al,92h

or al,00000010b

out 92h,al



mov eax,cr0

or al,1

mov cr0,eax



jmp Selector_Code32:0



[section .s32]

[bits 32]

LABEL_CODE32:

mov ax,Selector_Video

mov gs,ax

mov ah,0ch

mov al,'x'

mov [gs:80 * 10],ax

jmp $

运行结果如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: