x86总览

1.x86系统架构总览

1.1系统级体系结构总览

全局和局部描述符表

全局描述符表(GDT)是保护模式所必须的数据结构,里面存放的是段描述符,GDT相当于存放段描述符的数组,而索引则是选择子,全局则体现在多个程序都可以在GDT中定义自己的段描述符,是公用的,全局可见。GDT在内存中,需要用专门的寄存器GDTR(Global Descriptor Table Register)指引CPU找到GDT。GDTR是48位的寄存器,低16位存放GDT界限,剩余32位存放GDT起始地址。
局部描述符表(LDT)是为在硬件一级原生支持多任务而创建的表,按照设想,一个任务对应一个LDT。 每个任务的私有内存段都应该放到自己的段描述符表中,即每个任务都要有自己的LDT,随着任务的切换,也要切换到相应任务的LDT。LDT需要先在GDT中注册一个描述符,与GDT不同的是,LDT的第0个段描述符是可用的,因为选择子中的TI位为1才表示在LDT中索引段描述符,TI为1必然是经过显式初始化的结果。

选择子

实模式中,段寄存器存放的是段基地址,而在保护模式下段基址已经存放在段描述符中了。因此,段寄存器的作用就改为了存储全局描述符表的索引——选择子。
其中选择子的第2位是TI(Table Indicator)位,用来表示选择子是在GDT中,还是LDT中,该位为1表示在GDT中,为0表示在LDT中

段描述符

IA-32架构的处理器访问内存都是采用“段基址:段内偏移地址”的形式,即使到了保护模式也不例外。其次,实模式脆弱的安全性也是保护模式推出的重要原因。为了内存安全性,必须为内存段添加一些额外的安全属性,如特权级、段界限、段类型等。
段描述符如下

字节 7 6 - 5 4 - 2 1 - 0
内容 31…24段基址2 属性 23…0段基址1 15…0段界限1

其中属性包括

15 14 13 12 11 - 8 7 6 - 5 4 3-0
内容 G D/B 0 AVL 19…16段界限2 P DPL S TYPE

一个段描述符是8字节大小,它描述了一个内存段的地址范围和各种属性。
保护模式下地址总线宽度是32位,因此段基址需要用32位地址来表示。从上图看出段基址字段一共有2个部分,分别在第2~4字节和第7字节。这样划分的目的是为了兼容旧式的16位CPU(段界限字段也是如此)。

段界限和G字段:段界限表示段边界的扩展极值,即最大扩展到多少或最小扩展到多少。扩展方向只有上下两种,对于数据段和代码段,段的扩展方向是向上,即从低地址向高地址扩展,此时的段界限用来表示段内偏移的最大值(上界);对于栈段,段的扩展方向是向下,即从高地址向低地址扩展,此时的段界限表示段内偏移的最小值(下界)。无论是向上还是向下,段界限都表示段的边界。段界限字段给出的只是数值,其单位(或称粒度)则在G位中给出,G位为0则粒度为B,为1则为4KB。

内存访问需要用到“段基址:段内偏移地址”,段界限其实是用来限制段内偏移地址的,段内偏移地址必须位于段描述符给出的范围之内,否则CPU会抛出异常。任何超范围的偏移地址都被认为是非法的,CPU会捕获这个异常。(这也就是日常写程序中的报的段错误)

S字段:属性字段中的type字段用来指定段描述符的类型,而S位的数值决定了type字段中不同位的含义。一个段描述符首先分为两大类,要么是系统段(S位置0),要么是非系统段(S位置1),或称数据段。对于CPU而言,凡是硬件运行需要用到的东西都可称之为系统(如硬件在内存中的映射),凡是软件需要用到的东西(操作系统也是软件,对CPU而言在这一层面它与用户程序无区别)都是数据。无视是代码还是数据,都是作为硬件的输入,因此我们常说的代码段在段描述符中也属于数据段(非系统段)。type字段要和S字段配合才能确定段描述符的确切类型,只有S字段的值确定后type字段的值才有意义。

DPL字段为描述符特权级,这是保护模式提供的安全解决方案,有4个不同等级(0~3,数值越小特权级越大)。

AVL即Available,表示段是否可用

D/B字段:用来表示有效地址(段内偏移地址)及操作数的大小。对于代码段来说,此位是D位,若D为0,表示指令中的有效地址和操作数是16位,使用ip寄存器;否则为32位,使用eip寄存器。对于栈段来说,此位是B位,用来指定操作数大小,若B为0,使用的是sp寄存器,也就是栈的起始地址是16位寄存器的最大寻址范围,0xFFFF;若B为1则使用esp寄存器,即栈的起始地址是32位寄存器的最大寻址范围,0xFFFFFFFF。

1.2实模式和保护模式

实模式

实模式的“实”体现在程序中用到的地址都是真实的物理地址,“段基址:段内偏移地址”产生的逻辑地址就是物理地址,即程序员可见的地址完全是真实的内存地址。
实模式下由16位段寄存器的内容乘以16(左移4位)作为段基址,加上16位段偏移地址形成20位的物理地址,最大寻址空间1MB,最大分段64KB。可以使用32位指令,即32位的x86 CPU也可以兼容实模式,此时的实模式相当于高速的8086(32位CPU的实模式可以使用32位下的资源)。在32位CPU下,系统复位或加电时都是以实模式启动,然后再切换为保护模式。在实模式下,所有的段都是可以读、写和可执行的。
在实模式下,用户程序和操作系统拥有同等权利,因为实模式下没有特权级。此外,程序可以随意修改自己的段基址,加上实模式下对地址的访问就是实实在在的物理地址,因此程序可以随意修改任意物理地址,甚至包括操作系统所在的内存,这给操作系统带来极大的安全问题。<

保护模式

为了改进实模式下内存访问的不安全性,保护模式给内存段添加了段属性来限制用户程序对内存的操作权限。保护模式引入了全局描述符表(Global Descriptor Table,GDT),GDT的表项是描述段类型属性的数据结构——段描述符。GDT中的每一个段描述符都描述了一个内存段的基本属性,如段基址、段界限、类型、DPL等等。由于以上概念的提出,使得“段地址:段内偏移地址”的访问策略从实模式下对物理地址的直接映射变成了保护模式下对GDT或LDT的间接映射

实模式向保护模式切换

CPU启动环境为16位实模式,之后可以切换到保护模式。但从保护模式无法切换回实模式,但是可以切换会虚拟实模式

实模式>>保护模式>>实模式
切换流程:
1、设置必要的实模式环境,如实模式下的堆栈等。
2、初始化全局描述符表(GDT)、局部描述符表(LDT)及中断描述符表(IDT)等。
3、保存实模式下的堆栈地址到某内存处,以便切换回实模式后恢复,如有必要也可保存DS、ES、FS、GS等数据段寄存器的值。 (cs是代码段寄存器,ds是数据段寄存器,ss是堆栈段寄存器,es是扩展段寄存器,fs是标志段寄存器,gs是全局段寄存器)
4、加载全局描述符表至全局描述符表寄存器(GDTR),如果未定义中断描述符表,则关中断,然后打开地址线A20。
5、修改cr0的PE位为1,切换到保护模式。
6、使用段间跳转指令转到保护模式下的段,如果有局部描述符表,则应首先加载局部描述表段至局部描述符表寄存器(LDTR)。
7、设置保护模式下的堆栈段SS及堆栈指令SP(ESP)。
8、设置DS、ES、FS、GS指向某个数据段,防止无意中使用到未设置的数据段。
9、准备切换回实模式,用于切换回实模式的段必须是16位段且其段描述符必须定义在GDT中,其段限制必须是0FFFFH。
10、修改CR0的PE位为0切换回实模式。
11、恢复堆栈段至切换到保护模式之前的状态,如有必要也可恢复DS、ES等数据段。
12、关闭地址线A20。如中断为关闭状态,则打开中断。

1.3系统指令寄存器

EFLAGS(标志寄存器)

x86总览
1、状态标志(Status Flags)

EFLAGS寄存器的状态标志(0、2、4、6、7以及11位)指示算术指令(如ADD, SUB, MUL以及DIV指令)的结果,这些状态标志的作用如下

CF(bit 0) [Carry flag] 若算术操作产生的结果在最高有效位(most-significant bit)发生进位或借位则将其置1,反之清零。这个标志指示无符号整型运算的溢出状态,这个标志同样在多倍精度运算(multiple-precision arithmetic)中使用。
PF(bit 2) [Parity flag] 如果结果的最低有效字节(least-significant byte)包含偶数个1位则该位置1,否则清零。
AF(bit 4) [Adjust flag] 如果算术操作在结果的第3位发生进位或借位则将该标志置1,否则清零。这个标志在BCD(binary-code decimal)算术运算中被使用。
ZF(bit 6) [Zero flag] 若结果为0则将其置1,反之清零。
SF(bit 7) [Sign flag] 该标志被设置为有符号整型的最高有效位。(0指示结果为正,反之则为负)
OF(bit 11) [Overflow flag] 如果整型结果是较大的正数或较小的负数,并且无法匹配目的操作数时将该位置1,反之清零。这个标志为带符号整型运算指示溢出状态。

2、DF标志位

这个方向标志(位于EFLAGS寄存器的第10位)控制串指令(MOVS, CMPS, SCAS, LODS以及STOS)。设置DF标志使得串指令自动递减(从高地址向低地址方向处理字符串),清除该标志则使得串指令自动递增。STD以及CLD指令分别用于设置以及清除DF标志。

3、系统标志

EFLAGS寄存器中的这部分标志用于控制操作系统或是执行操作,它们不允许被应用程序所修改。这些标志的作用如下:
TF(bit 8) [Trap flag] 将该位设置为1以允许单步调试模式,清零则禁用该模式。
IF(bit 9) [Interrupt enable flag] 该标志用于控制处理器对可屏蔽中断请求(maskable interrupt requests)的响应。置1以响应可屏蔽中断,反之则禁止可屏蔽中断。
IOPL(bits 12 and 13) [I/O privilege level field] 指示当前运行任务的I/O特权级(I/O privilege level),正在运行任务的当前特权级(CPL)必须小于或等于I/O特权级才能允许访问I/O地址空间。这个域只能在CPL为0时才能通过POPF以及IRET指令修改。
NT(bit 14) [Nested task flag] 这个标志控制中断链和被调用任务。若当前任务与前一个执行任务相关则置1,反之则清零。
RF(bit 16) [Resume flag] 控制处理器对调试异常的响应。
VM(bit 17) [Virtual-8086 mode flag] 置1以允许虚拟8086模式,清除则返回保护模式。
AC(bit 18) [Alignment check flag] 该标志以及在CR0寄存器中的AM位置1时将允许内存引用的对齐检查,以上两个标志中至少有一个被清零则禁用对齐检查。
VIF(bit 19) [Virtual interrupt flag] 该标志是IF标志的虚拟镜像(Virtual image),与VIP标志结合起来使用。使用这个标志以及VIP标志,并设置CR4控制寄存器中的VME标志就可以允许虚拟模式扩展(virtual mode extensions)
VIP(bit 20) [Virtual interrupt pending flag] 该位置1以指示一个中断正在被挂起,当没有中断挂起时该位清零。与VIF标志结合使用。
ID(bit 21) [Identification flag] 程序能够设置或清除这个标志指示了处理器对CPUID指令的支持

内存管理控制寄存器

  • GDTR
    全局描述符表寄存器GDTR,48位寄存器。 用于存放全局描述符表GDT的32位的线性基地址和16位的表限长值。基地址指定GDT表中字节0在线性地址空间中的地址,表长度指明GDT表的字节长度值。指令LGDTSGDT分别用于加载和保存GDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。在保护模式初始化过程中必须给GDTR加载一个新值。

  • LDTR
    局部描述符表寄存器LDTR,16位寄存器。 其内保存的是16位的选择符。LDTR和GDTR有所不同,定义也有所不同,对于LDT来说,首先要根据16位的选择符从GDT中选择一个LDT描述符(换句话说,LDT的描述符放在GDT中),送入描述符高速缓冲寄存器(64位)中,以确定当前局部描述符LDT所在的位置的基地址和界限值。

  • IDTR
    中断描述符表寄存器IDTR,48位寄存器。 与GDTR的作用类似,IDTR寄存器用于存放中断描述符表IDT的32位线性基地址和16位表长度值。指令LIDT和SIDT分别用于加载和保存IDTR寄存器的内容。在机器刚加电或处理器复位后,基地址被默认地设置为0,而长度值被设置成0xFFFF。80486为每个中断或异常都定义了一个中断描述符

  • TR
    任务寄存器TR,16位寄存器。 其内保存的是任务状态段TSS的16位段选择符。每项任务都配有一个任务状态段TSS,用来描述该任务的运行状态。就用16位的选择符来检索任务状态段。总是将当前任务的TSS的选择符放在TR中,而TSS的描述符放在TSS描述符高速缓冲寄存器中

控制寄存器

控制寄存器CR0-CR3
1、CR0

0位是保护允许位PE(Protedted Enable),用于启动保护模式,如果PE位置1,则保护模式启动,如果PE=0,则在实模式下运行。

控制寄存器CR0中的位0用PE标记,位31用PG标记,这两个位控制分段和分页管理机制的操作

PE=0,处理器运行于实模式;PE=1,处理器运行于保护方式。PG控制分页管理机制。PG=0,禁用分页管理机制,此时分段管理机制产生的线性地址直接作为物理地址使用;PG=1,启用分页管理机制,此时线性地址经分页管理机制转换位物理地址。由此可知,如果要启用分页机制,那么PE和PG标志都要置位。

2、CR2

CR2用于发生页异常时报告出错信息。当发生页异常时,处理器把引起页异常的线性地址保存在CR2中。操作系统中的页异常处理程序可以检查CR2的内容,从而查出线性地址空间中的哪一页引起本次异常。

3、CR3

CR3 用于保存页目录表页面的物理地址,因此被称为PDBR。由于目录是页对齐的,所以仅高20位有效,低12 位保留供更加高级的处理器使用。向CR3中装入一个新值时,低12位必须为0;但从 CR3中取值时,低12位被忽略。每当用MOV指令重置CR3的值时,会导致分页机制高速缓冲区的内容无效,用此方法,可以在启用分页机制之前,即把PG 位置1之前,预先刷新分页机制的高速缓存。CR3寄存器即使在CR0寄存器的PG位或PE位为0时也可装入,如在实模式下也可设置CR3,以便进行分页机制的初始化。在任务切换时,CR3要被改变,但是如果新任务中CR3的值与原任务中CR3的值相同,那么处理器不刷新分页高速缓存,以便当任务共享页表时有较快的执行速度。

1.4系统指令

系统指令

系统指令: 系统指令用于处理系统级别功能,例如加载系统寄存器,管理中断等。大多数系统指令只能由处于特权级0的操作系统软件执行,其余一些指令可以在任何特权级上执行,因此应用程序也能使用,表4-2 中列出了我们将用到的一些系统指令,其中还指出了它们是否受到保护

操作码 指令 是否受保护 说明
0F 00 /0 SLDT r/m16 存储段选择子LDTR至r/m16
0F 00 /1 STR r/m16 存储段选择子TR至r/m16
0F 00 /2 LLDT r/m16 加载段选择子r/m16至LDTR
0F 00 /3 LTR r/m16 加载r/m16至TR
0F 01 /0 SGDT m 存储GDTR到m
0F 01 /1 SIDT m 存储IDTR到m
0F 01 /2 LGDT m16&32 将m加载到GDTR
0F 01 /2 LGDT m16&64 将m加载到GDTR
0F 01 /3 LIDT m16&32 将m加载到IDTR
0F 01 /3 LIDT m16&64 将m加载到IDTR