保护模式的进入
2010-02-05 09:00
162 查看
终于进入保护模式了,从放假开始纠结了4天,虽然是冰山一角,但还是很兴奋的~呵呵。之前没接触过汇编,很多概念都不清楚,所以学到了很多。
计算机在开机执行完BIOS进行最初的初始化之后,会跳转到0x7c00处执行引导程序,此时CPU运行在实地址模式下,在实模式下,只能寻址1MB的空间,而且操作系统的代码和数据并没有得到保护,应用程序可能会访问并修改他们,这是很不安全的。而保护模式,顾名思义,是为操作系统提供了保护措施的模式,在保护模式下,应用程序不能直接访问系统代码和数据,只能通过系统调用访问,而且每个进程独享自己4GB的地址空间。而保护模式还有一个更直观且很重要的作用就是它提高了寻址能力,应用程序可以访问4GB的空间。
进入保护模式的主要步骤有
1.创建GDT表
2.载入GDT表
3.打开A20地址线
4.设置CR0的PE位
5.跳转到保护模式
1.创建GDT表
GDT是全局描述符表,用来保存每个段的信息,包括段的限界、基地址和一些属性。描述符的结构如下
高地址………………………………………………………………………低地址
; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; |7654321076543210765432107654321076543210765432107654321076543210| <- 共 8 字节
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24┃ (见下图) ┃ 段基址(23..0) ┃ 段界限(15..0)┃
; ┃ ┃ ┃ ┃ ┃
; ┃ 基址2┃③│②│ ① ┃基址1b│ 基址1a ┃ 段界限1 ┃
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; ┃ %6 ┃ %5 ┃ %4 ┃ %3 ┃ %2 ┃ %1 ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
; │ /_________
; │ /__________________
; │ /________________________________________________
; │ /
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
; ┃ G ┃ D ┃ 0 ┃ AVL┃ 段界限 2 (19..16) ┃ P ┃ DPL ┃ S ┃ TYPE ┃
; ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
; ┃ ③: 属性 2 ┃ ②: 段界限 2 ┃ ①: 属性1 ┃
; ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
; 高地址 低地址
在创建GDT时,一定要注意以下几点:
a.Intel是采用小端来保存数据的,我在此采用的是Nasm,Nasm中没有类似于GASM中.quad的指令,所以在Nasm中声明8字节数据时,必须要 按照从右到左的顺序声明。例如
gdt:
dw 0,0,0,0
;代码段描述符
dw 0x0010;1-2字节
dw 0x0000;3-4字节 可执行/写
dw 0x9a00;5-6字节
dw 0x00cf;7-8字节
;显示段描述符
dw 0x0010
dw 0x8000;可读/写
dw 0x920B
dw 0x00cf
而且也要注意,拿代码段描述符的第1-2字节为例,[gdt+8]处的数据是0x10而不是0x00,因为是小端存放的。但是在寄存器中的数据仍然是 0x0010,即如果用访问内存的方式(加中括号)访问数据时,一定要注意存放的顺序,而在访问寄存器时则不用考虑。
b.在进入保护模式后,表达式seg:offset中的seg和实模式下seg的含义是完全不同的。实模式下seg的含义即为某个段的基地址,而保护模式下 seg只是一个索引,它指明了某个段在GDT中的索引,且叫作选择子,它也有自己的结构,如下
; 选择子图示:
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9 ┃ 8 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
; ┃ 描述符索引 ┃ TI ┃ RPL ┃
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
其中TI=0表明请求的是在GDT中的段,TI=1表明是一个LDT中的段。RPL是请求特权级。可以看到,一个GDT中最多可以有2^13个段。
2.载入GDT表
载入GDT表,可以用lgdt指令,它将一个结构载入gdtr寄存器,该结构指明了GDT表的地址和GDT的界限。GDTR结构共有六个字节,低两字节 指明GDT的界限,高四字节指明GDT的地址。此处也要注意从右到左的声明顺序。
;GDTR
gdtptr:
dw 0x0010
dw gdt,0;不是0x7c00+gdt
其余三个步骤都较为简单且都差不多,所以不多说了。还要注意的是跳转到保护模式后,所有的代码都应该是在32位模式下,所以要在程序中用[BITS 32]指明。整个程序的代码如下:
程序中代码段和显示段是这样联系的:在disppmmsg中将显示段赋给gs,然后从12行开始,把要执行代码的地址赋给代码段的基地址。此处要执行的代码地址为当前段基址cs左移四位加上偏移。
计算机在开机执行完BIOS进行最初的初始化之后,会跳转到0x7c00处执行引导程序,此时CPU运行在实地址模式下,在实模式下,只能寻址1MB的空间,而且操作系统的代码和数据并没有得到保护,应用程序可能会访问并修改他们,这是很不安全的。而保护模式,顾名思义,是为操作系统提供了保护措施的模式,在保护模式下,应用程序不能直接访问系统代码和数据,只能通过系统调用访问,而且每个进程独享自己4GB的地址空间。而保护模式还有一个更直观且很重要的作用就是它提高了寻址能力,应用程序可以访问4GB的空间。
进入保护模式的主要步骤有
1.创建GDT表
2.载入GDT表
3.打开A20地址线
4.设置CR0的PE位
5.跳转到保护模式
1.创建GDT表
GDT是全局描述符表,用来保存每个段的信息,包括段的限界、基地址和一些属性。描述符的结构如下
高地址………………………………………………………………………低地址
; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; |7654321076543210765432107654321076543210765432107654321076543210| <- 共 8 字节
; |--------========--------========--------========--------========|
; ┏━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━┓
; ┃31..24┃ (见下图) ┃ 段基址(23..0) ┃ 段界限(15..0)┃
; ┃ ┃ ┃ ┃ ┃
; ┃ 基址2┃③│②│ ① ┃基址1b│ 基址1a ┃ 段界限1 ┃
; ┣━━━╋━━━┳━━━╋━━━━━━━━━━━╋━━━━━━━┫
; ┃ %6 ┃ %5 ┃ %4 ┃ %3 ┃ %2 ┃ %1 ┃
; ┗━━━┻━━━┻━━━┻━━━┻━━━━━━━┻━━━━━━━┛
; │ /_________
; │ /__________________
; │ /________________________________________________
; │ /
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━╋━━╋━━╋━━╋━━┻━━┻━━┻━━╋━━╋━━┻━━╋━━╋━━┻━━┻━━┻━━┫
; ┃ G ┃ D ┃ 0 ┃ AVL┃ 段界限 2 (19..16) ┃ P ┃ DPL ┃ S ┃ TYPE ┃
; ┣━━┻━━┻━━┻━━╋━━━━━━━━━━━╋━━┻━━━━━┻━━┻━━━━━━━━━━━┫
; ┃ ③: 属性 2 ┃ ②: 段界限 2 ┃ ①: 属性1 ┃
; ┗━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━┛
; 高地址 低地址
在创建GDT时,一定要注意以下几点:
a.Intel是采用小端来保存数据的,我在此采用的是Nasm,Nasm中没有类似于GASM中.quad的指令,所以在Nasm中声明8字节数据时,必须要 按照从右到左的顺序声明。例如
gdt:
dw 0,0,0,0
;代码段描述符
dw 0x0010;1-2字节
dw 0x0000;3-4字节 可执行/写
dw 0x9a00;5-6字节
dw 0x00cf;7-8字节
;显示段描述符
dw 0x0010
dw 0x8000;可读/写
dw 0x920B
dw 0x00cf
而且也要注意,拿代码段描述符的第1-2字节为例,[gdt+8]处的数据是0x10而不是0x00,因为是小端存放的。但是在寄存器中的数据仍然是 0x0010,即如果用访问内存的方式(加中括号)访问数据时,一定要注意存放的顺序,而在访问寄存器时则不用考虑。
b.在进入保护模式后,表达式seg:offset中的seg和实模式下seg的含义是完全不同的。实模式下seg的含义即为某个段的基地址,而保护模式下 seg只是一个索引,它指明了某个段在GDT中的索引,且叫作选择子,它也有自己的结构,如下
; 选择子图示:
; ┏━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┳━━┓
; ┃ 15 ┃ 14 ┃ 13 ┃ 12 ┃ 11 ┃ 10 ┃ 9 ┃ 8 ┃ 7 ┃ 6 ┃ 5 ┃ 4 ┃ 3 ┃ 2 ┃ 1 ┃ 0 ┃
; ┣━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━┻━━╋━━╋━━┻━━┫
; ┃ 描述符索引 ┃ TI ┃ RPL ┃
; ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━┻━━━━━┛
其中TI=0表明请求的是在GDT中的段,TI=1表明是一个LDT中的段。RPL是请求特权级。可以看到,一个GDT中最多可以有2^13个段。
2.载入GDT表
载入GDT表,可以用lgdt指令,它将一个结构载入gdtr寄存器,该结构指明了GDT表的地址和GDT的界限。GDTR结构共有六个字节,低两字节 指明GDT的界限,高四字节指明GDT的地址。此处也要注意从右到左的声明顺序。
;GDTR
gdtptr:
dw 0x0010
dw gdt,0;不是0x7c00+gdt
其余三个步骤都较为简单且都差不多,所以不多说了。还要注意的是跳转到保护模式后,所有的代码都应该是在32位模式下,所以要在程序中用[BITS 32]指明。整个程序的代码如下:
;[BITS 16] org 0x7c00 mov ax,cs mov ds,ax mov es,ax ;为GDT中的描述符赋基地址,即该段要执行的指令的地址 xor eax,eax mov ax,cs;cs中是当前段的基地址 shl eax,4;左移四位加段内偏移 add eax,disppmmsg ;改变GDT中的第一个描述符(codeselector) mov word [gdt+10],ax shr eax,16 mov byte [gdt+12],al mov byte [gdt+15],ah lgdt [gdtptr] ;关中断 cli ;打开A20 才能访问1M以外的地址 in al, 92h or al, 00000010b out 92h, al ;准备CR0 0x60000010 mov eax, cr0 or eax, 1 mov cr0, eax jmp dword codeselector:0 ;创建GDT gdt: dw 0,0,0,0 dw 0x0010 dw 0x0000;可执行/写 dw 0x9a00;从右往左写数据 dw 0x00cf dw 0x0010 dw 0x8000;可读/写 dw 0x920B;显存地址为0x0B8000 dw 0x00cf ;选择子 codeselector: equ 0x08 videoselector: equ 0x10 ;GDTR gdtptr: dw 0x0010 dw gdt,0 [BITS 32] disppmmsg: mov ax,videoselector mov gs,ax mov edi,(80*0+0)*2;每个字符占两个字节,包括一个属性字节 mov ah,0x0c;高字节为属性 mov al,'S';低字节为字符 mov [gs:edi],ax mov edi,(80*0+1)*2 mov ah,0x0c mov al,'u' mov [gs:edi],ax mov edi,(80*0+2)*2 mov ah,0x0c mov al,'n' mov [gs:edi],ax jmp $ times 510-($-$) db 0 dw 0xaa55
程序中代码段和显示段是这样联系的:在disppmmsg中将显示段赋给gs,然后从12行开始,把要执行代码的地址赋给代码段的基地址。此处要执行的代码地址为当前段基址cs左移四位加上偏移。
相关文章推荐
- 二、进入保护模式--内核加载器LOADER:实模式下内存容量检测、开启保护模式、开启分页模式、加载kernel到内存缓冲区、加载kernel到内存(内存复制函数)->kernel
- 《Orange’s 一个操作系统的实现》3.保护模式7-特权级转移(通过调用门转移目标段-有特权级转换-进入ring3-b)
- WriteOS: 关于操作系统进入保护模式的总结
- 自制操作系统Antz day02——进入保护模式 (上) jmp到保护模式
- 自制操作系统Antz day05——深入理解保护模式与进入方法
- 007-进入保护模式
- 桌面电脑操作系统开发笔记(4)——进入32位保护模式
- 进入保护模式
- 一个操作系统的实现(11)-让操作系统进入保护模式
- linux-0.11调试教程,显示strat minix后不能进入保护模式的原因,看bochsout.txt
- ASM:《X86汇编语言-从实模式到保护模式》第11章:进入保护模式
- 进入保护模式
- 进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17
- 实现OS中BOOTLOADER过程,并进入保护模式的代码
- 写操作系统----3.进入保护模式
- 【PM复习】进入保护模式
- setup.bin进入保护模式
- 3 实模式进入保护模式
- boot.s进入保护模式
- Linux Kernel 2.6.37 启动过程:漫步进入保护模式