长模式
概述
- 长模式又名 AMD64,因为这个标准是 AMD 公司最早定义的
- 它使 CPU 在现有的基础上有了 64 位的处理能力,既能完成 64 位的数据运算,也能寻址 64 位的地址空间
- 这在大型计算机上犹为重要,因为它们的物理内存通常有几百 GB
长模式寄存器
-
长模式相比保护模式,增加了一些通用寄存器,并扩展寄存器的位宽,所有的通用寄存器都是64位,还可以单独使用32位
-
这个低32位可以拆分成一个低16位寄存器,低16位又可以拆分成两个8位寄存器
长模式段描述符
-
长模式依然具备保护模式绝大多数特性,如特权级和权限检查
-
在长模式下,CPU 不再对段基址和段长度进行检查,只对 DPL 进行相关的检查,这个检查流程和保护模式下一样
-
当描述符中的 L=1,D/B=0 时,就是 64 位代码段,DPL 还是 0~3 的特权级
-
然后有多个段描述在内存中形成一个全局段描述符表,同样由 CPU 的 GDTR 寄存器指向
-
例子 - 长模式下的段描述符表
ex64_GDT: null_dsc: dq 0 ;第一个段描述符CPU硬件规定必须为0 c64_dsc:dq 0x0020980000000000 ;64位代码段 ;无效位填0 ;D/B=0,L=1,AVL=0 ;P=1,DPL=0,S=1 ;T=1,C=0,R=0,A=0 d64_dsc:dq 0x0000920000000000 ;64位数据段 ;无效位填0 ;P=1,DPL=0,S=1 ;T=0,C/E=0,R/W=1,A=0 eGdtLen equ $ - null_dsc ;GDT长度 eGdtPtr:dw eGdtLen - 1 ;GDT界限 dq ex64_GDT
- 上面代码中注释已经很清楚了,段长度和段基址都是无效的填充为 0,CPU 不做检查
- 但是上面段描述符的 DPL=0,这说明需要最高权限即 CPL=0 才能访问
- 若是数据段的话,G、D/B、L 位都是无效的
长模式中断
概述
- 保护模式下为了实现对中断进行权限检查,实现了中断门描述符,在中断门描述符中存放了对应的段选择子和其段内偏移,还有 DPL 权限
- 如果权限检查通过,则用对应的段选择子和其段内偏移装载 CS:EIP 寄存器
中断门描述符扩展
-
中断门描述符,就会发现其中的段内偏移只有 32 位
-
但是长模式支持 64 位内存寻址,所以要对中断门描述符进行修改和扩展
-
结合上图,我们可以看出长模式下
中断门描述符的格式变化
- 首先为了支持 64 位寻址中断门描述符在原有基础上增加 8 字节,用于存放目标段偏移的高 32 位值
- 其次,目标代码段选择子对应的代码段描述符必须是 64 位的代码段
- 最后其中的 IST 是 64 位 TSS 中的 IST 指针
-
长模式也同样在内存中有一个中断门描述符表,只不过表中的条目(如上图所示)是 16 字节大小
-
最多支持 256 个中断源,对中断的响应和相关权限的检查和保护模式一样
切换到长模式
概述
从实模式直接切换到长模式,也可以从保护模式切换长模式
切换模式
第一步,准备长模式全局段描述符表
ex64_GDT:
null_dsc: dq 0
;第一个段描述符CPU硬件规定必须为0
c64_dsc:dq 0x0020980000000000 ;64位代码段
d64_dsc:dq 0x0000920000000000 ;64位数据段
eGdtLen equ $ - null_dsc ;GDT长度
eGdtPtr:dw eGdtLen - 1 ;GDT界限
dq ex64_GDT
第二步,准备长模式下的 MMU 页表
- 这个是为了开启分页模式,切换到长模式必须要开启分页,想想看,长模式下已经不对段基址和段长度进行检查了,那么内存地址空间就得不到保护了
- 而长模式下内存地址空间的保护交给了 MMU,MMU 依赖页表对地址进行转换,页表有特定的格式存放在内存中,其地址由 CPU 的 CR3 寄存器指向
mov eax, cr4
bts eax, 5 ;CR4.PAE = 1
mov cr4, eax ;开启 PAE
mov eax, PAGE_TLB_BADR ;页表物理地址
mov cr3, eax
第三步,加载 GDTR 寄存器,使之指向全局段描述表
lgdt [eGdtPtr]
第四步, 开启长模式,要同时开启保护模式和分页模式
- 实现长模式时定义了 MSR 寄存器,需要用专用的指令 rdmsr、wrmsr 进行读写
- IA32_EFER 寄存器的地址为 0xC0000080,它的第 8 位决定了是否开启长模式
;开启 64位长模式
mov ecx, IA32_EFER
rdmsr
bts eax, 8 ;IA32_EFER.LME =1
wrmsr
;开启 保护模式和分页模式
mov eax, cr0
bts eax, 0 ;CR0.PE =1
bts eax, 31
mov cr0, eax
第五步,进行跳转,加载 CS 段寄存器,刷新其影子寄存器
jmp 08:entry64 ;entry64为程序标号即64位偏移地址
最后
- 切换到长模式和切换保护模式的流程差不多,只是需要准备的段描述符有所区别,还有就是要注意同时开启保护模式和分页模式