【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 $
运行结果如下:
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 $
运行结果如下:
![](http://blog.csdn.net/userfiles/未命名(130).jpg)
相关文章推荐
- 【PM复习】进入保护模式改进
- 【PM复习】进入保护模式再改进
- 【PM复习】从保护模式切换回实模式
- 【PM复习】保护模式下的一点迷你应用(上)
- 【PM复习】保护模式下的一点迷你应用(下)
- 进入保护模式总结
- ASM:《X86汇编语言-从实模式到保护模式》第11章:进入保护模式
- 写操作系统(八)执着 进入保护模式
- linux-0.11调试教程,显示strat minix后不能进入保护模式的原因,看bochsout.txt
- 进入保护模式(一)
- 加载setup后,进入保护模式
- 《Orange’s 一个操作系统的实现》3.保护模式7-特权级转移(通过调用门转移目标段-有特权级转换-进入ring3)
- Linux Kernel 2.6.37 启动过程:漫步进入保护模式
- 进入保护模式(二)——《x86汇编语言:从实模式到保护模式》读书笔记14
- 一步一步进入保护模式
- 进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17
- 进入保护模式(二)
- [Intel汇编-NASM]进入保护模式之前的准备
- java开发操作系统内核:由实模式进入保护模式之32位寻址
- 第4章 进入保护模式(设置GDT全局描述符表)