1.Intel处理器架构-实模式
BIOS加载引导程序开始执行时,程序位于实模式。
实模式下特点有:
1.1.实模式下的物理寻址
jmp 0x1000:0x0010
实模式下上述0x1000:0x0010对应的目标位置物理地址为0x1000 << 4 + 0x0010 = 0x10010。
1.1.1.实模式下物理地址位宽限制
其中形如0x1000:0x0010这样的寻址形式。
前一部分称为段地址,后一部分称为段内偏移。两个地址数值上必须限定在16位宽度。两部分合起来计算出的物理地址位宽限定在20位宽度。
这样实模式下
0xFFFF:0xFFFF = 0xFFFF << 4 + 0xFFFF = 0x10FFEF,这样的一个物理地址至少需要21个比特位来表达,就超过了20个比特位的限制。所以,0xFFFF:0xFFFF实际的物理地址为0xFFEF。超出20个比特位部分被舍弃掉了。
这样实模式下,可表达的最大物理地址为0xFFFFF。这样的一个物理地址可用0xF000:0xFFFF来得到。
1.2.寄存器和标号位数限制
es
cs
ds
...
上述实模式下寄存器位数是16位。
Label1:
xxxx
Label2 dd 0x00
上述标号代表标号所在位置距离所在段首部的偏移。在实模式下上述标号数值是16位宽度的。
1.3.实模式下中断处理
实模式下在物理地址[0, 1KB)范围内存储中断向量表。每个表项占据4B,共256项。
每一项前2字节存储offset,后2字节存储segment。
2.Intel处理器架构-保护模式
保护模式显著特点是重新设计了分段机制。
物理内存的一块区域可被定义为一个段,段通过段描述符来描述该段的属性信息。
特别的段描述符还可以用来描述调用门,陷阱门,中断门任务门以便实现特定的功能。
段描述符需要存放在表结构中。
存放段描述符的表结构有三种类型:
一种称为GDT,全局描述符表。
一种称为LDT,局部描述符表。
一种称为LDT,中断描述符表。
三种类型的表定位不同,分别用于存储符合其定位的段描述符。
2.1.GDT
对保护模式来说,必须提供一个GDT以便处理器正常开展工作。
GDT中存储的段,可以是用于描述一段代码区域的段,描述一段数据区域的段(包含描述栈区域的段)。特殊点的每个LDT表占据区域也构成一个段,这样的也须用段描述符来描述,且对应的描述符只能存储到GDT的表中。每个任务允许定义个称为TSS的区域,这个区域也构成一个段,这样的也须用段描述符来描述,且对应的描述符只能存储到GDT的表中。
2.1.1.8字节段描述符
GDT中存储的段描述符占据8个字节。即64个比特位。我们对这64个比特位按从低到高进行编号,从0开始编号。
编号[0,15]区域存储 段长度的[0,15]
编号[16, 31]区域存储 段基地址的[0,15]
编号[32, 39]区域存储 段基地址的[16,23]
编号[40,43]区域存储 Type类型字段
编号44区域存储 S标志(系统段标志。1:非系统段,0:系统段)
编号[45,46]区域存储 DPL标志(段的特权级)
编号47区域存储 P标志(段在内存标志。1:在,0:不在)
编号[48,51]区域存储 段长度的[16,19]
编号52区域存储 AVL标志(一般为0)
编号53区域存储 L标志(保留,0)
编号54区域存储 D/B标志
编号55区域存储 G标志(尺寸粒度,1:4KB,0:字节)
编号[56,63]区域存储 段基地址的[24,31]
D/B标志:
对可执行代码段。为1时,默认下,段内地址是32位的。操作数是32位或8位的。
对栈段。为1时,使用ESP作为栈指针。为0时,使用SP作为栈指针。
对数据段。为1时,上边界为0xFFFF FFFF。为0时,上边界为0xFFFF。
2.1.2.2字节段选择子
保护模式下,段寄存器内存储的2字节内容称为段选择子。16个比特位。我们对这16个比特位按从低到高进行编号,从0开始编号。
编号[0,1]区域存储 RPL(请求特权级)
编号2区域存储 TI(0,GDT;1,LDT)
编号[3, 15]区域存储 段选择子索引
2.1.3.代码段
S标志位为1,且编号43区域为1。此时段描述的类型称为代码段。
进一步,此时,
编号42区域称为C(一致性)标志位。为1时,称为一致性代码段。为0时,称为非一致性代码段。
对一致性代码段有以下机制:
当前程序(代码段)可执行或跳转到一个一致性代码段,即使该一致性代码段的特权级高于当前。且跳转后CPL不变。
对非一致性代码段,上述跳转到一个更高特权级代码段是不可行的。
编号41区域称为R(可读)标志位。表示这个段内容是否可被读取。代码段必然是可执行的,不可写的。但是否可读允许通过此位来配置。
编号40区域称为A(访问)标志位。当代码段被访问时,处理器设置对应段描述符的A标志位为1。
2.1.4.数据段
S标志位为1,且编号43区域为0。此时段描述的类型称为数据段。
进一步,此时,
编号42区域称为E(扩展方向)标志位。为1时,段向下扩展。为0时,段向上扩展。向上与向下扩展可以理解为从段基地址向段存储新数据时,新数据是存储到基地址之上,还是存储到基地址之下。对正常数据区域,是向上的。对栈,一般是向下的。
编号41区域称为W(可写)标志位。表示这个段内容是否可写。数据段必然是可可读的。但是否可写允许通过此位来配置。
编号40区域称为A(访问)标志位。当代码段被访问时,处理器设置对应段描述符的A标志位为1。
2.1.5.系统段
S标志位为0。此时段描述的类型称为系统段。
对于系统段,需要结合编号[40,43]区域4个比特位来进一步确定段的类型。
43 | 42 | 41 | 40 | 系统段类型 |
0 | 0 | 0 | 0 | 保留 |
0 | 0 | 0 | 1 | 16位TSS段(有效的) |
0 | 0 | 1 | 0 | LDT段 |
0 | 0 | 1 | 1 | 16位TSS段(使用中) |
0 | 1 | 0 | 0 | 16位调用门 |
0 | 1 | 0 | 1 | 任务门 |
0 | 1 | 1 | 0 | 16位中断门 |
0 | 1 | 1 | 1 | 16位陷进门 |
1 | 0 | 0 | 0 | 保留 |
1 | 0 | 0 | 1 | 32位TSS段(有效的) |
1 | 0 | 1 | 0 | 保留 |
1 | 0 | 1 | 1 | 32位TSS(使用中) |
1 | 1 | 0 | 0 | 32位调用门 |
1 | 1 | 0 | 1 | 保留 |
1 | 1 | 1 | 0 | 32位中断门 |
1 | 1 | 1 | 1 | 32位陷阱门 |
对于LDT段描述符,用于描述LDT区域。LDT区域作为一个表,D/B标志位对其无意义,一般设置为0。
对于TSS段描述符,用于描述TSS区域。
2.1.6.TSS区域
一个TSS区域的结构可以用如下表格来描述
偏移 | 尺寸 | 描述 |
0x00 | 2 | 上个任务的TSS选择子 |
0x02 | 2 | 保留 |
0x04 | 4 | ESP0 |
0x08 | 2 | SS0 |
0x0A | 2 | 保留 |
0x0C | 4 | ESP1 |
0x10 | 2 | SS1 |
0x12 | 2 | 保留 |
0x14 | 4 | ESP2 |
0x18 | 2 | SS2 |
0x1A | 2 | 保留 |
0x1C | 4 | CR3 |
0x20 | 4 | EIP |
0x24 | 4 | EFLAGS |
0x28 | 4 | EAX |
0x2C | 4 | ECX |
0x30 | 4 | EDX |
0x34 | 4 | EBX |
0x38 | 4 | ESP |
0x3C | 4 | EBP |
0x40 | 4 | ESI |
0x44 | 4 | EDI |
0x48 | 2 | ES |
0x4A | 2 | 保留 |
0x4C | 2 | CS |
0x4E | 2 | 保留 |
0x50 | 2 | SS |
0x52 | 2 | 保留 |
0x54 | 2 | DS |
0x56 | 2 | 保留 |
0x58 | 2 | FS |
0x5A | 2 | 保留 |
0x5C | 2 | GS |
0x5E | 2 | 保留 |
0x60 | 2 | LDT段选择子 |
0x62 | 2 | 保留 |
0x64 | 2 | 最低比特位是T标志,其余保留 |
0x66 | 2 | I/O位图基地址 |
上面表格中红色区域我们称为动态区域,绿色区域我们称为静态区域。
TSS结构需要在如下场景中发挥作用:
1.程序通过JMP/CALL提供的段选择子,定位到GDT中的TSS段描述符时。发生任务切换。
此时TR所关联的TSS区域中的动态区域用于存储当前程序执行现场。所谓执行现场就是由动态区域中各个对应字段构成的。
动态区域中"上个任务的TSS选择子"特殊处理,使用目标TSS区域中的"上个任务的TSS选择子"来记录上个程序的TSS选择子。
同时会使用目标TSS区域中的静态区域来设置新任务的CR3,LDT,根据新任务的特权级相应设置SS,ESP为目标TSS区域中静态区域内对应值。
文章来源地址https://uudwc.com/A/6znEE
2.程序通过JMP/CALL提供的段选择子,定位到GDT/LDT中的任务门描述符时。发生任务切换。
具体当前程序的TSS区域的动态区域,目标程序的TSS区域的静态区域如何起作用参考1。
3.触发中段或异常,且对应的处理表项是任务门描述符。发生任务切换。
具体当前程序的TSS区域的动态区域,目标程序的TSS区域的静态区域如何起作用参考1。
4.EFLAGS.NT设置为1时,执行IRET。发生任务切换。
具体当前程序的TSS区域的动态区域,目标程序的TSS区域的静态区域如何起作用参考1。
文章来源:https://uudwc.com/A/6znEE
综上所述,TSS机制为任务切换提供的一种支持机制。借助TSS机制实现的任务切换可以让处理器帮应用自动处理执行现场保留,新任务必要执行环境设置等工作。
2.1.7.如何告知处理器GDT的位置信息
处理器存在GDTR寄存器,用于存储GDT尺寸,GDT起始位置。
可使用 "LGDT [SrcAddr]"告知。
实模式下,默认操作数大小是16位。保护模式下,操作数大小需结合数据段的D/B标志位确定。IA-32e模式下默认64位。
2.2.LDT
LDT表也用于存储保护模式下的8字节段描述符。不过无法存储描述LDT区域的描述符,描述TSS区域的描述符。
2.2.1.如何告知处理器LDT的位置信息
处理器存在LDTR寄存器。用于存储描述LDT区域的段选择子。
使用 "LLDT LDT段的选择子" 告知。
2.3.如何从实模式切换到保护模式
1.系统进入保护模式前,需创建一个拥有代码段描述符,数据段描述符的GDT(首个表项需为NULL描述符)。然后通过LGDT告知处理器GDT表尺寸,位置。
2.LDT表的使用是可选的,如使用,则需准备LDT表。然后通过LIDT告知处理器LDT的段选择子。
3.保护模式需开启分页机制下,开启分页机制前,需要准备好页目录表,页表。并将页目录表的物理地址加载到CR3控制寄存器。进入保护模式后,再置位CR0.PG可开启分页机制。
4.进入保护模式前,需创建一个IDT。IDT可用于存储中断门描述符,陷阱门描述符,任务门描述符。然后通过LIDT告知处理器IDT表尺寸,位置。
5.进入保护模式后,如果需要使用基于TSS区域的任务切换机制,则需在执行任务切换前,创建对应的TSS区域,TSS段描述符。使用LTR汇编指令将TSS段选择子加载到TR寄存器。以便处理器知道当前任务的TSS区域。
如何进入保护模式?
通过MOV指令置位CR0.PE后即进入保护模式。进入保护模式后,将从特权级0开始执行。
一般推荐的实践方式为:
1.CLI禁止可屏蔽硬件中断
2.MOV 设置CR0.PE为1
3.远跳转
进入保护模式后,数据段寄存器仍保留着实模式的段数据,需重新加载数据段选择子或用JMP/CALL执行新任务来更新。
使用STI使能可屏蔽硬件中断前,需用LIDT告知处理器IDT表的基地址和长度。
2.4.寻址能力
实模式下,我们通过段寄存器,段内偏移,支持的寻址范围可用20个比特位来表示。
保护模式下,我们的段基础地址,段偏移都是32位的,相应的寻址范围也变为32位比特位。
但要让处理器允许我们寻址到超过20个比特位的地址空间,需要开启A20地址线才可以。
以下是一种开启A20地址线的方法:
in al, 92h
or al, 00000010b
out 92h, al
即从io端口0x92先读取1字节,将读取1字节的比特位1(从0开始编号)设置为1后,再写入0x92端口即可。
2.5.实模式下进行4GB寻址的方法
默认实模式下,通过16位段寄存器,16位段内偏移,支持的寻址范围是20个比特位。
通过以下方法,可在实模式下进行4GB寻址:
1.cli禁止中断
2.准备GDT,通过LGDT告知处理器GDT尺寸,位置
3.通过MOV置位CR0.PE进入保护模式
4.通过MOV为希望具备4GB寻址的段寄存器p设置为保护模式GDT中支持4GB寻址数据段描述符的段选择子。
5.通过MOV复位CR0.PE回到实模式
这样操作后,实模式下使用p作为段寄存器,计算p:off的线性地址时,不再采用
线性地址=p>>4+off的方式。而是采用
线性地址=p对应数据段的32位Base+off
值得注意的是,实模式下,搭配不具备p特征的段寄存器时,段内偏移只能使用16个比特位。搭配具备p特征的段寄存器时,段内偏移可以使用32个比特位。
mov byte [fs:edi], al
2.6.IA-32e模式及其下的兼容模式,64位模式。
IA-32e模式是Intel为64位处理器设计的新模式,也称长模式。包含兼容模式,64位模式两种子运行模式。
2.6.1.检测处理器是否支持IA-32e模式
CPUID.80000001h:EDX[29].LM
2.6.2.IA-32e下地址空间
线性地址位宽64位。有效地址是低48位用于线性地址寻址,高16位作为符号位扩展的地址。也称为Canonical地址。
相应的Canonical型地址区间0x0000 0000, 0000 0000~0x0000 7FFF, FFFF FFFF和0xFFFF 8000, 0000 0000~0xFFFF FFFF, FFFF FFFF是程序可用区域。
2.6.3.IA-32e下段机制
IA-32e下段机制继承自保护模式,又有较大改进。
存放段描述符的表结构有三种类型:
一种称为GDT,全局描述符表。
一种称为LDT,局部描述符表。
一种称为LDT,中断描述符表。
三种类型的表定位不同,分别用于存储符合其定位的段描述符。
2.6.4.8字节代码段,数据段描述符
IA-32e模式下,代码段,数据段的段基地址强制为0x00,段尺寸强制为无限制。
我们对这64个比特位按从低到高进行编号,从0开始编号。
编号[0,15]区域存储 段长度的[0,15]
编号[16, 31]区域存储 段基地址的[0,15]
编号[32, 39]区域存储 段基地址的[16,23]
编号[40,43]区域存储 Type类型字段
编号44区域存储 S标志(系统段标志。1:非系统段,0:系统段)
编号[45,46]区域存储 DPL标志(段的特权级)
编号47区域存储 P标志(段在内存标志。1:在,0:不在)
编号[48,51]区域存储 段长度的[16,19]
编号52区域存储 AVL标志(一般为0)
编号53区域存储 L标志
编号54区域存储 D/B标志
编号55区域存储 G标志(尺寸粒度,1:4KB,0:字节)
编号[56,63]区域存储 段基地址的[24,31]
上述是保护模式下8字节段描述符解析。
IA-32e模式下(IA32_EFER.LMA=1),依然对代码段,数据段依然采用8字节段描述符。依然可按上述去解析。但不同之处在于。
1.对代码段,L标志位为0时,处理器将运行在IA-32e的兼容子模式。此时完全按照和保护模式一致的方式去解释段描述符。
2.对代码段,L标志位为1时,处理器将运行在IA-32e的64位子模式。此时D/B标志位必须是0。此时代码段的默认操作数位宽是32位。地址位宽是64位。
3.对数据段,
2.6.5.16字节系统段描述符
IA-32e模式下,对系统段采用16字节段描述符。低8字节依然沿用保护模式下8字节段描述符格式。具体细节为:
1.IA-32e模式下,系统段描述符类型
43 | 42 | 41 | 40 | 功能 |
0 | 0 | 0 | 0 | 16B描述符的高8B |
0 | 0 | 0 | 1 | 保留 |
0 | 0 | 1 | 0 | LDT段描述符 |
0 | 0 | 1 | 1 | 保留 |
0 | 1 | 0 | 0 | 保留 |
0 | 1 | 0 | 1 | 保留 |
0 | 1 | 1 | 0 | 保留 |
0 | 1 | 1 | 1 | 保留 |
1 | 0 | 0 | 0 | 保留 |
1 | 0 | 0 | 1 | 64位TSS段描述符(有效的) |
1 | 0 | 1 | 0 | 保留 |
1 | 0 | 1 | 1 | 64位TSS段描述符(使用中) |
1 | 1 | 0 | 0 | 64位调用门描述符 |
1 | 1 | 0 | 1 | 保留 |
1 | 1 | 1 | 0 | 64位中断门描述符 |
1 | 1 | 1 | 1 | 64位陷阱门描述符 |
2.对LDT段描述符,TSS段描述符
多出来的高8字节,最高4字节保留,低4字节存储段基地址63:32。
低8字节按保护模式下8字节解释。Type字段以1中为准。
对于TSS段所描述的TSS区域,使用场景发生了较大改变。由于IA-32e模式不支持任务门机制。相应的,IA-32e模式下,TSS区域无需用来提供对任务切换时现场保存与恢复的支持。只需负责为不同特权级间的栈切换提供支持即可。
相应的TSS区域结构变为如下
偏移 | 尺寸 | 描述 |
0x0 | 4 | 保留 |
0x4 | 4 | RSP0的低32位 |
0x8 | 4 | RSP0的高32位 |
0xC | 4 | RSP1的低32位 |
0x10 | 4 | RSP1的高32位 |
0x14 | 4 | RSP2的低32位 |
0x18 | 4 | RSP2的高32位 |
0x1C | 4 | 保留 |
0x20 | 4 | 保留 |
0x24 | 4 | IST1低32位 |
0x28 | 4 | IST1高32位 |
0x2C | 4 | IST2低32位 |
0x30 | 4 | IST2高32位 |
0x34 | 4 | IST3低32位 |
0x38 | 4 | IST3高32位 |
0x3C | 4 | IST4低32位 |
0x40 | 4 | IST4高32位 |
0x44 | 4 | IST5低32位 |
0x48 | 4 | IST5高32位 |
0x4C | 4 | IST6低32位 |
0x50 | 4 | IST6高32位 |
0x54 | 4 | IST7低32位 |
0x58 | 4 | IST7高32位 |
0x5C | 4 | 保留 |
0x60 | 4 | 保留 |
0x64 | 2 | 保留 |
0x66 | 2 | I/O位图基地址 |
IA-32e下,针对特权级0,1,2的SS段寄存器,支持加载NULL段选择子(GDT的0项)。
IA-32e下,发生特权级切换时,新的SS段寄存器强制加载一个NULL段选择子。RSP将根据特权级被赋值为RSPn(n=0~2)。旧的SS,RSP将被保存到新栈中。
2.6.6.IA-32e模式下的调用门描述符
IA-32e下调用门描述符从原来的8B扩充到16B.
我们对这128个比特位按从低到高进行编号,从0开始编号。
编号[0,15]区域存储 段内偏移的[0,15]
编号[16, 31]区域存储 段选择子
编号[32, 39]区域存储 0
编号[40,43]区域存储 Type类型字段(1100)
编号44区域存储 S标志(系统段标志。1:非系统段,0:系统段)
编号[45,46]区域存储 DPL标志(段的特权级)
编号47区域存储 P标志(段在内存标志。1:在,0:不在)
编号[48,63]区域存储 段内偏移[16, 31]
编号[64, 95]区域存储 段内偏移[32, 63]
编号[96, 127]区域存储 保留
处理器在执行IA-32e模式的调用门时,将以8B的数据位宽向栈中压入数据。
RETF指令的默认操作数是32位,若要返回到64位程序,需在RETF前额外加上前缀0x48。
2.6.7.IA-32e模式下的中断和异常处理
IA-32e模式的中断/异常处理机制与保护模式的异常处理机制相似。
不过中断发生时,栈空间的保存(SS, RSP)由选择性保存(特权级CPL变化时),改为无条件保存。
IA-32e模式还引入了一种全新的中断栈切换机制。
IA-32e模式的系统段描述符不再支持任务门描述符,只剩下陷阱门描述符,中断门描述符。
我们对这128个比特位按从低到高进行编号,从0开始编号。
编号[0,15]区域存储 段内偏移的[0,15]
编号[16, 31]区域存储 段选择子[0,15]
编号[32, 34]区域存储 IST
编号[35]区域存储 0
编号36区域存储 S标志(系统段标志。1:非系统段,0:系统段)
编号[37,39]区域存储 0
编号[40, 43]区域存储 Type
编号[44]区域存储 0
编号[45, 46]区域存储 DPL
编号47区域存储 P
编号[48, 63]区域存储 段内偏移的[16, 31]
编号[64, 95]区域存储 段内偏移的[32, 63]
编号[96,127]区域存储 保留
在IDT的任意一个门描述符都可使用IST机制或原有栈切换机制。当IST=0时,使用原有栈切换机制。否则,使用IST机制,此时处理器无条件进行栈切换。
使用IST机制下,
处理器强制将SS赋值为NULL段选择子,并将中断栈地址加载到RSP寄存器中。
最后,将原SS,RSP,RFLAGS,CS和RIP寄存器值压入新栈中。
2.7.IA32e下分页机制
IA-32e模式需伴随页管理机制的开启(置位CR0.PG,CR4.PAE,IA32_EFER.LME)。
IA-32e模式的页管理机制可将Canonical型的线性地址映射到52位物理地址空间(由处理器最高物理可寻址位宽值MAXPHYADDR决定)。
处理器通过CR3保存的物理地址,可将线性地址转换成一个多层级页表结构,IA-32e模式的页管理机制共支持4KB,2MB,1GB(CPUID.80000001h:EDX[26].1G-Page位可检测是否支持1GB物理页)三种规格的物理页容量。
IA-32e模式的页管理机制使用多层级页表来结构化线性地址空间,CR3负责定位顶层页表PML4的物理基地址。
PCIDs功能支持检测:
CPUID.01h:ECX[17].PCID
支持下,置位CR4.PCIDE标志位时开启PCID功能。
开启PCID功能前提下,使用MOV指令操作CR3的第63位,将会影响TLB的有效性(全局页除外)。
1.CR3寄存器解析
处理器使能PCIDs功能时:
编号[0,11]区域存储 PCID
编号[12, M-1]区域存储 PML4页表的物理基地址
编号[M, 63]区域存储 保留
处理器使能PCIDs功能时:
编号[0,2]区域存储 保留
编号[3]区域存储 PWT标志位
编号[4]区域存储 PCD标志位
编号[5, 11]区域存储 保留
编号[12, M-1]区域存储 PML4页表的物理基地址
编号[M, 63]区域存储 保留
2.对于PML4页表(容量4KB)的页表项的解析
若页表项是无效的
编号[0]区域 0
编号[1, 63]区域 保留
若页表项是有效的
编号[0]区域 1
编号[1]区域 R/W
编号[2]区域 U/S
编号[3]区域 PWT
编号[4]区域 PCD
编号[5]区域 A
编号[6, 11]区域 保留
编号[12, M-1]区域 PDPT页表的物理基地址
编号[M, 62]区域 保留
编号[63]区域 XD标志位
3.对于PDPT页表(容量4KB)的页表项的解析
若页表项是无效的
编号[0]区域 0
编号[1, 63]区域 保存
若物理页是1GB的
编号[0]区域 1
编号[1]区域 R/W
编号[2]区域 U/S
编号[3]区域 PWT
编号[4]区域 PCD
编号[5]区域 A
编号[6]区域 D
编号[7]区域 1(可作为1GB物理页的依据)
编号[8]区域 G
编号[9, 11]区域 保留
编号[12]区域 PAT
编号[13, 29]区域 保留
编号[30, M-1]区域 1GB物理页的基地址
编号[M, 62]区域 保留
编号[63]区域 XD标志位
若物理页不是1G的
编号[0]区域 1
编号[1]区域 R/W
编号[2]区域 U/S
编号[3]区域 PWT
编号[4]区域 PCD
编号[5]区域 A
编号[6]区域 保留
编号[7]区域 0(可作为非1GB物理页的依据)
编号[8, 11]区域 保留
编号[12, M-1]区域 页目录表的物理基地址
编号[M, 62]区域 保留
编号[63]区域 XD标志位
4.对PDT表(容量4KB)的页表项的解析
若页表项是无效的
编号[0]区域 0
编号[1, 63]区域 保存
若物理页是2MB的
编号[0]区域 1
编号[1]区域 R/W
编号[2]区域 U/S
编号[3]区域 PWT
编号[4]区域 PCD
编号[5]区域 A
编号[6]区域 D
编号[7]区域 1(可作为2MB物理页的依据)
编号[8]区域 G
编号[9, 11]区域 保留
编号[12]区域 PAT
编号[13, 20]区域 保留
编号[31, M-1]区域 2MB物理页的基地址
编号[M, 62]区域 保留
编号[63]区域 XD标志位
若物理页不是2MB的
编号[0]区域 1
编号[1]区域 R/W
编号[2]区域 U/S
编号[3]区域 PWT
编号[4]区域 PCD
编号[5]区域 A
编号[6]区域 保留
编号[7]区域 0(可作为非2MB物理页的依据,那只能是4KB物理页了)
编号[8, 11]区域 保留
编号[12, M-1]区域 页目录表的物理基地址
编号[M, 62]区域 保留
编号[63]区域 XD标志位
5.对PT表(容量4KB)的页表项的解析
若页表项是无效的
编号[0]区域 0
编号[1, 63]区域 保存
此时物理页只能是4KB的
编号[0]区域 1
编号[1]区域 R/W
编号[2]区域 U/S
编号[3]区域 PWT
编号[4]区域 PCD
编号[5]区域 A
编号[6]区域 D
编号[7]区域 PAT
编号[8]区域 G
编号[9, 11]区域 保留
编号[12, M-1]区域 4KB物理页的物理基地址
编号[M, 62]区域 保留
编号[63]区域 XD标志位
6.对1~5中涉及的相关标志位的解释
P 物理页存在标志位
W 物理页写权限标志位(读权限始终有)
U/S 访问模式标志位。0时,特权级3程序不可访问。1时,无限制。
PWT 0,回写。1,写穿。
PCD 0,页可以缓存。1,页不能缓存。
A 访问标志位。0,未访问。1,已访问。
D 脏页标志位。0,干净。1,脏页。
PAT 页面属性标志位。
G 全局页标志位。0,局部页。1,全局页。
PS 物理页容量标志位。
CR0.CD为1时,PWT和PCD被忽略。
7.如何翻译64位线性地址
对4KB物理页:
64位线性地址构成=16位符号扩展位+9位PML4表索引+9位PDPT表索引+9位PDT表索引+9位PT表索引+12位段内偏移。
对2MB物理页:
64位线性地址构成=16位符号扩展位+9位PML4表索引+9位PDPT表索引+9位PDT表索引+21位段内偏移。
对1GB物理页:
64位线性地址构成=16位符号扩展位+9位PML4表索引+9位PDPT表索引+30位段内偏移。
2.8.如何从保护模式切换到IA-32e模式
2.8.1.检测处理器是否支持IA-32e模式
call support_long_mode
test eax, eax
jz no_support
;======= test support long mode or not
support_long_mode:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
setnb al
jb support_long_mode_done
mov eax, 0x80000001
cpuid
bt edx, 29
setc al
support_long_mode_done:
movzx eax, al
ret
;======= no support
no_support:
jmp $
对上述代码的解释:
1.通过0x80000000执行cpuid,来查询cpuid支持的最大功能号
2.向cpuid查询是否支持长模式(IA-32e)依赖0x80000001。所以,
2.1.cpuid执行结果(存储在eax)大于等于0x80000001,则设置al为1。
2.1.cpuid执行结果小于0x80000001,用al设置eax并返回。eax将被设置为0。
3.接下来,必然cpuid大于等于0x80000001
3.1.通过0x80000001执行cpuid。结果存储在edx。edx第29比特位为1,则支持长模式。为0,则不支持。
3.2.测试edx第29比特位来设置al,并用al设置eax,返回。
4.测试eax即可知道是否支持长模式。eax为0时,不支持。不为0时,支持。
2.8.2.从保护模式切换到IA-32e模式
处理器只能在开启分页机制的保护模式下才能切换到IA-32e模式。
1.IA-32e模式激活后,GDTR,LDTR,IDTR,TR依然沿用保护模式的描述符表。IA-32e中,上述描述符表寄存器应重新加载为IA-32e模式的64位描述符表。
2.IA-32e模式激活后,在将IDTR更新为IA-32e模式下描述符表前,需用CLI禁止可屏蔽硬件中断。对NMI不可屏蔽中断,需借助外部硬件电路来禁止。
激活IA-32e:
IA32_EFER.LME。
激活步骤:
1.保护模式下,用MOV CR0复位CR0.PG标志位来关闭分页机制。
2.置位CR4.PAE,开启物理地址扩展。
3.将页目录(顶层页表PML4)的物理基地址加载到CR3
4.置位IA32_EFER.LME开启IA-32e模式
5.置位CR0.PG开启分页机制。此时处理器会自动置位IA32_EFER.LMA。
注意,开启分页机制,关闭分页机制前后对附近代码的线性地址的解释是不一样的。但应控制前后线性地址在开启,关闭前后,解释到相同的物理地址。