ARM体系结构是一种精简指令集体系结构RISC,具有以下基本特点:通用寄存器数量较多;采用统一寻址模式,系统主存和外设分布在CPU物理地址空间的不同范围;采用load/store专用访存指令,其他指令不能直接访问地址单元,地址由寄存器或指令本身决定。
飞腾CPU遵循的ARMv8标准架构,所有的指令编码长度为32位,支持64位虚拟地址,4个权限级和64位执行状态。虽然飞腾CPU也兼容32位执行状态,但是本章仅仅讨论在操作系统相关的64位执行状态内容。
CPU权限级主要体现在三方面的约束:指令,寄存器,物理和虚拟地址空间。当飞腾CPU处于低权限级时,可以执行的指令类型会受限,访问的寄存器类型也会受限,可以访问的物理和虚拟地址范围也受限。当飞腾CPU处于低权限级时,如果超出受限范围执行指令、访问寄存器或地址时,飞腾CPU就会触发异常。
飞腾CPU支持64位执行状态AArch64,采用指令集A64。参见图1-1,执行状态AArch64下的飞腾CPU支持ELx(x=0,…,3)4个CPU权限级:
EL0是应用级权限,权限最低;
EL1是内核级,主要运行操作系统内核;
EL2是超权限级,主要运行虚拟机监控器;
EL3是安全级,也是最高权限级。
图1-1 权限级模型
飞腾CPU系统寄存器的命名都有一个权限级ELx(x=0/1/2/3)后缀,表示允许操作该寄存器的最低权限级。只有在EL3权限级时,飞腾CPU才能在安全态和非安全态之间切换。非安全态具有完整的四个权限级;安全态没有实现EL2权限级。
2. 系统调用和返回
当异常发生时,飞腾CPU的权限级要么升高,要么不变,不允许降低。因此只有触发异常才能实现飞腾CPU从低权限级到高权限级的切换。SVC,HVC和SMC系统调用指令会触发飞腾CPU的异常,
因此系统调用是飞腾CPU在低权限级进入高权限级的一种常用方式。在高权限级只需要通过异常返回指令ERET,就可以返回到触发异常之前的权限级(权限级要么降低,要么不变,不允许升高)。
图1-2 系统调用的权限切换模型
在飞腾CPU发生异常后,切换到新的权限级之前,将发生异常时的处理器状态保存起来,处理器状态包含了权限级和执行状态等信息;只要在新权限级返回之前不对保存的处理器状态进行修改,ERET指令就会直接将飞腾CPU切回到发生异常时的权限级。
图1-2给出了系统调用和系统调用返回的权限级切换示意图。根据上图可以看出:
在EL0权限级时不能运行HVC和SMC指令;
在EL0级发生异常后,飞腾CPU一定不会切换到EL0级。
第二节 通用寄存器
1. 64位通用寄存器
在AArch64执行状态下,飞腾CPU提供31个64位通用寄存器Xn(n=0/1/…/30),其中X30用作跳转链接寄存器。某些指令仅使用通用寄存器的低32位,寄存器Wn用于寄存器Xn低32位的独立访问,参见图1-3。 需要说明的是,32/16/8位数据都是用寄存器Wn来描述,具体数据长度由指令来具体。
图1-3 64位通用寄存器
2. SIMD和浮点寄存器
飞腾CPU还提供了32个128 位SIMD和浮点寄存器,这32个128位寄存器可以作为标量寄存器使用,也可以作为向量寄存器来使用。
每个128位SIMD和浮点寄存器作为128位标量寄存器时记做Qn(其中n=0...31),也可以作为8/16/32/64位标量寄存器使用:8位标量寄存器Bn是低八位;16位标量寄存器Hn是低16位;32位标量寄存器Sn是低32位;64位标量寄存器Dn是低64位。
图1-4 标量寄存器
图1-5 向量寄存器
除了可以做标量寄存器,每个128位SIMD和浮点寄存器还可以作为向量寄存器Vn(n=0/1/…/31)来使用。虽然每个寄存器是128位的,但是向量长度可以是64位也可以是128位,具体的向量长度取决于具体的指令。
当向量长度为128位时:Vn是一个128位寄存器,Vn.2D是两个64位寄存器,Vn.4S是四个32位寄存器,Vn.8H是八个16位寄存器,Vn.16B是十六个8位寄存器。
当向量长度为64位时:Vn和Vn.1D是一个64位寄存器,Vn.2S是两个32位寄存器,Vn.4H是四个16位寄存器,Vn.8B是八个8位寄存器。
无论向量长度是64位,还是128位,其中每个寄存器都可用下标方式来命名,例如Vn.B[i],Vn.H[i],Vn.S[i]和Vn.D[i]。
第三节 处理器状态
1. 处理器状态
处理器状态主要包括条件标志,异常屏蔽标志、执行状态和权限级等信息。飞腾CPU的当前处理器状态不是单一系统寄存器来描述的,是由若干系统寄存器一起来描述的。
除了描述当前处理器状态的系统寄存器组,飞腾CPU还提供了保存处理器状态寄存器,专门用于权限级切换的处理器状态保存。
图1-7 NZCV寄存器
2. 条件标志
NZCV寄存器是一个32位寄存器,描述了用于数据处理过程的条件标志,包括负标志N、零标志Z、借位标志C和溢出标志V。这些条件标志位可以在EL0权限级修改。MRS/MSR指令用于访问NZCV寄存器:
“mrs xn, nzcv”用于读取条件标志;
“msr nzcv, xn”用于设置条件标志。
条件标志常常用于条件跳转,条件跳转指令根据条件标志进行跳转选择,参见表1-1,即如果条件满足就跳转到
表1-1 条件跳转指令
条件跳转指令B.cond
条件标志位
条件跳转指令B.cond
条件标志位
b.mi
N=1
b.hi
C=1且Z=0
b.pl
N=0
b.ls
C=0且Z=1
b.eq
Z=1
b.ge
N=V
b.ne
Z=0
b.lt
N!=V
b.hs
C=1
b.gt
Z=0且N=V
b.lo
C=0
b.le
Z=1且N!=V
b.vs
V=1
b.vc
V=0
3.异常屏蔽标志
DAIF寄存器是一个32位寄存器,设置异常屏蔽标志,包括调试异常屏蔽位D、系统错误屏蔽位A、中断IRQ屏蔽位C和快速中断FIQ屏蔽位F。如果某个异常掩码位设置为1,对应的异常事件就被飞腾CPU屏蔽。这些异常屏蔽位一般不允许在EL0权限级访问;在高权限级下,MRS/MSR指令用于访问DAIF寄存器。
图1-8 DAIF寄存器
图1-9 CurrentEL寄存器
4.权限级信息
CurrentEL寄存器是一个32位寄存器,描述了飞腾CPU当前所处的权限级,其中第2、3两位有效:0b00是EL0;0b01是EL1;0b10是EL2;0b11是EL3。该寄存器不允许在EL0权限级访问,在高权限级下是只读寄存器,只能用“mrs xn, CurrentEL”指令获取当前权限级。
5. 堆栈寄存器选择
一般说来,CPU会提供一个通用堆栈寄存器SP;因此当权限级切换时,软件需要对堆栈寄存器进行保存或恢复操作。为了优化这种由权限级切换导致的堆栈寄存器保存恢复操作,飞腾CPU分别为四权限级定义了64位堆栈寄存器SP_ELx(x=0/1/2/3)。
如果直接访问这四个堆栈寄存器SP_ELx,必须用MRS/MSR指令才能直接访问。直接访问这四个堆栈寄存器有最低权限级要求,参见表1-2。
表1-2 SP_ELx最低访问权限级
堆栈寄存器
直接访问要求的最低权限级
SP_EL0
EL1
SP_EL1
EL2
SP_EL2
EL3
SP_EL3
-
另外,也可以通过通用堆栈寄存器SP来访问相应的堆栈寄存器SP_ELx。在权限级EL0下,堆栈寄存器只能是SP_EL0; 权限级EL1下,堆栈寄存器可以是SP_EL0或者SP_EL1;权限级EL2和EL3的情况和EL1类似。
因此,飞腾CPU提供了堆栈选择寄存器SPSel,用来决定哪一个SP_ELx作为通用堆栈寄存器SP来使用。如果堆栈选择寄存器SPSel的第0位为1,在权限级ELx下对寄存器SP的访问就是对相应的SP_ELx访问; 如果堆栈选择寄存器SPSel的第0位为0,无论哪一个权限级,对堆栈寄存器SP的访问就是对SP_EL0访问。 Linux4.19操作系统内核实现中,当飞腾CPU处于EL0权限级时,堆栈寄存器SP是SP_EL0;在EL1权限级时,堆栈寄存器SP是SP_EL1。
6. 处理器状态保存
在发生异常后切换到新的权限级之前,飞腾CPU会将发生异常时的处理器状态和异常返回地址分别保存起来,这过程会用到两类寄存器:32位的保存处理器状态寄存器SPSR和64位的异常链接寄存器ELR。 寄存器SPSR并不描述飞腾CPU的当前状态,只是记录最近一次异常发生时的处理器状态。除了异常发生时的条件标志、异常屏蔽标志和权限级等信息,寄存器SPSR还包含了异常发生时处理器其他相关信息:字节序状态、32位/64位执行状态等信息。 下图1-10具体分析两个过程:某个应用程序执行系统调用SVC指令进入操作系统内核,以及操作系统内核处理完成后调用ERET指令返回应用程序。
图1-10 EL0和EL1权限级切换过程分析
飞腾CPU提供了3个异常链路寄存器ELR_ELx(x=1/2/3),当异常进入ELx权限级时,ELR_ELx就用于保存异常返回地址。飞腾CPU提供了七个SPSR寄存器,其中包含了三个SPSR_ELx(x=1/2/3)寄存器。
第四节 访存指令
1. 寻址方式
在AArch64执行模式下,飞腾CPU的Load/Store访存指令采用64位地址。访存指令包括单寄存器类型、双寄存器类型、独占类型、内存屏障类型、标量类型和向量类型。
访存指令的基地址是由64位的通用寄存器Xn或者堆栈寄存器SP来提供;在基地址基础上,访存指令还支持立即数偏移量或寄存器偏移量寻址。在基本的寻址功能基础上,飞腾CPU增加了基地址寄存器更新的能力,参见表1-3。
表1-3 基本寻址方式
寻址方式
格式
基地址寄存器Xn更新规则
寄存器基地址
[Xn]
不更新
[Xn], Xm
Xn+Xm
[Xn], #imm
Xn+立即数
寄存器基地址+立即数偏移量
[Xn, #imm]
不更新
[Xn, #imm]!
Xn+立即数
寄存器基地址+寄存器偏移量
[Xn, Xm]
不更新
[Xn, Xm, LSL #imm]
不更新
PC相对地址
label
注:1)“[Xn], Xm”的寻址方式仅限于SIMD Load/Store指令。
2)“[Xn, Xm, LSL #imm]”的寻址方式,偏移量为Xm左移imm位。
3)PC相对寻址的地址范围在1MB之内。
表1-4 常用单寄存器类型的访存指令
8位
符号8位
16位
符号16位
符号32位
32/64位
读指令
LDRB
LDRSB
LDRH
LDRSH
LDRSW
LDR
写指令
STRB
-
STRH
-
-
STR
注:1)只有LDR和LDRSW采用PC相对寻址方式,地址范围是±1MB。
2. 单寄存器类型
单寄存器类型的访存指令,用单个寄存器对一个内存单元进行访存操作,参见表1-4。下面介绍常规读指令相关的基本用法,参见表1-5。
表1-5 常规读指令的基本用法
数据长度
指令
说明
64位数据
ldr x1, [x0]
64位数据加载到寄存器x1。
32位数据
ldr w1, [x0]
32位数据加载到寄存器w1,
高32位清零。
ldrsw x1, [x0]
32位数据加载到寄存器x1的低32位,符号扩展到高32位。
16位数据
ldrh w1, [x0]
16位数据加载到寄存器w1低16位,
高48位清零。
ldrsh w1, [x0]
16位数据加载到寄存器w1的低16位,符号扩展到中间16位,高32位清零。
ldrsh x1, [x0]
16位数据加载到寄存器x1的低16位,符号扩展到高48位。
8位数据
ldrb w1, [x0]
8位数据加载到寄存器w1的低8位,
高56位全部清零。
ldrsb w1, [x0]
8位数据加载到寄存器w1的低8位,
符号扩展到中间24位,高32位清零。
ldrsb x1, [x0]
8位数据加载到寄存器x1的低8位,
符号扩展到高56位。
表1-6 双寄存器类型的访存指令
双寄存器类型指令
等价单寄存器类型指令
ldp w1,w2,[x0]
ldr w1, [x0, #0]
ldr w2, [x0, #4]
ldp x1,x2 [x0]
ldr x1, [x0, #0]
ldr x2, [x0, #8]
stp w1,w2,[x0]
str w1, [x0, #0]
str w2, [x0, #4]
stp x1,x2 [x0]
str x1, [x0, #0]
str x2, [x0, #8]
2. 双寄存器类型
双寄存器类型的访存指令,用两个寄存器对地址连续的两个内存单元进行访存操作,主要包括LDP、LDPSW和STP三条指令,参见表1-6。LDNP/STNP两条指令是面向流数据的双寄存器类型指令,因为流数据的局部性比较低,所以飞腾CPU对这种流数据所使用的高速缓存块内容优先替换出去。
3. 独占标记类型
独占标记类型的访存指令:读指令会把访问的物理地址标记上独占标记;写指令会在执行写操作之前检查独占标记是否完整。如果独占标记被破坏,就不执行并返回失败标志;如果独占标记完整,执行写操作,并释放独占标志,并返回成功标志。独占标记类型指令访问的地址要求地址对齐。
表1-7 独占标记类型的访存指令
8位
16位
32/64位
双寄存器
独占读
LDXRB
LDXRH
LDXR
LDXP
独占写
STXRB
STXRH
STXR
STXP
图1-11 ldxr/stxr指令对的原子操作
软件一般成对使用LDXR/STXR指令,软件成对使用LDXP/STXP指令可以实现“读后写”、信号量、共享内存、互斥锁和自旋锁等原子操作。由于飞腾CPU采用多核结构,这种原子性可能会失败,参见图1-11。下面是实现对内存单元的加原子操作,内存单元地址由寄存器x2给出,增加的值由寄存器w3给出,参见表1-8。
表1-8 加原子操作汇编实现
汇编实现
说明
1: ldxr w0, [x2]
从内存单元读数据到寄存器w0,同时将该内存单元打上独占标记。
add w0, w0, w3
寄存器w0增加w3
stxr w1, w0, [x2]
将增加后的w0写入内存单元,可能有两种情况
1)写成功,释放独占标记,寄存器w1返回0;
2)写失败,寄存器w1返回1。
cbnz w1, 1b
寄存器w1为非零时,表示写失败,就跳回到地址标签<1>处重新运行。
注:软件要确保,ldxr/stxr之间不能有访存指令操作或改变飞腾CPU状态的操作。
4. 内存屏障类型
为了提高CPU性能,飞腾CPU采用乱序和指令多发射的弱顺序性访存技术,这样会导致某些代码段执行结果的不确定性;为了避免弱顺序性访存的错误,处理器一般提供了内存屏障等同步指令。内存屏障类型的访存指令主要是保障了飞腾CPU访存的强顺序性,避免直接采用DMB指令等内存屏障指令。
表1-9 内存屏障类型的Load/Store指令
8位
16位
32/64位
Load-acquire指令
LDARB
LDARH
LDAR
Store-release指令
STLRB
STLRH
STLR
LDAR指令仅对该指令之后的访存指令有约束,要求在LDAR指令之后的所有访存指令,必须在LDAR指令完成之后才能执行。
STLR指令仅对该指令之前的访存指令有约束,要求在STLR指令之前的所有访存指令,必须在STLR指令执行之前全部完成。
图1-12 LDAR/STLR指令的临界代码
参见图1-12,最下面的普通访存指令序列可以推迟到STLR指令之前的任何时间执行;中间的普通访存指令序列只能在LDAR/STLR指令之间执行;最上面的普通访存指令序列可以提前到LDAR指令之后的任何时间执行。
5. 标量类型
标量类型访存指令包括常规单寄存器类型LDR/STR指令,无符号扩展类型的LDUR/STUR指令,基于双寄存器类型LDP/STP指令,和面向数据流类型的LDNP/STNP指令。
表1-10 内存屏障类型的Load/Store指令
8/16/32/64/128
标量
无符号扩展
类型
基于双寄存器
类型
面向数据流
类型
LDR
LDUR
LDP
LDNP
STR
STUR
STP
STNP
6. 向量类型
向量类型Load/Store指令主要包括四对常规指令LD1/ST1、 LD2/ST2、LD3/ST3和LD4/ST4。
第一种是单寄存器访存方式。这四对指令分别针对1、2、3或4组向量寄存器组中的一个寄存器进行访存操作,这个寄存器需要通过明确索引给出,参见图1-13。
图1-13 单寄存器访存方式
第二种是LD1/ST1的向量寄存器组访存方式,LD1/ST1指令可以利用1、2、3或4组向量寄存器组进行访存操作,参见图1-14。
图1-14 LD1/ST1向量寄存器组访存方式
第三种是LD2/ST2、LD3/ST3和LD4/ST4的向量寄存器组访存方式,可以分别利用2、3、4个寄存器组进行访存操作,参见图1-15。
图1-15 LD2/ST2、LD3/ST3和LD4/ST4向量寄存器组访存方式
最后,除了LD1/ST1、LD2/ST2、 LD3/ST3和LD4/ST4四对向量访存指令,飞腾CPU还提供LD1R、LD2R、LD3R和LD4R复制模式的向量寄存器组加载指令;针对一个向量寄存器组内的每一个寄存器,都加载同一个内存单元内容,参见图1-16。
图1-16 复制模式的向量寄存器组加载方式
第五节 高速缓存
1. 高速缓存模型
为了优化整体性能,飞腾CPU芯片内部提供L1高速缓存和L2高速缓存,有些飞腾CPU也提供芯片内部的L3高速缓存,本节不对这个情况进行赘述。靠近流水线的高速缓存具有低延迟,高代价的特点;离流水线更远的高速缓存,实现代价通常会低一些,容量通常会更大些,但是延迟也更长些。L1缓存延迟一般在几拍左右;L2缓存延迟一般是L1缓存延迟的2~3倍;内存墙延迟一般是L2缓存的10倍以上。
图1-17 飞腾CPU高速缓存模型
例如FT2000+/64服务器芯片,每个核具有各自独立的32KB的L1数据缓存和32KB的L1指令缓存。其中四个核为一个核簇,每个核簇共享2MB的L2缓存;全芯片64核一共有32MB的L2缓存。L2缓存是统一缓存,不区分指令和数据。而芯片内的处理核共享芯片内的L2缓存;对每个处理核而言,L2缓存块在硬件设计上保证数据一致性。
飞腾CPU的外部内存和L1/L2缓存是以缓存块为单位进行数据传输的,每个缓存块长度为64字节,L1缓存块是L2缓存块副本,L2缓存块是芯片外内存的副本,参见图1-18。虽然是副本,但飞腾CPU的硬件设计上并不保证,缓存块副本和原数据会时刻保持完全一致。
图1-18 缓存块示意图
表1-10 常用高速缓存常规指令
指令
说明
IC IALLUIS
IC IALLU
释放全部的指令缓存。
IC IVAU, Xn
释放寄存器Xn指定虚拟地址相关的指令缓存块。
DC IVAC, Xn
DC CIVAC, Xn
将寄存器Xn指定虚拟地址相关的数据缓存块更新到内存,然后释放该数据缓存块和L2缓存块。
DC CVAC, Xn
将寄存器Xn指定虚拟地址相关的数据缓存块更新到内存。
DC CVAU, Xn
将寄存器Xn指定虚拟地址相关的数据缓存块更新到L2缓存。
注:1)指令缓存指令后面要紧跟DSB指令,只有在DSB指令执行之后在硬件上才能确保指令缓存指令真正完成。
2)EL0权限级下,应用程序只能使用IC IVAU,DC CIVAC,DC CVAC和DC CVAU四条缓存指令。
2. 常规指令
参见表1-10,飞腾CPU提供的常规高速缓存指令分为:面向指令缓存的IC指令;面向数据缓存的DC指令。IC指令的invalidate操作主要是释放指令缓存块。DC指令有两类操作:clean操作主要是将L1数据缓存块内容更新到L2缓存块或者芯片外部内存中;invalidate操作主要是先执行clean操作然后将数据缓存块释放。
3. 预取指令
飞腾CPU为了提升性能,支持从外部内存中预取数据到L1或L2缓存中。如果某个缓存块内容不在L1/L2缓存中,当该缓存块的某个地址单元被第一访问时,就会遇到内存墙,即飞腾CPU会从内存中将整个缓存块内容传输到缓存中。软件不能直接感知这种预取机制,但内存墙会导致第一次访问该缓存块的访存指令延迟比较大。
飞腾CPU还提供专门的预取指令PRFM,用于第一次访问新数据之前就提前通知缓存去内存预取相关缓存块内容,来避免内存墙延迟。需要注意的是,这种预取指令PRFM只能用于系统主存类型,不能用于外设内存类型。
除了数据地址之外,预取指令PRFM还指定了类型,目标和策略三种基本参数。
表1-11 预期指令常规参数说明
参数
说明
类型
pld
为了load预期
pst
为了store预期
目标
l1
L1缓存
l2
L2缓存
策略
keep
常规缓存策略
strm
流数据缓存策略
注:1)指令“prfm pldl1keep [x0]”表示将缓存块从内存预读到L1缓存中,该缓存块会在缓存中保存一段时间。
2)指令“prfm pldl2strm [x0]”表示将缓存块从内存预读到L1缓存中,该缓存块会在缓存中访问完成后就可以失效了。
4. 内存属性
飞腾CPU支持的物理地址宽度为44位,物理地址空间实际上只有16TB。系统主存(芯片外部内存)和IO外设(例如SoC设备、PCIe设备)都在这个物理地址空间上统一编址。
飞腾CPU将系统主存和IO外设看作两种不同的内存类型。
针对系统主存的内存属性,飞腾CPU主要为了提升访存性能,因此主要支持弱顺序性访存模型,支持地址不对齐的访问方式,支持写操作合并,支持乱序访问机制。
在无缓存模式下,飞腾CPU可以忽略高速缓存直接读/写系统主存,NC和WT两种模式都采用无缓存模式来实现。在有缓存模式下,即WB模式,飞腾CPU读/写地址单元内容时,如果缓存不命中,就从系统主存中将相应的缓存块内容拷贝到高速缓存中;然后再对高速缓存的缓存块进行相应的操作。
针对IO外设的内存属性,为了确保外设操作的正确性,飞腾CPU支持强顺序性访存模型,不支持缓存模式和地址不对齐访问方式。这种强顺序性访存模型,根据强弱程度有nGnRnE、nGnRE 、nGRE和GRE四种类型。
G表示支持访存合并操作;nG表示不支持访存合并操作;
R表示支持乱序操作;nR表示不支持乱序操作;
E表示写操作的确认不一定来自目标设备;nE表示写操作的确认来自目标设备。
常用的外设内存类型是nGnRE,表示不支持访存合并和乱序操作,写指令完成时,设备内存不保证及时得到更新。
第六节 虚拟内存管理
1. 地址映射
程序员编程一般都是使用虚拟地址,无论是否用到高速缓存,飞腾CPU的内存管理单元MMU都会把虚拟地址翻译成物理地址,然后根据物理地址进行数据的实际访问操作。
内存管理单元MMU负责将虚拟地址翻译成物理地址,即虚拟地址到物理地址的映射。这种映射关系仅仅描述了从虚拟地址到物理地址的单向性映射关系;一个或多个虚拟地址可以同时映射到相同的物理地址上;而且这种映射是以页帧长度为粒度来处理的。
图1-19 虚拟地址和物理地址的映射
页帧长度的定义是根据程序和数据的局部性原理来定义的,一般说来常规页帧长度为4K、16K和64K。在服务器应用场景中64K页帧长度比较普遍,所以后面我们都以64K的页帧长度来讲解飞腾CPU的虚拟内存管理机制。
64位虚拟地址空间,分成三部分:空洞、内核地址空间和用户地址空间。空洞是内核地址空间和用户地址空间之间的隔离区。
Linux操作系统中的每个进程具有专属的、等长的用户地址空间,所有进程共享一个内核地址空间。
2. 页帧、页表和页表项
以页帧长度为粒度,将虚拟地址空间和物理地址空间进行逻辑切割。从虚拟地址到物理地址的映射关系就可以用从虚拟页帧号到物理页帧号的映射关系来表述。因此这种映射关系包含三部分内容:虚拟页帧号、物理页帧号和映射属性。每一个进程的用户地址空间内的所有虚拟页帧到物理页帧的映射关系都要进行描述,因此用户地址空间上的映射关系就形成一个映射关系数组,虚拟页帧号就是映射关系数组的索引号。
针对某个进程的整个用户地址空间而言,每一个虚拟页帧的映射关系就是一个页表项;所有虚拟页帧的映射关系数组就是该进程的用户地址空间页表。由于虚拟页帧号就是页表项在页表中的索引号 ,因此页表项只需要包含物理页帧号和映射属性。
针对64K的页帧长度,飞腾CPU采用三级页表方式来设计页表结构。本节不详细介绍三级页表结构,我们可以简单地理解为三维数组结构,一般情况下虚拟页帧号连续的无效页表项很多,因此三维数组具有稀疏性。采用三级页表结构主要为了节省内存资源。
ARMv8支持最大物理地址长度为48位,ARMv8.2可以支持最大52位,目前飞腾CPU支持44位物理地址。如果页帧长度为64K=216,物理页帧号最多需要52-16=36位。因此,飞腾CPU的每个页表项只需要64位,就可以包含物理页帧号和映射属性。
3. MMU原理
MMU的大致工作原理是:64位虚拟地址首先分成虚拟页帧号和页内偏移量两部分,虚拟页帧号即页表项索引;根据页表项索引,在进程的页表里查找到对应页表项,然后将页表项的物理页帧号和页内偏移量组合成物理地址,最后根据映射属性进行访存操作。
因为页表是软件预先设定在系统主存中的,如果每次地址翻译都要去访存才能获取页表项,这一定会严重降低系统性能。因此,MMU提供了页表项的高速缓存TLB。因此,可以简单地将TLB理解为页表项在高速缓存中的副本。当TLB失效时,MMU到内存页表中获取相应的页表项内容,并将其装载到TLB中;如果内存页表中的页表项是无效的(一般情况是无有效的物理页帧号),飞腾CPU就会触发页故障异常,页故障异常处理软件就会分配物理页帧,并将内存页表中的页表项完善,处理完成页故障异常后会重新执行寻址指令。
图1-20 飞腾CPU的MMU工作原理
飞腾CPU不提供对TLB写的操作指令,只提供TLB失效指令。TLB的写更新完全是硬件设计来实现,当MMU访问TLB没有命中时,
MMU就会在内存中的进程页表中查找相应的页表项,并将页表项内容更新到TLB中。飞腾CPU提供一条TLB失效指令TLBI,并提供多种操作选项,可以实现所有TLB失效,以及某个地址范围的TLB失效等操作。
4. 映射属性
MMU将虚拟地址翻译成物理地址后,会根据映射属性进行访存操作。映射属性包括访问控制和内存类型等属性。内存类型属性可以参考第五节高速缓存中的内存属性内容;因此,我们这里仅仅介绍EL0和EL1权限级相关的访问控制属性。
访问控制属性主要描述了相应的虚拟页帧是否允许读、写和执行等操作。“是否允许读/写”主要体现为针对Load/Store指令的数据访问控制上。“是否允许执行”主要体现为对指令的获取控制上。
表1-12 访问控制
数据访问控制
指令访问控制
AP[2:1]
EL1权限级
EL0权限级
UXN
PXN
EL1权限级
EL0权限级
00
读写
不可访问
0
0
可取指
可取指
01
读写
读写
0
1
可取指
不可取指
10
只读
不可访问
1
0
不可取指
可取指
11
只读
只读
1
1
不可取指
不可取指
飞腾CPU采用多级页表结构,上一级页表的页表项包括了下一级页表的物理地址和映射属性。上级页表项的映射属性会对下级页表项的映射属性进行约束。
表1-13 对一下级页表的限制
APTable[2:1]
对下一级页表访问控制属性的约束
00
不限制
01
EL0权限级不可访问
10
所有权限级不可写
11
所有权限级不可写,且EL0权限级不可读