在这一章的主要任务就是从实模式进入保护模式
- 首先创建几个初始化描述符,确保在进入保护模式之后程序能够正常运行,
- 将初始化描述符的内容放到GDT表中,此时虽然将GDT表的基地址和大小加载到GDT寄存器中,但是GDT表所在位置已经定义好了
- 接下来将GDT表所在位置和大小全部加载到GDT寄存器中
- 接下来开启第21根地址线,禁止中断,将cr0寄存器的PE位的值置为1,进入保护模式,但是此时还是处于16位的保护模式
-
什么是GDT表?
-
在保护模式中,内存访问方式与实模式不同,在实模式中,寻址方式是
段寄存器*16+偏移量
在32处理器中,段寄存器的结构发生了变化,结构如下
-
-
在保护模式下的内存访问的方式
- 段寄存器中存储的值为段描述符的索引,也就是0-15位可见部分
- 当将索引加载到寄存器中,处理器会自动到GDT表中根据索引找到对应的描述符,然后将描述符的内容加载到描述符高速缓冲器部分
- 内存访问的方式是根据段寄存器的高速缓冲器中的基地址的内容加上指令的偏移值(描述符的内容在将索引加载到寄存器时,会将描述符的值加载到寄存器的不可见部分),最终得到物理地址
GDT表其实是对应了一块内存区域,GDT寄存器保存了GDT表的基地址和表的最大偏移量(GDT表的大小 - 1),所有的全局描述符都存储在这块内存区域中,便形成了GDT表
GDT寄存器的格式如下:
![gdt寄存器.PNG](https://upload-images.jianshu.io/upload_images/17673361-541aa8d4861f01fd.PNG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 全局描述符表的边界总共占据了16位,而一个描述符占据8个字节,所以GDT表最多可以存储8192个描述符,而0号描述符不允许使用,所以最多是8191个全局描述符
-
那么什么是描述符呢?
在实模式模式下,我们是采用分段机制对内存进行访问的,在32位保护模式下,仍然是采用分段模式,即使是开启分页模式后,分段模式仍然使用,而描述符便是用来描述内存段(代码段和数据段)的属性
- 描述符的格式如下:
- 其中一些将要使用的属性如下:
- 段基地址:总共32位,存储了该段在内存中的位置
- 段界限:声明了该段的大小
- D/B:默认操作数大小,当值为1时,默认为32位,当值为0时,默认为16位
- type字段:用于指示描述符的类别,数据段寄存器只能装载数据类型的描述符,而cs寄存器之能装载代码段的描述符
type字段占据4个位,具体描述如下
- X:表示是否为可执行的
- E:表明扩展方向
- R:是否可读
- A:accessed,表明最近是否被访问
- 接下来就是开启第21根地址了,这个主要是以前为了兼容,在实模式下第21根地址线是默认关闭的,所以当进入保护模式就必须开启第21根地址线
- 进入保护模式就只要简单将cr0寄存器的PE位置为1即可
此时仍然是处于16位保护模式下,并且cs寄存器的值仍然是原先在实模式下的值,所以通过jmp指令跳转到下一条指令,但是cs寄存器的值将能够更新为对应代码段的选择子,并且还能够清空流水线
贴下代码
;代码清单11-1
;文件名:c11_mbr.asm
;文件说明:硬盘主引导扇区代码
;创建日期:2011-5-16 19:54
;设置堆栈段和栈指针
mov ax,cs
mov ss,ax
mov sp,0x7c00
;计算GDT所在的逻辑段地址
mov ax,[cs:gdt_base+0x7c00] ;低16位
mov dx,[cs:gdt_base+0x7c00+0x02] ;高16位
mov bx,16
div bx
mov ds,ax ;令DS指向该段以进行操作
mov bx,dx ;段内起始偏移地址
;创建0#描述符,它是空描述符,这是处理器的要求
mov dword [bx+0x00],0x00
mov dword [bx+0x04],0x00
;创建#1描述符,保护模式下的代码段描述符
mov dword [bx+0x08],0x7c0001ff
mov dword [bx+0x0c],0x00409800
;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区)
mov dword [bx+0x10],0x8000ffff
mov dword [bx+0x14],0x0040920b
;创建#3描述符,保护模式下的堆栈段描述符
mov dword [bx+0x18],0x00007a00
mov dword [bx+0x1c],0x00409600
;初始化描述符表寄存器GDTR
mov word [cs: gdt_size+0x7c00],31 ;描述符表的界限(总字节数减一)
lgdt [cs: gdt_size+0x7c00]
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al ;打开A20
cli ;保护模式下中断机制尚未建立,应
;禁止中断
mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移
;清流水线并串行化处理器
[bits 32]
flush:
mov cx,00000000000_10_000B ;加载数据段选择子(0x10)
mov ds,cx
;以下在屏幕上显示"Protect mode OK."
mov byte [0x00],'P'
mov byte [0x02],'r'
mov byte [0x04],'o'
mov byte [0x06],'t'
mov byte [0x08],'e'
mov byte [0x0a],'c'
mov byte [0x0c],'t'
mov byte [0x0e],' '
mov byte [0x10],'m'
mov byte [0x12],'o'
mov byte [0x14],'d'
mov byte [0x16],'e'
mov byte [0x18],' '
mov byte [0x1a],'O'
mov byte [0x1c],'K'
;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作
mov cx,00000000000_11_000B ;加载堆栈段选择子
mov ss,cx
mov esp,0x7c00
mov ebp,esp ;保存堆栈指针
push byte '.' ;压入立即数(字节)
sub ebp,4
cmp ebp,esp ;判断压入立即数时,ESP是否减4
jnz ghalt
pop eax
mov [0x1e],al ;显示句点
ghalt:
hlt ;已经禁止中断,将不会被唤醒
;-------------------------------------------------------------------------------
gdt_size dw 0
gdt_base dd 0x00007e00 ;GDT的物理地址
times 510-($-$$) db 0
db 0x55,0xaa
总结:第11章中伴随着的任务就是从实模式进入保护模式,顺便介绍了GDT表,段描述符的结构,cr0寄存器的内容,其实这个只是一个笔记,没有说很详细,有兴趣的同学可以看一下这本书,我个人的理解就是这一章有一个中心,那就是从实模式到保护模式,这一章的内容都是根据这个的需要来介绍的
本文由博客一文多发平台 OpenWrite 发布!