x86系统架构概述
目录
[TOC]
系统级体系架构概述
Global and Local Descriptor Tables(全局和局部描述符表)
当在保护模式下操作时,所有的内存访问都要经过全局描述符表(GDT)或可选的本地描述符表(LDT),如图2-1所示。这些表包含的条目描述符称为段 。段描述符提供了段的基本地址以及访问权限、类型和使用信息。
每个段描述符都有一个相关的段选择器。一个段选择器为使用它的软件提供了 一个GDT或LDT的索引(其相关段描述符的偏移量),一个全局/本地标志(决定选择器是否指向GDT或LDT),以及访问权限信息。
要访问段中的一个字节,必须提供一个段选择器和一个偏移量。段选择器提供访问该段的段描述符(在GDT或LDT中)。从段描述符中,处理器获得该段在线性地址空间中的基本地址。然后,偏移量提供了字节相对于基址的位置。这种机制可以用来访问任何有效的代码、数据或堆栈段。只要该段可以从处理器所处的当前权限级别(CPL)访问。CPL被定义为当前执行的代码段的保护级别。
见图2-1。图中的实心箭头表示一个线性地址,虚线表示一个段选择器。而点状箭头表示物理地址。为了简单起见,许多段选择器被显示为 直接指向一个段。然而,从段选择器到其相关段的实际路径总是通过GDT或LDT。GDT的基址的线性地址包含在GDT寄存器(GDTR)中;LDT的线性地址包含在LDT寄存器(LDTR)中。
Global and Local Descriptor Tables in IA-32e Mode
GDTR 和 LDTR 寄存器在 IA-32e 子模式(64 位模式和兼容模式)中都被扩展到 64 位宽。全局和局部描述符表在64位模式下被扩展以支持64位基地址,(16字节的LDT 描述符持有一个64位的基本地址和各种属性)。在兼容模式下,描述符不被扩展 。
System Segments, Segment Descriptors, and Gates(系统段,段描述符和门)
除了构成程序或过程执行环境的代码、数据和堆栈段之外,架构还定义了两个系统段:任务状态段(TSS)和LDT。GDT不被视为。
因为它不是通过段选择器和段描述符访问的。TSSs和LDTs有为它们定义了段描述符。该体系结构还定义了一组特殊的描述符,称为门[调用门(call gates),中断门(interrupt gates),陷阱门(trap gates),和 任务门(task gates)]。这些描述符为系统程序和处理程序提供了受保护的通道,这些程序和处理程序可能在与应用程序不同的权限级别上运行。例如,一个调用门的CALL可以提供访问一个代码段中的程序,该代码段与当前代码段处于相同或更低的权限级别(更多权限)。
为了通过调用门访问一个过程,调用过程提供了调用门的选择器。然后,处理器对调用门进行访问权限检查,将CPL与调用门和调用门所指向的目标代码段的权限级别进行比较。如果对目标代码段的访问是允许的,处理器就会得到目标代码段的段选择器和该代码段的偏移。如果调用需要改变权限级别,处理器也会切换到目标权限级别的堆栈。新堆栈的段选择器是从当前运行任务的TSS中获得的。门也促进了16位和32位代码段之间的转换,反之亦然。
Gates in IA-32e Mode
在IA-32e模式下,以下描述符是16字节的描述符(扩大到允许64位基数)。LDT描述符、64位TSS、调用门、中断门和陷阱门。调用门促进了64位模式和兼容模式之间的转换。在IA32e模式下不支持任务门。在权限级别改变时,堆栈段选择器不从TSS中读取。相反,它们被设置为NULL。
Task-State Segments and Task Gates(任务状态段任务门)
TSS(见图2-1)定义了一个任务的执行环境的状态。它包括通用寄存器、段寄存器、EFLAGS寄存器、EIP寄存器和段选择器的状态-指向三个堆栈段(每个权限级别有一个堆栈)。TSS还包括段选择器 ,用于与任务相关的LDT和分页结构层次的基址。
所有受保护模式下的程序执行都发生在一个任务(称为当前任务)的上下文中。
当前任务的TSS的段选择器被存储在任务寄存器中。最简单的切换方法是调用或跳转到一个新的任务。这里,新任务的TSS的段选择器是在CALL或JMP指令中给出的。在切换任务时,处理器会执行以下动作:
1 | 1. 将当前任务的状态存储在当前TSS中。 |
Task-State Segments in IA-32e Mode
在IA-32e模式下不支持硬件任务开关。然而,TSSs继续存在。TSS的基本地址由其描述符指定。一个64位的TSS持有以下对64位操作很重要的信息:
1 | - 每个特权级别的堆栈指针地址 |
Interrupt and Exception Handling(中断和异常处理)
外部中断、软件中断和异常是通过中断描述符表(IDT)处理的。IDT存储了一个门描述符的集合,提供对中断和异常处理程序的访问。与 GDT一样,IDT不是一个段。IDT基础的线性地址包含在IDT寄存器(IDTR)中。IDT中的门描述符可以是中断、陷阱、或任务门描述符。要访问一个中断或异常处理程序 ,处理器首先从内部硬件、外部中断控制器或软件中接收一个中断向量(中断号)。中断控制器,或通过INT、INTO、INT 3或BOUND指令从软件接收一个中断向量(中断号)。中断向量 提供了一个进入IDT的索引。如果选择的门描述符是一个中断门或陷阱门,相关的处理程序就会被访问。处理程序的访问方式与通过调用门调用程序的方式类似。如果描述符是一个
任务门,处理程序将通过一个任务开关被访问。
Interrupt and Exception Handling IA-32e Mode
在IA-32e模式下,中断描述符被扩展到16个字节,以支持64位基本地址。IDTR寄存器被扩展为容纳64位基地址。不支持任务门。
Memory Management(内存管理)
系统架构支持内存的直接物理寻址或虚拟内存(通过分页)。
当使用物理寻址时,线性地址被当作物理地址处理。当使用分页时:所有的代码、数据、堆栈和系统段(包括GDT和IDT)可以被分页,只有最近访问的 页被保存在物理内存中。物理内存中的页面(有时称为页框)的位置包含在分页结构中。这些结构位于物理内存中。
分页结构层次结构的基本物理地址包含在控制寄存器CR3中。分页结构中的条目决定了 一个分页框的物理地址、访问权限和内存管理信息。为了使用这种分页机制,一个线性地址被分解成几个部分。这些部分提供了进入分页结构和页框的单独偏移。一个系统可以有一个单一的分页结构层次,也可以有几个。例如 ,每个任务可以有自己的层次结构。
Memory Management in IA-32e Mode
在IA-32e模式下,物理内存页由一组系统数据结构管理。在兼容模式 和64位模式下,使用四级系统数据结构。这些结构包括 :
1 | - 第4级页面映射(PML4)--PML4表中的一个条目包含了一个页面的基点的物理地址目录指针表、访问权限和内存管理信息。PML4的基本物理地址被存储在CR3中。 |
System Registers(系统寄存器)
为了帮助初始化处理器和控制系统操作,系统结构在EFLAGS寄存器中提供了系统标志和几个系统寄存器:
1 | - EFLAGS寄存器中的系统标志和IOPL字段控制任务和模式的切换,中断处理,指令跟踪和访问权限。 |
System Registers in IA-32e Mode
在IA-32e模式下,四个系统描述符表寄存器(GDTR、IDTR、LDTR和TR)在硬件上被扩展为
以容纳64位的基本地址。EFLAGS成为64位的RFLAGS寄存器。CR0-CR4被扩展到64位。CR8变得可用。CR8提供了对任务优先级寄存器(TPR)的读写访问,这样操作系统就可以控制外部设备的优先级。在64位模式下,调试寄存器DR0-DR7为64位。在兼容模式下,DR0-DR3的地址匹配也是以64位粒度进行的。在支持IA-32e模式的系统上,扩展功能启用寄存器(IA32_EFER)是可用的。这个特定型号的寄存器控制IA-32e模式的激活和其他IA-32e模式的操作。此外,还有几个特定型号的寄存器管理 IA-32e 模式指令。
1 | - IA32_KernelGSbase - 由 SWAPGS 指令使用。 |
Other System Resources
除了前几节描述的系统寄存器和数据结构,系统结构还提供了以下的额外资源。
1 | - 操作系统指令 |
实模式和保护模式转换
二者根本区别为:进程内存受保护与否
保护模式 - 这是处理器的原生操作模式。它提供了一套丰富的结构特性、灵活性、高性能和对现有软件基础的向后兼容性。
真实地址模式 - 这种操作模式提供了英特尔8086处理器的编程环境,并有一些扩展(如切换到受保护或系统管理模式的能力)。
实模式工作原理
实模式出现于早期8088CPU时期。当时由于CPU的性能有限,一共只有20位地址线(所以地址空间只有1MB),以及8个16位的通用寄存器,以及4个16位的段寄存器。所以为了能够通过这些16位的寄存器去构成20位的主存地址,必须采取一种特殊的方式。当某个指令想要访问某个内存地址时,它通常需要用下面的这种格式来表示:
1 | (段基址:段偏移量) |
其中第一个字段是段基址,它的值是由段寄存器提供的。
第二字段是段内偏移量,代表你要访问的这个内存地址距离这个段基址的偏移。它的值就是由通用寄存器来提供的,所以也是16位。
CPU采用把段寄存器所提供的段基址先向左移4位。这样就变成了一个20位的值,然后再与段偏移量相加,即可组合成一个二十位的地址。即:
1 | 物理地址 = 段基址<<4 + 段内偏移 |
保护模式工作原理
随着CPU的发展,CPU的地址线的个数也从原来的20根变为现在的32根,所以可以访问的内存空间也从1MB变为现在4GB,寄存器的位数也变为32位。所以实模式下的内存地址计算方式就已经不再适合了。所以就引入了现在的保护模式,实现更大空间的,更灵活也更安全的内存访问。
在保护模式下,CPU的32条地址线全部有效,可寻址高达4G字节的物理地址空间; 但是我们的内存寻址方式还是得兼容老办法,即(段基址:段偏移量)的表示方式。当然此时CPU中的通用寄存器都要换成32位寄存器(除了段寄存器)来保证寄存器能访问所有的4GB空间。
我们的偏移值和实模式下是一样的,就是变成了32位而已,而段值仍旧是存放在原来16位的段寄存器中,但是这些段寄存器存放的却不再是段基址了,毕竟之前说过实模式下寻址方式不安全,我们在保护模式下需要加一些限制,而这些限制可不是一个寄存器能够容纳的,于是我们把这些关于内存段的限制信息放在一个叫做全局描述符表(GDT)的结构里。全局描述符表中含有一个个表项,每一个表项称为段描述符。而段寄存器在保护模式下存放的便是相当于一个数组索引的东西,通过这个索引,可以找到对应的表项。段描述符存放了段基址、段界限、内存段类型属性(比如是数据段还是代码段,注意一个段描述符只能用来定义一个内存段)等许多属性,具体信息见下图:
其中,段界限表示段边界的扩张最值,即最大扩展多少或最小扩展多少,用20位来表示,它的单位可以是字节,也可以是4KB,这是由G位决定的(G为1时表示单位为4KB)。
实际段界限边界值=(描述符中的段界限+1)*(段界限的单位大小(即字节或4KB))-1,如果偏移地址超过了段界限,CPU会抛出异常。
全局描述符表位于内存中,需要用专门的寄存器指向它后, CPU 才知道它在哪里。这个专门的寄存器便是GDTR(一个48位的寄存器),专门用来存储 GDT 的内存地址及大小。
还需要介绍一个新的概念:段的选择子。段寄存器 CS、 DS、 ES、 FS、 GS、 SS,在实模式下时,段中存储的是段基地址,即内存段的起始地址。 而在保护模式下时,由于段基址已经存入了段描述符中,所以段寄存器中再存放段基址是没有意义的,在段寄存器中存入的是一个叫作选择子的东西。选择子“基本上”是个索引值。由于段寄存器是 16 位,所以选择子也是 16 位,在其低 2 位即第 0~1 位, 用来存储 RPL,即请求特权级,可以表示 0、 1、 2、 3 四种特权级。在选择子的第 2 位是 TI 位,即 Table Indicator,用来指示选择子是在 GDT 中,还是 LDT 中索引描述符。 TI 为 0 表示在 GDT 中索引描述符, TI 为 1 表示在 LDT 中索引描述符。选择子的高 13 位,即第 3~15 位是 描述符的索引值,用此值在 GDT 中索引描述符。前面说过 GDT 相当于一个描述符数组,所以此选择子中的索引值就是 GDT 中的下标。选择子结构如下:
此外, 扩充的存储器分段管理机制和可选的存储器分页管理机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持; 支持多任务,能够快速地进行任务切换(switch)和保护任务环境(context); 4个特权级和完善的特权检查机制,既能实现资源共享又能保证代码和数据的安全和保密及任务的隔离; 支持虚拟8086方式,便于执行8086程序。
实模式到保护模式的切换
从实模式切换到保护模式大致可以分为以下几个步骤:
1 | 1、屏蔽中断 |
屏蔽中断
在16位实模式下的中断由BIOS处理,进入保护模式后,中断将交给中断描述符表IDT里规定的函数处理,在刚进入保护模式时IDTR寄存器的初始值为0,一旦发生中断(例如BIOS的时钟中断)就将导致CPU发生异常,所以需要首先屏蔽中断。屏蔽中断可以使用cli指令:
1 | cli |
初始化GDT
在32位保护模式中,段与段之间是互相隔离的,当访问的地址超出段的界限时处理器就会阻止这种访问,因此每个段都需要有起始地址、范围、访问权限以及其他属性四个部分,这四个部分合在一起叫做段描述符(Segment Descriptor),总共需要8个字节来描述。但Intel为了保持向后兼容,将段寄存器仍然规定为16-bit,显然我们无法用16-bit的段寄存器来直接存储64-bit的段描述符。
解决的办法是将所有64-bit的段描述符放到一个数组中,将16-bit段寄存器的值作为下标来访问这个数组(以字节为单位),获取64-bit的段描述符,这个数组就叫是全局描述符表
将CR0最低位置1
CR0是系统内的32位控制寄存器之一,可以控制CPU的一些重要特性。其中最低位是保护允许位(Protected Mode Enable, PE),PE位置1后CPU进入保护模式(注意此时还是16位保护模式,不是32位保护模式),置0时则为实模式。现在我们要进入保护模式,即将CR0的最低位置1,汇编代码如下:
1 | -把 cr0 的最低位置为 1,开启保护模式 |
执行远跳转
将cr0最低位置1后,CPU就进入了保护模式,此时需要马上执行一条远跳转指令:
1 | jmp 08h:PModeMain |
这条指令有两个作用,第一个作用是将cs段寄存器的值修改为08h,切换到保护模式后,CPU寻址的方式就从实模式中的段地址 * 16 + 偏移地址改为了通过gdt寻址,所以这里的08h是段选择子而不是段地址,并且远跳转指令会自动将cs的值修改为对应的段选择子,这里是08h。
远跳转的另一个作用是清空CPU的流水线,流水线的作用在计组中有提到过,为了加速指令的执行,CPU在执行当前指令时会同时加载并解析接下来的一些指令,在进入保护模式之前,已经有许多指令进入了流水线,这些指令都是按16位模式处理的,而进入保护模式后的指令都是32位,所以这里通过一个远跳转来让CPU清空流水线。
切换到32位模式后,就应该执行32位的指令了,所以从PModeMain开始的指令都采用32位模式编译,通过[bits 32]这个标记实现:
1 | [bits 32] |
初始化段寄存器和栈指针
上一步中我们将代码段寄存器cs初始化成了0x08,现在我们还需要初始化其他的段寄存器如数据段寄存器ds,拓展段寄存器es,栈段ss以及fs,gs两个由操作系统使用的段。
另外我们还需要初始化栈指针ebp和esp,代码如下:
1 | [bits 32] |
需要修改的内容
GDT初始化:定义段描述符、定义GDTR的数据结构、定义GDT选择子
数据段+堆栈段
16位代码段(实模式下)的定义
1
2
3
4
5
6
71.设置代码运行环境,即给相关寄存器赋值;
2.初始化16位代码段描述符 + 32位代码段描述符 + 堆栈段描述符 +数据段描述符;
3.初始化全局描述符表寄存器GDTR的内容,因为其基地址还没有初始化, 然后通过lgdt [GdtPtr],将内存中GDTR的内容加载到GDTR中,重点在于保存 GDT的基地址;
4.关中断, 即设置CPU不响应任何其他的外部中断,因为CPU现在的时间片只属于当前加载的程序;
5.打开地址线A20;
6将CR0的 PE 位置1;PE位==1,表明CPU运行在保护模式下;
7.跳转到保护模式: jmp dword SelectorCode32:0 ,这里的代码指提供了选择子,(2.3)末部分,已经说明了为什么通过选择子就可以索引到 32位代码段 LABEL_SEG_CODE32;(这就是从实模式跳入保护模式)32位代码段(由实模式跳入,即保护模式)的定义
1
2
31.将对应选择子赋值到 对应寄存器, 即设置任务代码的运行环境,不得不提的是本段代码还改变了ss和esp,则在32位代码段中所有的堆栈操作将会在新增的堆栈段中进行;
2.做任务;
3.任务做完后,跳转到16位代码段,因为从保护模式跳回实模式,只能从16位代码段中跳回;
80x86系统指令寄存器
标志寄存器
EFLAGS系统标志和IOPL字段控制I/O,可屏蔽的硬件中断、调试、任务切换和虚拟8086模式。仅允许特权代码(通常为操作系统过执行代码)修改这些位。
在64位模式下,RFLAGS寄存器扩展为64位,保留高32位。PFLAGS中系统标志(64位模式)或EFLAGS(兼容模式)。在IA-32e模式下,处理器不允许设置VM位,因为不支持virtual-8086模式(尝试设置该位将被忽略)。同样,处理器将不会设置NT位。但是处理器确实允许软件将NT位置1(请注意,如果将NT位置1,则IRET会在IA-32e模式下引起一般性保护故障)。在IA-32e模式下,YSCALL/SYSRET指令具有一种可编程的方法来指定哪些位是已RFLAGS/EFLAGS中清除。这些说明保存/恢复EFLAGS/RFLAGS。
内存管理寄存器
GDTR
保存基地址(在保护模式下为32位,在IA-32e模式下为64位)和16位表GDT的限制。基地址指定GDT字节0的线性地址;表格限制指定了表中的字节数。LGDT和SGDT指令分别加载和存储GDTR寄存器。开机或重置在处理器中,基地址设置为默认值0,限制设置为0FFFFH。必须有新的基本地址将其作为保护模式操作的处理器初始化过程的一部分加载的GDTR。
LDTR
保留16位段选择器的结伴地址(在保护模式下为32位,在IA-32e模式下为64位)段限制和LDT的描述符属性。基地址指定字节的线性地址LDT段的0,段限制指定段中的字节数。LLDT和SLDT指令分别加载和存储LDTR寄存器的段选择器部分的包含LDT的段必须在GDT中具有段描述符。当LLDT指令加载一个LDTR中的段选择器:LDT描述符中的基地址、限制和描述符属性会自动加载到LDTR中。
发生任务切换时,LDTR会自动加载LDT的段选择器和描述符为新任务。在写入新的LDT信息之前,不会自动保存LDTR的内容进入寄存器。在处理器加电或重置时,段选择器和基地址被设置为默认值0和限制设置为0FFFFH。
IDTR
寄存器保存基地址(保护模式下为32位,IA-32e模式下为64位)和16位表限制IDT。基地址指定IDT字节0的线性地址,表限制指定数量表中的字节数。LIDT和SIDT指令分别加载和存储IDTR寄存器。开机或重置处理器后,基地址设置为默认值0,限制设置为0FFFFH。然后可以在处理器初始化过程中更改寄存器中的地址和限制。
TR
任务寄存器包含16位段选择器,基地址(在保护模式下为32位,在IA-32e中为64位),段限制和当前任务的TSS的描述符属性。选择器引用TSS、GDT的描述符。基地址指定TSS字节0的线性地址;段限制指定TSS中的字节数。LTR和STR指令分别加载和存储任务寄存器的段选择器部分。当LTR指令将段选择器加载到任务寄存器中时,基址、限制和描述符属性从TSS描述符将自动加载到任务寄存器中。处理器加电或重置时,基地址设置为默认值0,限制设置为0FFFFH。发生任务切换时,任务寄存器会自动加载段选择器和描述符新任务的TSS。在写入的新的TSS之前,不会自动保存任务寄存器的内容信息进去寄存器。
控制寄存器
CR0
包含控制处理器的操作模式和状态的系统控制标志。
CR3
包含分页结构层次结构基础的物理地址和两个标志(PCD和PWT)。仅指定基址的最高有效位(减去低12位);低12位地址“0”假定为0.因此,第一个分页结构必须与页面(4KB)对齐边界。PCT和PWT标志控制处理器内部数据中该分页结构的缓存(它们不控制页面目录信息的TLB缓存)。使用物理地址扩展中,CR3寄存器包含页面目录指针表的基地址。在IA-32e模式下,CR3寄存器包含PML4表的基地址。
系统指令
LGDT加载GDTR寄存器——将GDT基址和限制从内存加载到GDTR寄存器。
SGDT存储GDTR寄存器——将GDT基址和GDTR寄存器中的限制存储到内存。
LIDT加载IDTR寄存器——将IDT基址和限制从存储器加载到IDTR寄存器中。
SIDT加载IDTR寄存器——将IDT寄存器的IDT基址和限制存储到内存中。
LLDT加载LDT寄存器——将LDT段选择器和段描述符从内存加载到LDTR,段选择器操作数也可以位于通用寄存器中。
SLDT存储LDT寄存器——将LDTR寄存器中的LDT段选择器存储到存储器或存储器中。
LTR记载任务寄存器——将TSS的段选择器和段描述符从内存加载到任务寄存器,段选择器操作数也可以位于通用寄存器中。
STR存储任务寄存器——将当前任务TSS的段选择器从任务存储器存储到存储器或通用寄存器。
鸣谢
1 | https://zhuanlan.zhihu.com/p/42309472 |