自己动手写操作系统读书笔记——第三章:保护模式(上)
- 准备环境
- 关于CPU的一些说明
- 保护模式
-
- 段式内存管理
-
- 全局描述符表(GDT)
- 局部描述符表(LDT)
- 描述符选择子
- 进入保护模式
- 进入保护模式
-
- A20地址线
- Cr0寄存器
准备环境
在测试本书源码的时候,我选择的环境是Vmware+IDA,当然也可以使用单纯的gdb调试器,但是IDA集成了源码分析,汇编等功能,使用起来较为方便。至于选择这个环境的原因在于Vmware使用的人较多,而且联合IDA调试比较方便。
这里就记录一下Vmware的相关设置
debugStub.listen.guest32 = "TRUE"//联合调试
debugStub.hideBreakpoints= "TRUE"//用硬件断点代替软件断点
debugStub.listen.guest32.remote = "TRUE"//远程调试
#monitor.debugOnStartGuest32 = "TRUE"//在运行BIOS启动代码时下断点
关于CPU的一些说明
在阅读本书之前,我一直认为CPU内部的寄存器只有寥寥几个,如通用寄存器、EFLAGS、段、IP寄存器这几个分类,但是等到读完本书并且读到了《软件调试》后才发现,实际上CPU的寄存器还有许许多多,并且关于这些寄存器的用法都有着特别的规定,比如在段式管理中出现的GDTR寄存器。
保护模式
在IA-32架构下,CPU有三种工作模式:
- 实模式
- 保护模式
- 虚拟86模式
实模式是对8086CPU的兼容,而保护模式则是IA-32处理器工作的最主要模式,也是多道程序操作系统所工作的环境,而虚拟86模式则是实现了多个实模式的并发运行。
保护模式是一个庞大的内容,包括了GDT、LDT、分页、中断、特权级、TSS、I/O保护等等内容。
需要指出的,实现这些机制是需要硬件设施的,这也意味着对于这些机制,我们是不可能在软件调试器中看到每一步的实现的。
段式内存管理
在保护模式下,由于ip寄存器的大小达到了32bit,所以寻址能力达到了4GB之多,在全新的保护模式下,我们不再需要将内存地址划分为 段寄存器:偏移 这样模式,但是段寄存器并没有被取代,但是寄存器内部的内容不再是一个段基址了,而是一个包含了线性表的相对偏移的索引。这个线性表就是GDT。需要注意的是,这个索引是按字节为粒度的。
全局描述符表(GDT)
GDT是一个数组,每一个表项分别描述了线性内存中的一块区域,描述的内容包括段基址(线性地址)、段界限、属性这三个内容,我们把GDT中的表项称为描述符(Descriptor),在保护模式中只有一个GDT发挥作用。需要注意的是GDT代表的是经典的段式管理,所以每一个描述符描述的内存空间不一样相等,而且两个描述符中间可能存在空洞。
那么描述符中的属性有些什么呢?比较重要的有
- 描述符的类型:包括段描述符、门描述符
- 段在内存中是否存在
- 段的特权级
- 段的读写、粒度等属性
在CPU进行内存操作的时候,会根据描述符进行相应的检查,如果检查不通过会出现相应的异常。
当我刚刚了解到上面这个知识的时候,我一直在想一个问题,CPU是如何知道GDT的基地址的呢?是约定俗称的吗?实际上,这个问题的答案很简单,在CPU内部有一个专门的寄存器用于存放GDT的基地址,名称叫做GDTR。实际上,这种专门开辟一个寄存器来存放一个特定内容的做法在IA-32架构里面还很多。使用如下指令就可以将GDT表放入这个寄存器中,注意到,这里使用了一个lgdt操作码,对应的还有sgdt指令。
lgdt [GdtPtr]
需要注意的是,放入寄存器的并不是单纯的GDT的基地址,而是下面这样一个结构体
struct
{word GdtLen;//以字节为单位dword GdtBase;
}
所以一个GDT最多可以放2132^{13}213个描述符
局部描述符表(LDT)
LDT大体上和GDT差不多,加载其基地址的指令也差不多lldt something
仅仅有一点点区别:
- LDT可以有许多个
- LDTR寄存器的内容有一点不一致,其内容是一个GDT描述符
描述符选择子
在上面我们提到了段寄存器中的内容,这里我们正式把他赋予一个名字,选择子。选择子的结构如下
选择子的低2位和特权级有关,第3位为TI位,这个位指定了我们的选择子选择的是GDT里面的描述符还是LDT里面的描述符。
进入保护模式
现在我们已经了解了保护模式下必须的段式模式中绝大部分东西(除了特权级),那么CPU是如何进行模式切换的呢?有以下几个步骤:
- 准备GDT
- 装载GDT
- 打开A20地址线
- 打开位于Cr0寄存器中的保护模式标志位(PE位)
- 更新选择子
进入保护模式
现在我们已经了解了保护模式下必须的段式模式中绝大部分东西(除了特权级),那么CPU是如何进行模式切换的呢?有以下几个步骤:
- 准备GDT
- 装载GDT
- 打开A20地址线
- 打开位于Cr0寄存器中的保护模式标志位(PE位)
- 更新选择子
A20地址线
由于要兼容A20的程序,在实模式下A20这个位的地址线总是为0的,所以为了开启保护模式,达到4GB的寻址能力,我们必须打开A20地址线。我们通过以下指令来开启A20地址线。
in al,92h
or al,00000010b
out 92h,a1
通过这个例子我们可以看到微机系统中CPU控制系统其他部分的通用方法:通过向约定的端口读/写操作字来控制外设和芯片。
Cr0寄存器
就如同前面所说的那样,CPU的通用寄存器、段寄存器、PSW等寄存器都只是冰山上的一角,除了这些之外CPU还有其他其他用途的寄存器,在这里我们第一次遇见了专用寄存器Cr0,这个寄存器的最低位置1标志着CPU工作在保护模式下。