前言
ARMv8是ARM公司在2013年推出的架构,对于面向系统底层开发的工程师来说,准确理解ARM处理器内部的工作原理是至关重要的。
本书专注于陈述处理器的行为,而不会详细解释处理器内部的工作原理以及如此设计的原因。
第一章 简介
ARMv8有64-bit或32-bit两种运行状态,分别用AArch64和AArch32表示,其中AArch32的保留使v8架构向后兼容ARMv7。换句话说,基于ARMv7架构编写的代码可以在v8架构上运行,但是反过来不行。
Cortex-A系列处理器以及它们所属的架构如下所示:
ARMv7-A | ARMv8-A |
---|
Cortex-A5, Cortex-A7, Cortex-A8, Cortex-A9, Cortex-A15, and Cortex-A17 | Cortex-A53, Cortex-A57 and Cortex-A72 |
从ARMv7的32位升级到v8架构的64位所带来的好处有:
- A64指令集可以带来显著的性能提升,它提供了一个更大的寄存器池(register pool)。这些相比于之前的ARMv7多出来的寄存器可以用于函数之间的传参,使最大形参数量从原来的4个提高到8个,减少了堆栈操作,提高效率。
- 提高了64位数据进行算数运算的速度。
- 64位的操作允许使用更大的虚拟内存空间,从原来的32bits(4GB)拓展到了40bits。这样所带来的好处是可以处理更大体积的文件,即使我们没有足够大的物理内存空间,可以通过虚拟内存对文件内容进行映射。
- 当然,64bits的位宽也会带来一些负面影响:①指针长度的增加会产生额外的内存空间消耗。②可访问内存空间的增加会导致cache的命中率降低,降低运行效率。
第二章 ARMv8-A 架构和处理器
上图为Cortex-A53处理器的结构示意图,这是一款中规格,主打低功耗、高效率的处理器架构,最多封装4个核心协同工作。
上图为Cortex-A57架构处理器的示意图,这是ARMv8架构的全规格处理器,最多封装4个核心在一个处理器中,支持CoreSight调试技术。使用无序,15+级流水线,对L2 Cache进行优化,何以使多个核心同时访问。
第三章 ARMv8基础
使用ARMv8架构的处理器运行时,会在4个异常模式中切换。在AArch64中,异常模式的级别决定了优先级,也就是说ELn(Exception Level)对应于PLn(Privilege Level)。n的数值越大,异常等级越高。
异常等级 | 运行状态 |
---|
EL0 | 正常用户应用程序所使用的等级 |
EL1 | 操作系统内核专用 |
EL2 | 超级管理员模式 |
EL3 | 底层固件(包括安全监视器),所使用的异常等级 |
总而言之,一段代码在运行时,无论这段代码是属于普通应用程序还是系统内核又或是超级管理员,只能占据一个异常等级。但是核内超级管理员(in-kernal hypervisors)是一个例外,例如KVM(kernal-based virtual machine),它在EL2和EL1异常等级中穿插运行。
3.1 运行域
ARMv8-A提供安全和非安全(Secure and Non-secure)两种运行域(World)。其中,非安全域也被认为是正常域(Normal World)。在正常域中,可以允许另外一个操作系统与可信操作系统(trusted OS)在同一套硬件平台上并行运行,并且提供特定的软硬件攻击保护。ARM TrustZone 技术允许操作系统在正常域和安全域之间分区。和ARMv7-A架构一样,安全监视器(Secure monitor)充当正常域和安全域间转移门户的角色。如下图所示:
ARMv8-A在正常域(Normal World)中提供对虚拟化的支持,这意味着CPU可以运行超级管理员或虚拟机管理器(VMM,Virtual Machine Manager)的相关代码,并且支持多用户操作系统。
在多用户操作系统中,每一个用户的操作系统本质上都是虚拟机,但每一个虚拟机操作系统都不会察觉到正在和其他的系统共享资源。
- 正常域,由如下几种程序可以运行在特权模式(PL0不属于特权模式):
- 用户系统内核(Guest OS Kernal):这类内核包括运行在EL1异常等级的Linux或Windows内核。当它们的异常等级低于超级管理员(EL2),多个系统内核可以运行在客户端模式或主机模式,这取决于超级管理员的调度。
- 超级管理员:该状态的异常等级为EL2,通常是非安全的。当使能并运行在超级管理员模式时,可以向多个系统内核提供虚拟化服务。
- 安全域(Secure World)
- 安全固件(Secure firmware):在处理器上电时,处理器固件是首先被运行的。固件可以提供诸多服务,包括硬件平台的初始化,可信操作系统(Trusted OS)的启动以及安全监视器的调度。
- 可信操作系统:可信操作系统为正常域提供安全服务,并且为执行安全(executing Secure)和可信应用(trusted applications)提供实时运行环境。
安全监视器(Secure monitor)在ARMv8架构中享有最高异常等级,和最大优先级,它掌管软件的优先级调度逻辑。
3.2 运行状态(Execution states)
ARMv8架构定义有AArch64和AArch32两个运行状态,这两个状态分别使用64bits位宽的通用寄存器或是32bits位宽的通用寄存器。为了向后兼容,AArch32仍然保持ARMv7的优先级定义,但在AArch64中优先级与异常等级一致,即ELn对应于PLn。
当运行在AArch64状态时,处理器使用A64指令级;当运行在AArch32状态时,处理器既可以执行A32(原ARM指令集)指令级也可以执行T32(Thumb)指令级。
通过对比可以发现,在AArch32中,可信操作系统的软件执行在EL3中而在AArch64状态下则是运行在EL1中。
3.3 改变异常等级
在ARMv7架构中,处理器模式可以在特权软件的控制下改变或是在异常发生时自动改变。当一个异常出现时,CPU内核会保存异常状态并且返回当前的运行地址,进入到需要的模式,并且有可能禁止硬件中断。
用户应用程序的优先级最低,PL0,也就是之前的非特权模式;操作系统内核在PL1优先级运行;在一个启用虚拟化拓展的系统中,超级管理员(管理程序)的运行优先级是PL2;安全监视器作为安全域和正常域之间转换的门户,也运行在PL1优先级(这里指的是ARMv7)。对应的表格描述如下:
在ARMv8,AArch32架构中,处理器模式与异常等级之间的映射关系如下图所示:
当异常发生时,处理器会改变到对应的异常等级来处理发生的异常。
异常之间的跳转遵循以下原则:
-
当异常等级提高时,例如从EL0到EL1,同时也意味这软件执行优先级的提高。
-
任何异常都不能降级到比原来更低的异常级别。
-
异常会改变原来程序的执行顺序,当大于EL0的异常发生时,一个异常句柄(handler)将会随即启动,该句柄的地址由中断向量表进行映射。
这类异常包括:
- 中断请求(IRQ)以及快速中断请求(FIQ)
- 存储系统终止(Memory System Abort)
- 未定义指令
- 系统级调用(System calls),该异常可以允许非特权(PL0)向操作系统程序发起系统及调用请求。
- 安全监视器或超级管理员(管理程序)陷阱
-
结束异常处理,返回到之前的异常等级的动作是通过执行ERETERETERET指令完成的。
-
从一个异常返回时,可以继续停留在该等级或进入一个较低的异常等级。不能在异常返回时进入一个更高的异常等级。
-
安全域会随着异常等级的改变而改变,除了从EL3返回到正常域的情况。
3.3 改变运行状态
有时候我们不得不改变系统的运行状态。例如我们想在一个64位的操作系统中运行异常等级为EL0的32位的应用程序,为了实现这个目的,CPU必须从AArch64进入到AArch32运行状态。
当应用程序运行结束或交还CPU使用权给操作系统,CPU则必须从AArch32切换回到AArch64。但是需要注意,一个AArch32的操作系统不能切换到AArch64执行64位的应用程序。
为了改变CPU的运行状态并维持切换前后的异常等级不变。必须先切换到更高的异常等级然后再回到原本的异常等级。例如,我们的应用项目中同时有32位和64位的应用程序,在64位操作系统的环境下运行。在这种情况下,32位的应用程序可以产生、执行一个超级管理员调度(Supervisor Call,SVC)指令,或是收到一个中断,使异常等级升高到EL1,完成从AArch32到AArch64的切换。之后操作系统可以进行任务切换,回到AArch64运行模式下的EL0异常等级。从实际操作的角度,这意味着我们不能运行一个混合了64bits和32bits指令的应用程序,因为这些指令之间无法相互调用。
总结:我们通过触发异来实现运行状态之间(AArch32和AArch64之间)的相互转换。
另外,执行在EL3等级的代码无法通过异常来到更高的异常等级,所以也就无法从该异常等级改变运行状态,除非进行复位。
下面是在AArch32和AArch64运行状态之间进行转换的要点总结:
- AArch32和AArch64的执行状态有大致类似的异常等级,但是在安全域和非安全域的划分上稍有区别。异常发生时,处理器所处的运行状态会对另一运行状态的可用异常等级有所限制。
- 切换到AArch32需要从一个更高的异常等级切换到较低的异常等级,这是由执行异常句柄过程中的ERETERETERET指令导致的。
- 从AArch32切换回到AArch64需要从一个较低的异常等级升高到较高的异常等级,触发异常等级切换的异常事件可以是外部信号也可以是指令的执行。
- 如果处理异常事件或从异常事件中返回时,异常等级没有发生改变,运行状态不会发生改变。
- 当CPU运行在AArch32状态时,异常状态的切换逻辑同ARMv7保持一致。当运行在AArch64状态时,异常的切换逻辑参阅第十章。
两个运行状态的相互配合发生在安全监控器,超级管理员或操作系统层面。一个运行在AArch64超级管理员(管理程序)或操作系统可以支持较低优先级的AArch32操作。这意味着,一个运行在AArch64状态的操作系统可以支持运行在AArch32以及AArch64的应用。类似的,一个AArch64的超级管理员(管理程序)可以支持AArch32以及AArch64的操作系统。但是反过来,一个32位的操作系统不能兼容64位的应用程序,同样,32位的超级管理员(管理程序)也不能兼容64位的操作系统。
对于最高的应用程序异常状态EL3,当异常发生时,用于每一个异常等级的运行状态是一定的。异常等级只有在处理器复位后才能改变。对于EL2和EL1,运行状态由系统寄存器决定。