总览

system register(硬件基础,寄存器是计算机硬件的一个抽象,体系结构的思想)

CR:用于控制系统的重要行为,例如分页分段之类

System-Flags Register:一些标志位,进入内核态时需要保存

Descriptor-Table Registers:指向一些描述内存的数据结构

Task- Reg : 包含任务状态段的位置以及大小

Debug Registers:控制软件调试

Extended-Feature-Enable Register:EFER寄存器用于启用和报告非由CRn控制寄存器控制的特殊功能的状态。特别是,EFER被用于控制长模式的激活。

System-Configuration Register:启用和配置系统总线功能。

System-Linkage Registers:这些寄存器被系统链接指令用于指定操作系统入口点、堆栈位置和指向系统数据结构的指针。(可以看出若陷入os,其作用及其重要)

Memory-Typing Registers:目前没见过用法

Debug-Extension Registers:debug扩展

Performance-Monitoring Registers:性能监视寄存器用于计数处理器和系统事件,或事件的持续时间。

Machine-Check Registers:机器检查寄存器控制处理器对不可恢复故障的响应。它们还用于将有关此类故障的信息报告给旨在响应此类故障的系统实用程序。

sysregs

System-Data Structures(软件基础)

系统数据结构由系统软件创建和维护,以供处理器在受保护模式下运行时使用。在受保护模式下运行的处理器使用这些数据结构来管理内存和保护,并在中断或任务切换发生时存储程序状态信息

descriptors:描述符向处理器提供有关段的信息,例如它的位置、大小和特权级别。一种特殊类型的描述符,称为门,用于为软件例程提供代码选择器和入口点。可以定义任意数量的描述符,但是系统软件必须至少要为当前执行的代码段和堆栈段创建一个描述符。

descriptor table:全局描述符表包含对所有程序都可用的描述符,而本地描述符表包含由单个程序所使用的描述符。中断描述符表只包含由中断处理所使用的gate描述符

task-state segment:任务状态段是用于保存特定程序或任务的处理器状态信息的特殊段。它还包含在切换到更多特权程序时使用的堆栈指针。硬件多任务处理机制在挂起和恢复任务时使用段中的状态信息。调用和对交换堆栈的中断会导致从任务状态段读取堆栈指针。

Page-Translation Tables

sysdatastructor

内存模型

这个是策略,机制是分段和分页以及地址的表示形式那些。

感觉好复杂,主要看一下保护模式下的内存模型吧,文档原话

传统的x86架构支持一种段翻译机制,允许系统软件将指令和数据重新定位并隔离到虚拟内存空间中的任意位置。一个段是在线性地址空间内的一块连续内存。段在线性地址空间中的大小和位置是任意的。指令和数据可以分配到一个或多个内存段中,每个段都有其自身的保护特性。处理器硬件强制执行规则,规定一个段是否可以访问另一个段。

分段机制提供了十个段寄存器,每个寄存器定义了一个段。这十个段寄存器中有六个(CS、DS、ES、FS、GS 和 SS)定义了用户段。用户段存储软件、数据和栈,可以被应用软件和系统软件使用。剩下的四个段寄存器(GDT、LDT、IDT 和 TR)定义了系统段。系统段包含的数据结构仅由系统软件初始化和使用。段寄存器包含一个基址,指向段的起始位置;一个限值,定义段的大小;以及定义段保护特性的属性。

尽管分段在软件和数据的重新定位和保护方面提供了很大的灵活性,但通常通过结合软件和硬件分页支持来处理内存隔离和重新定位更加高效。因此,大多数现代系统软件绕过了分段功能。然而,分段不能完全禁用,理解分段机制对于实现长模式(long mode)系统软件仍然非常重要。

实模式

如其名,不赘述了

Virtual-8086 Mode Segmentation

到这好像有虚拟内存了,然后利用分段,实现了更多的空间利用,但是感觉分页并不成熟

虚拟8086模式支持在保护模式下运行16位实模式程序(详见下文)。它使用了一种简单的内存分段方式,可选的分页机制,以及有限的保护检查。在虚拟8086模式下运行的程序最多可以访问1MB的内存空间。

与实模式下的分段一样,每个64K的段(CS、DS、ES、FS、GS、SS)在16字节边界上对齐。段基址是给定段中的最低地址,等于段选择子乘以16。POP和MOV指令的工作方式与实模式完全相同,可以用来将一个(可能是新的)段选择子加载到某个段寄存器中。当发生这种情况时,选择子会更新,段基址会被设置为选择子乘以16。段限值和段属性不会改变,通常它们分别是64K(允许的最大限值)和读/写数据。

除中断和异常外,远程传输(FAR transfers)的操作与实模式相同。在远程传输时,CS(代码段)选择子会更新为新值,CS段基址会设置为选择子乘以16。CS段限值和属性保持不变,但通常分别为64K和读/写属性。中断和异常会将处理器切换到保护模式。(更多信息请参见第8章“异常和中断”)。

Protected Mode Segmented-Memory Models

现在的主流,若开启部分分段,则可以向后兼容,如果全分页的话就是平坦内存模式

Multi-Segmented Model

分段分页结合体,可以多个段映射到一个页或一个段映射到多个页

Flat-Memory Model

将段寄存器的基址置零,段限制在4GB,则可用4GB的连续的虚拟内存空间

在64位模式下,分段机制被禁用了。分段硬件会忽略段基址值,并将其视为0。同样,段限值和大多数段属性也会被忽略。不过,有几个例外情况。CS段的DPL、D和L属性仍然会被使用,分别用于确定程序的特权级别、默认操作数大小,以及程序是在64位模式还是兼容模式下运行。FS和GS段可以在地址计算中用作额外的基址寄存器,并且这些段可以具有非零的基址值。这一特性有助于访问线程局部数据(thread-local data)和某些系统软件的数据结构。有关在64位模式下FS和GS段的详细信息,请参阅第80页的“64位模式下的FS和GS寄存器”。系统段寄存器在64位模式下始终会被使用

Segment Selectors and Registers

此部分讲解了段寄存器中存储的内容的结构

Segment Selectors

Segment selectors are pointers to specific entries in the global and local descriptor tables.

segmentselector

选择子索引字段(Selector Index Field):位于位15:3。选择子索引字段指定描述符表中的一个条目。描述符表中的条目是8字节长,因此选择子索引会乘以8来形成描述符表中的字节偏移量。然后,将该偏移量加到全局或本地描述符表基址(由表索引位指示)上,以在虚拟地址空间中形成描述符条目的地址。 在长模式下,一些描述符条目是16字节长,而不是8字节(有关长模式描述符表条目的更多信息,请参阅第88页的“遗留段描述符”)。这些扩展的描述符在描述符表中占用了两个条目。然而,长模式仍然将选择子索引乘以8来形成描述符表的偏移量。系统软件负责分配选择子,以便它们正确地指向扩展条目的起始位置。

表指示符(TI)位(Table Indicator (TI) Bit):位于位2。TI位指示哪个表保存了选择子索引引用的描述符。当TI=0时,使用GDT(全局描述符表),当TI=1时,使用LDT(本地描述符表)。从适当的描述符表寄存器中读取描述符表基址,并将其加到上述的缩放选择子索引上。

请求者特权级(RPL)字段(Requestor Privilege-Level (RPL) Field):位于位1:0。RPL表示创建选择子时处理器运行的特权级别(CPL)。 RPL用于段特权检查,以防止低特权级别的软件访问特权数据。有关段特权检查的更多信息,请参阅第106页的“数据访问特权检查”和第109页的“控制传输特权检查”。
空选择子:目前没看懂啥意思先遗留一下

Segment Registers

存储段结构的硬件基础,此处只看了64位程序的

64位模式下的CS寄存器:在64位模式下,CS寄存器的隐藏部分大部分被忽略。只有L(长模式)、D(默认操作数大小)和DPL(描述符特权级别)属性在64位模式下被识别。地址计算假定CS.base的值为0。CS引用不会检查CS.limit值,而是检查有效地址是否为规范形式(canonical form)。

64位模式下的DS、ES和SS寄存器:在64位模式下,ES、DS和SS段寄存器的内容被忽略。段寄存器隐藏部分的所有字段(基址、限值和属性)都被忽略。在64位模式下,引用ES、DS或SS段的地址计算被视为段基址为0。处理器不会执行限值检查,而是检查所有虚拟地址引用是否为规范形式。

启用和激活长模式或在64位模式和兼容模式之间切换时,不会改变段寄存器的可见部分或隐藏部分的内容。除非执行显式的段加载操作,否则这些寄存器在64位模式执行期间保持不变。

64位模式下的FS和GS寄存器:与CS、DS、ES和SS段不同,FS和GS段覆盖在64位模式下可以使用。当在64位模式下使用FS和GS段覆盖时,它们各自的基址会在有效地址(EA)计算中使用。完整的EA计算公式为:(FS或GS).base + base + (scale * index) + displacement。FS.base和GS.base的值也被扩展为完整的64位虚拟地址,如图4-5所示。在64位线性地址计算中的任何溢出都会被忽略,结果地址将环绕到地址空间的另一端。

gs和fs的特殊更新方式

貌似段寄存器有隐藏部分,需要特殊处理

没看懂回来补上,貌似段寄存器有隐藏部分,因此赋值要额外小心,并且gs和fs也是64位需要用的

段描述符表

虽然64位平坦模式用户层已经基本没有了分段机制,但是0环还存在着,因此需要理解描述符表,主要分为三个,分别为GDT,LDT,IDT

软件通过初始化其相应的描述符-表寄存器来建立描述符表在内存中的位置。下面的部分将描述描述符表寄存器和描述符表。

GDT

保护模式下的系统软件必须创建一个全局描述符表(GDT)。GDT包含了代码段和数据段的描述符条目(用户段),这些段可以被所有任务共享。除了用户段外,GDT还可以包含门描述符和其他系统段描述符。系统软件可以将GDT存储在内存中的任何位置,并且应该保护包含GDT的段,使其不被非特权软件访问。寻表方式如下:(如果要理解的话需要理解空选择子还要理解实模式和80386模式下的段描述符)

image-20240923125736202

长地址模式下的GDTR和IDTR格式

限值(Limit):2字节。这些位定义了GDT(全局描述符表)的16位限值或大小(以字节为单位)。限值与基址相加得到GDT的结束字节地址。如果软件尝试访问超出GDT限值的描述符,将会发生一般保护异常(#GP)。

AMD64架构在长模式下不扩展描述符表的偏移量。因此,GDTR(全局描述符表寄存器)和IDTR(中断描述符表寄存器)的限值字段大小与传统大小保持不变。处理器在长模式下访问GDT和IDT时仍然会检查这些限值。

基址(Base Address):8字节。基址字段保存GDT在虚拟内存空间中的起始字节地址。GDT可以位于虚拟内存中的任何字节地址,但系统软件应将GDT对齐到四字边界(quadword boundary),以避免访问未对齐数据时可能产生的性能损失。

AMD64架构将GDTR的基址字段扩展为64位,以便在长模式下运行的系统软件可以在64位虚拟地址空间中的任意位置定位GDT。当处理器在传统模式下运行时,它会忽略基址的高4字节。

GDTR

LDT

GDT

系统寄存器详解

RFLAGS

包含控制位和状态位,控制位中含有权限信息,状态位则是用来保存算数运算的状态,内核态到用户态需要恢复

部分内核级指令

Fast System Call and Return

SYSCALL and SYSRET

SYSENTER and SYSEXIT (Legacy Mode Only)

SWAPGS

SWAPGS指令为系统软件提供了一种加载系统数据结构指针的快速方法。SWAPGS可以在由于系统切换指令或由于中断或异常而进入系统软件例程时使用。在返回到应用程序软件之前,SWAPGS可以恢复一个被系统数据结构指针所取代的应用程序数据结构指针。

内核指令字典

IRETQ

将程序控制流从异常处理返回,并置位相关寄存器,会返回flags,cs,rip,如果cpl变化,ss和rsp也将被返回,

示例代码如下:

static void restore_state() {
asm volatile("swapgs ;"
"movq %0, 0x20(%%rsp)\t\n" //ss
"movq %1, 0x18(%%rsp)\t\n" //rsp
"movq %2, 0x10(%%rsp)\t\n" //flag
"movq %3, 0x08(%%rsp)\t\n" //cs
"movq %4, 0x00(%%rsp)\t\n" //rip
"iretq"
:
: "r"(user_ss),
"r"(user_rsp),
"r"(user_rflags),
"r"(user_cs), "r"(win));
}