x86架构学习之操作模式、内存寻址、特权级

0 前言

CPU的操作模式,一言难尽,可以理解为CPU的身份,而特权级就表示CPU的权利,比如,皇帝和臣子,不同的身份具有不同的权限,皇帝可以给臣子尚方宝剑,来提升权力。

CPU的内存模式,可以理解为访问资源的方式,不同的路径也许都能访问相同的资源,但是不同路径的安保程度不同。

CPU的特权级表示CPU对资源控制的权限级别,运行在不同特权级下的CPU,对物理资源具有不同的访问权限。

大部分CPU都具有操作模式和特权级,但只有高级CPU才配备的内存模式,如单片机,大部分型号是没有内存模式的,高级点的单片机内存保护单元(MPU),arm的cotex A系列,x86系列,都具有内存模式,由内存管理单元实现(MMU)。

例如ARM的cotex-m系列的cpu:

  • 特权级下的cpu可以访问所有物理资源,即所有寄存器和外设,而非特权级下的cpu,有一些关键的cpu寄存器是无法访问的,例如CONTROL寄存器,特权级下的CPU可以通过修改CONTROL寄存器,进入非特权级,一旦进入非特权级,则无法再次修改CONTROL寄存器回到特权级。
  • cpu操作模式有两种:线程模式(thread mode)和处理者模式(handler mode)。处理器模式固定被视为特权级,不受CONTROL寄存器控制,中断(含异常)是进入处理器模式的唯一方法。
  • 系统代码(通常被视作经过大量测试、稳定的代码)运行在特权级,而应用代码(通常被视作不稳定、不安全的代码)运行在应用层,这在一定程度上实现了程序隔离。非特权级CPU想要回到特权级,可以触发中断,在中断程序中修改CONTROL寄存器,等返回中断,程序自然就回到特权级。

同样对于x86架构(含推论),也存在操作模式和特权级,只是复杂些,另外还存在内存寻址模式。

1 内存寻址

1.1 内存管理单元(MMU,memory manage unit)

1.1.1 基本组成

x86的MMU由分段单元分页单元组成:

  • 分段单元:将程序访问的地址(称为逻辑地址)做段映射,得到的地址,叫线性地址,在保护模式下,所有代码访问的地址均是逻辑地址,这其中保护程序计数器(pc)取指的地址。分段单元负责将逻辑地址转换成线性地址
  • 分页单元:将线性地址进一步做分页映射,得到最终的物理地址
逻辑地址
线性地址
物理地址
代码
分段单元
分页单元
内存

分段单元中包含6个段寄存器,其中3个专用寄存器,3个通用寄存器:

  • cs 代码段寄存器,用于代码寻址,关联指令,call、jmp等,以及程序计数器。
  • ss 栈段寄存器,用于堆栈寻址,关联指令push、pop等。
  • ds 数据段寄存器,用于数据寻址,关联指令,mov等。
  • es,fs,gs 通用段寄存器。

1.1.2 硬件分段

分段单元的用法跟3种内存寻址模式有关,详见后面内存寻址模式一节。

平坦模式
分段单元关闭,确切的说,分段单元将逻辑地址直接等于线性地址。

实地址模式
线性地址由逻辑地址+上端寄存器左移4位得到。这个模式是为兼容8086老程序而存在,具体参考后面实地址模式说明。

逻辑地址
线性地址
左移4位
代码
+
分页单元
段寄存器

分段模式
x86的段寄存器是个16位的寄存器,高13位表选择符,中间1位表描述符表指示器(TI,table indicator),最后2位表示请求者特权级(RPL,requestor privilege level),请求者特权级在后面特权级一节讲到。

bit 15~3bit 2bit 1~0
indexTIRPL
  • index指示,该段的段描述符在描述符表中的索引位置,段描述符是一个8字节的数据结构,用于描述段的一些基本信息,如段的访问权限,段基地址和大小,因此称为段描述符
  • TI指示,该段描述符是在全局描述符表(GDT,global descriptor table),还是局部描述符表(LDT,local descriptor table),与之关联的两个物理寄存器就是gdtrldtr,分别存放着全局描述符表和局部描述符表的线性地址(注意是线性地址)。
  • RPL则是之前说过的请求者特权级。

硬件层面

  1. 先根据TI决定描述符在全局描述符表还是局部描述符表,从gdtr/ldtr取得描述符表的线性基地址
  2. 然后根据index索引偏移指定地址数,得到段描述符的线性地址
  3. 最后线性地址经过分页单元得到物理地址,取得段描述符的实际内容,从内容中取得段的线性基地址

ps:注意别搞混淆了,段描述符段描述符,段描述符是描述段用的。

左移3位
段选择符
index
TI
gdtr or ldtr
描述符表线性基地址
+
段描述符线性地址
段描述符物理地址
段描述符线性地址
分页单元
段描述符
段线性基地址
逻辑地址
段线性基地址
+
线性地址
分页单元
代码

1.1.3 x86的硬件分页

分页模式是通过cr0寄存器的PG标志来开启,PG=0时,分页模式被关闭了,线性地址被直接解释成物理地址。

二级分页
当分页开启的时候,硬件分页默认采用二级分页,也就是当分页单元接受到一个32位线性地址时,它被按如下规则进行解释

10bit 31~2210bit 21~1212bit 11~0
页目录索引(pdi)页表索引(pti)页内偏移(pi)

由页内偏移字段的位数知,一页的大小为4kB,页表和页目录本身大小也是一个页,页表和页目录中存放着一个个等大小的条目,页表的称为页表项,页目录的称为页目录项,或者简单理解为表项即可,不管几级页表,表项等大小,默认情况下表项为32位,当开启物理地址扩展(PAE,physical address extension)时,表项扩展位64位。
表项中字段(关键字段已用红色标出)

字段占用位长作用
present1 bit标志页是否在主存,如果不在主存,访问时,将产生缺页异常,线性地址被暂存到cr2寄存器
field20bit物理地址的高20bit
accessed1 bit标志位,每当分页单元访问该页时,则自动设置它(分页单元从不重置它,由操作系统重置),当页被交换到磁盘时,可由操作系统使用,如Linux用于判断是否是缺页异常,还是无权限
dirty1bit标志位,每当写入页时,分页单元自动设置该位(分页单元从不重置它,由操作系统重置)
read/write1bit读写权限
user/supervisor2bit访问页需要的特权级
PCD(page cache disable)1bit设置该页是否启用硬件高速缓存,Linux永远开启
PWT(page write through)1bit设置该页是否采用通写策略(硬件缓存加速的一种策略),Linux永远开启
page size1bit页大小标志位,仅用于页目录项,设置是否启用扩展分页,扩展分页下,页表索引10bit和页内偏移12bit合并,形成一个22bit的页内偏移,因此页大小由4kB变成4MB,同时field字段的20bit只有高10bit具有意义
global1bit用来防止常用页被从TLB高速缓存中刷新出去

二级目录硬件分页寻址

页内偏移
目标地址
页表索引
页基地址
页目录索引
页表基地址
页目录基地址
pi
+
内存
pti
+
pdi
+
cr3

ps1:分页单元这,输出的地址都是是物理地址
ps2:硬件cr3寄存器中存放着页目录的线性基地址

1.1.4 x86的物理地址扩展(PAE)

  • PAE通过设置 cr4的PAE标志位激活
  • 物理上,intel将地址总线从32位扩展为36位
  • 页表项中的field字段20位已不够用,因此页表项被扩展成64位,其中field字段扩展成24位,页表大小固定4kB(PAE下开启扩展分页时为2MB),因此页表项数为512(之前为1024)。
  • 增加一级寻址,称为页目录指针表,线性地址按如下解释
2bit9bit9bit12bit
页目录指针表索引(pdpti)页目录索引(pdi)页表索引(pti)页内偏移(pi)

ps1:硬件cr3寄存器中存放着页目录指针表的线性基地址
ps2:由于逻辑地址仍然是32位,而用户进程不能修改cr3寄存器,因此用户进程只能访问4GB空间,而内核程序可以通过修改cr3,访问64GB的内存空间。

1.1.5 x64的硬件分页

x64是64位地址总线,因此不需要PAE功能,不同架构的硬件分页可能不同,x64的线性地址解释如下

reserved9bit9bit9bit9bit12bit
页全局目录索引(pgdi)页上级目录索引(pudi)页中间目录索引(pmdi)页表索引(pti)页索引(pi)

ps1:寻址逻辑图可以参考二级页表的寻址图,只须进行简单扩展即可
ps2:硬件cr3寄存器中存放着页全局目录的线性基地址

1.1.6 x86的硬件高速缓存

高速缓存的结构如下

高速缓存sram
缓存控制器
DRAM
分页单元

高速缓存支持两种更新策略:

  • 通写(write through),即写缓存,又写内存。
  • 回写(write back),仅写缓存。

高速缓存通过 cr0 寄存器的 CD 标志开启,同时也可通过页表项的PCD字段进行二次控制,CD 相当于全局开关,PCD相当于以页为粒度的二级开关。

1.2 内存寻址模式

x86有三个内存寻址模式,即平坦模式分段模式实地址模式
在这里插入图片描述

1.2.1 平坦模式(flat memory mode)

分段单元被关闭,程序访问的地址被直接解释成线性地址,如果分页单元被禁用(CR0 PG标志位为0),则线性地址又直接被解释成物理地址。

线性地址
代码
分页单元

1.2.2 分段模式(segmented memory mode)

分段单元被开启,程序访问的地址被解释成逻辑地址,经过分段单元得到线性地址。

逻辑地址
线性地址
段选择符
段描述符线性地址
代码
+
分页单元

1.2.3 实地址模式(real-address memory mode)

分段单元被开启,但使用方式与分段模式不同,段寄存器的内容被当作段偏移,线性地址空间大小只有1M,16的段寄存器值左移4位,再加上程序访问的地址的低16位,得到线性地址,最后线性地址被直接解释成物理地址。程序访问地址只有16位,因此,每个段的大小也只有64KB。这个地址模式存在的原因,是为了兼容执行历史上为8086机器编写的程序,8086是16位机,或者说准16位机,它有20位地址线,16位数据总线,cpu寄存器16位宽,代码访问的地址也是16位,这就是为什么上面程序访问的地址只取16位,最后生成的物理地址有20位。

在这里插入图片描述

1.3 分段和分页开关

要使用分段和分页,首先要进入保护模式(详见后面操作模式切换),保护模式下会启动段/页 保护机制(详见后面特权级)。

分段开关:进入保护模式自动开启,将段描述符中基地址设置为0,相当于关闭了分段,但段保护功能正常。
分页开关:设置 CR0.PG 位为1开启,CR0.WP 位为写保护是能位,页表项(页描述符)中的 read/write 字段可以控制单个页的写保护开关。

2 特权级(内核态与用户态)

当保护模式开启时(CR0.PE=1),内存在寻址访问会别保护,这其中就包括特权级校验(保护模式还有读写权限校验)

2.1 4种特权级

保护模式下有4种特权级,每个段寄存器最低2位表示,请求者特权级(RPL,requester privilege level),2位刚好对应4种取值0~3,表示4种特权级,0最高,3最低,其中代码段寄存器cs的RPL比较特殊,它代表当前CPU的特权级(CPL,current privilege level)。多数操作系统,如Linux,只使用0和3两个特权级,表内核态用户态

bit 15~3bit 2bit 1~0
indexTIRPL

2.2 特权级校验

段描述符中还有一个字段,描述符特权级(DPL,descriptor privilege level),表是访问该段需要的最低特权级,如果是取指,则仅需要cs的RPL≤DPL,如果是访问数据,则同时需要cs的RPL和数据段寄存器的RPL均小于等于DPL。

CPL=RPL
RPL
DPL
代码段寄存器
CPL和RPL均<=DPL?
数据段寄存器
段描述符
允许访问
拒绝访问并产生异常

ps:中断是个例外,因为中断的目的就是用来提权,因此,中断发生时,在访问中断处理程序入口地址时,需要CPL>=DPL,详见我另一篇文章《x86架构学习之中断》。

2.3 特权级穿越

想要从低特权级跨越到高特权,需要使用x86提供的 机制,你可以理解为,x86有4个区域(4个特权级),每两个区域之间有一扇门,用于在不同区域之间穿越。这些门有4种:

  • 调用门,远跳转指令使用,参考x86架构学习之调用指令call
  • 任务门,由硬件任务切换使用(非操作系统任务,x86有自己的任务架构,操作系统可以基于它实现多任务系统,也可不用,如Linux就没有使用硬件任务,因为使用硬件任务系统,会导致可移植性变差,x86就不兼容8086的硬件任务)。
  • 陷阱门,由中断或异常使用,具体看操作系统的配置,如Linux用它处理异常。
  • 中断门,由中断或异常使用,具体看操作系统的配置,如Linux用它处理中断。

3 操作模式(处理器模式)

3.1 操作模式种类

CPU操作模式决定了CPU的哪些指令集和架构特性可以使用。

当编写 IA-32 或 Intel 64位处理器的代码时,程序员需要知道处理器所处的操作模式,操作模式与内存寻址模式的关系如下:

  • 保护模式,当在保护模式时,处理器能使用三种内存寻址模式中的任意一种(实地址模式一般只在virtual-8086模式下使用),保护模式下,能够直接执行 实地址模式的 8086 代码,这种场景被称为virtual-8086模式,这并不是一个操作模式,而是保护模式的一个特性。保护模式下会对分段和分页进行保护:
    • 分段保护:特权级校验,访问范围校验(limit checking),描述符类型校验(type checking)。
    • 分页保护:特权级校验,访问范围校验,读写权限校验。
  • 实(地址)模式,处理器仅使用实地址模式的内存寻址模式,用于兼容老8086程序,处理器刚上电或复位时,是处于实模式。
  • 系统管理模式(SMM),此模式下,处理器会转换到一个隔离的地址空间,称为系统管理RAM(SMRAM),SMM用来处理硬件平台相关的系统级的事务,比如电源管理,不用于应用或一般系统软件,它好处就是,在早期就与操作系统和应用程序隔离开,对于应用或操作系统来说,是不可见的。这个模式下,内存寻址模式与实地址模式相似。
  • IA-32e模式(长模式,仅64位机),这不是真正的操作模式,是64位模式和兼容模式的统称,为了跟32位机区分开,或称为32位机的扩展模式。
    • 兼容模式,此模式支持大多数老32位、16位程序直接在64位机上运行,但是老的运行在virtual-8086模式的,和使用了硬件任务管理的应用程序,无法在该模式下运行。兼容模式由代码段寄存器的L位控制开关,因此64位的操作系统,可以让64位程序运行在64位模式,让老的32位或16位程序运行在兼容模式。兼容模式和32位的保护模式相似,只能访问4GB线性地址空间,只能使用16位宽和32位宽的指令,同时继承了32位保护模式的物理地址扩展(PAE,physical address extension)(这在64位模式是不需要的)
    • 64位模式,此模式下,分段部分关闭(部分段寄存器分段功能无效,权限校验还在),CS, DS, ES, SS段寄存器指定的段基地址被视为0,即内存寻址模式为平坦模式,但是FS和GS除外,他们的分段功能仍然生效。64位模式下,分段模式和实地址模式在该操作模式下是不可用的。同样64位模式由基于代码段的操作系统开启。此模式下,程序能够访问:
      • 64位平坦空间。
      • 8个额外的通用寄存器,即R8~R15。
      • 8个单指令多数据流水线(SIMD,single-instruction multiple-data)扩展 (Intel SSE, SSE2, and SSE3, and SSSE3)用到的寄存器,SIMD是为加速MMX(multimedia extension)指令族而引入的硬件电路。
      • 允许按64位宽访问cpu寄存器(例如 mov rax [mem] 必须是64位模式,前缀r表64位)。

3.2 操作模式切换

  • 实(地址)模式,上电复位就在这个模式,这是默认模式。
  • 保护模式,通过设置寄存器 CR0.PE 使能位,来开启保护模式, CR0.PE=0 则回到实模式。寄存器 EFLAGS.VM 位决定当前是否在 virtual-8086 模式。
  • 系统管理模式,任何时候,CPU收到SMI中断信号,就会进入该模式。
  • IA-32e模式,保护模式下,通过开启分页,并设置寄存器 IA32_EFER.LME 位进入 IA-32e 模式, IA32_EFER.LMA 也标志着当前是否在 IA-32e 模式。只有进入 IA-32e 模式,才能进入下面子模式。
    • 兼容模式,寄存器 CS.L = 0。
    • 64位模式,寄存器 CS.L = 1。

intel 64架构的CPU支持所有IA-32架构CPU的操作模式。

请添加图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值