目录
1,异常等级(Exception Level)
2,Execution states,执行状态
AArch64的异常等级
AArch32的异常等级:
3,异常等级切换
4,执行状态切换(AArch64 <=> AArch32)
5,状态切换后的寄存器状态
Registers at AArch32
PSTATE at AArch32
6,指令集的切换:Switching between the instruction sets
1,异常等级(Exception Level)
在ARMv8中,存在4级异常等级(Exception Level),程序需要跑在这四级异常等级中的一个。在AArch64下,异常等级近似于ARMv7中的特权等级(Privilege Level),异常等级定义了每个等级的特权等级,所以可以认为ELn 对应这 PLn。n (0到3)越大,说明特权等级越高。
对于适用于ARMv8架构的所有操作状态,异常级别提供了软件执行权限的逻辑分离。它类似于并支持计算机科学中常见的分层保护域(hierarchical protection domains)的概念。
以下是软件跑在每个异常等级下的用例:
EL0 | 普通的用户程序 |
EL1 | 操作系统内核,通常被描述为具有特权的程序 |
EL2 | 管理员,切换操作系统时可以进入该状态。比如需要从Win切到Linux,需要先进入EL2 Hypervisor模式,然后再切到EL1下的Linux。 |
EL3 | 底层硬件, 包括 Secure Monitor。 |
通常情况下,软件(应用程序,操作系统内核,或者hypervisor)只在某一个异常等级下工作。但是 虚拟机管理程序不适用于这个规则,比如KVM,或者VMware等内核虚拟机监控程序(in-kernel hypervisors),它们可以跨EL2和EL1工作,因为需要进入EL2来管理不同的虚拟机程序(切换操作系统)。
在ARMv8-A系列中,存在两种安全状态:Secure 和 Non-secure。Non-secure状态也可称为Normal World。Secure 和 Non-secure的区分使操作系统(OS)能够与受信任的操作系统(trusted OS)在同一硬件上并行运行,并提供针对某些软件攻击和硬件攻击的保护。
ARM TrustZone技术使系统之间可以被分成Normal world和Secure world。与ARMv7-A体系结构一样,Secure monitor充当在Normal world和Secure world之间移动的网关,即通过Secure monitor可以在Normal world和Secure world之间切换。ARMv8-A同样支持虚拟化,但是只针对Normal World,这意味着hypervisor或者是VMM(Virtual Machine Manager,虚拟机管理器)程序可以在系统中运行,并且可以管理多个 客户机操作系统(Guest OS)。每一个客户机操作系统都跑在一个虚拟机。然而,每个操作系统都不知道它正在与其他客户机操作系统共享系统上的时间 。
处于Non-secure状态下的Normal world,具有以下具有特权的组件:
- Guest OS kernels,客户机操作系统内核,比如Windows和Linux等,跑在Non-Secure EL1状态。当跑在hypervisor状态时,富操作系统内核(rich OS kernels)可以作为客户机或主机运行,具体取决于系统hypervisor程序模型。
- Hypervisor,跑在EL2状态,总是处于Non-Secure,虚拟化管理程序(hypervisor)在存在并启用时,为富操作系统内核提供虚拟化服务。
处于Secure状态下的Normal world,具有以下具有特权的组件:
Secure firmware,安全固件,在一个应用处理器上,这个固件必须是处理器在启动(boot)时,跑的第一个模块。它可以提供:平台初始化,受信任的操作系统的安装,以及Secure Monitor调用路由等服务。
Trusted OS,受信任的操作系统为Normal world提供安全服务,并为执行安全或受信任的应用程序提供运行时环境。
ARMv8架构中的Secure monitor处于更高的Exception级别,并且比所有其他级别拥有更多特权。这提供了一个软件特权的逻辑模型。
2,Execution states,执行状态
ARMv8架构定义了两种执行状态:AArch64和AArch32。AArch64状态使用64-bit宽的通用寄存器,而AArch32使用32-bit宽的通用寄存器。ARMv8 AArch32保留了ARMv7的特权级别,在AArch64状态,特权等级被描述为异常等级。因此,可以认为ELn对应PLn。
在AArch64状态下,处理器使用A64指令集。而在AArch32状态下,处理器可以使用A32或者T32(Thumb)指令集
AArch64的异常等级
AArch32的异常等级:
在AArch32状态下,Trusted OS软件执行在安全的EL3下,而在AArch64状态下,Trusted OS可以跑在Secure EL1下。
3,异常等级切换
在ARMv7体系结构中,处理器模式可以在具有特权的软件控制下改变,也可以在发生异常时自动改变。当发生异常时,处理器会保存当前执行状态和返回地址,进入所需模式,并可能禁用硬件中断。
- 应用程序执行在最低的特权等级,PL0,即无特权模式。
- 操作系统跑在PL1.
- 支持虚拟化扩展的系统中的Hypervisor跑在PL2.
- Secure monitor,充当secure和non-secure世界的网关,同样跑在PL1.
- SVC,当处理器进入reset(若默认启动状态为AArch32,则处理器启动后的默认异常等级为SVC),或者执行SVC指令时进入。
总结如下表,
-
用户模式(USR):ARM处理器正常的程序执行状态。
-
快速中断模式(FIQ):用于高速数据传输或通道处理。
-
外部中断模式(IRQ):用于通用的中断处理。
-
管理模式(SVC):操作系统使用的保护模式。
-
中止模式(ABT):当数据或指令预取终止时进入该模式,用于虚拟存储及存储保护。
-
未定义指令模式(UND):当未定义的指令执行时进入该模式,用于支持硬件协处理器的软件仿真。
-
系统模式(SYS):运行具有特权的操作系统任务。
-
Monitor(MON):执行SMC(Secure Monitor Call)指令,或者当处理器出现为安全处理而配置的异常时。
-
Hyp(HYP):当处理器出现为安全处理而配置的异常时。
从中也可以发现,只有HYP 模式处于PL2 异常等级(Non-secure only),只有USR模式处于PL0等级,其余模式都处于PL1:
在AArch64状态下,处理器的模式用异常等级表示,如下图所示,显示了AArch32的异常模式与AArch64的对应关系。
异常等级之间的切换,需要遵守一下规则:
-
移动到更高的异常等级,例如从EL0到EL1,表明增加了软件的执行权限
-
异常不能切换到更低的等级
-
EL0没有异常处理,异常只有在更高的异常等级(大于EL0)中才会被处理
-
异常会改变程序的正常执行流程,异常处理器(Exception handler)在大于EL0的异常等级下开始执行,从一个先前被定义好的,与该异常相关的异常向量开始,异常包括:
-
中断,比如 IRQ 和FIQ.
-
内存系统中止
-
未定义的指令。
-
系统调用。一些异常调用的指令允许不具有特权的软件,通过系统调用指令,进入操作系统权限。
-
Secure Monitor或者hypervisor 陷阱。
-
-
可以操作ERET指令来终止异常处理并且返回上一个异常等级
-
从异常返回可以保持相同的异常级别,或者变成较低的异常级别。但不能转移到更高的异常级别。
-
除非从EL3返回到非安全状态,否则安全状态不会随着Exception级别的更改而更改。(补充说明:这句话在官方的手册中的描述如下:“The security state does change with a change of Exception level, except when retuning from EL3 to a Non-secure state”. 如果硬翻这句话,那么就成了:“安全状态确实会随着异常级别的更改而更改,但从 EL3 返回为不安全状态时除外”。这样的翻译显然说不通,笔者的理解如下:EL3是sec-world和non-sec world之间的gateway, 也就是说sec 想变成 non-sec,一定要经过EL3这条路,也就是我的文章中所描述:除了通过EL3 返回到non-sec,安全状态是没办法随着Exception级别的更改而更改的(变成非安全状态)。
3.1 举例:EL1 sec如何变成 EL1 non-sec?
EL3是sec-world和non-sec world之间的gateway, 也就是说sec 想变成 non-sec,一定要经过EL3这条路。这同样可以说明跑在EL3下的管理程序为什么叫做 Secure monitor。如下图所示,为AArch64状态下的异常等级示意图:
根据这张图片,EL1 sec如何变成 EL1 non-sec ? 我们可以得出答案,先进入EL3 SEC,然后转成EL3 NSEC,最后通过ERET返回到EL1 NSEC:
- EL1 SEC => EL3 SEC
- EL3 SEC => EL3 NSEC
- EL3 NSEC => EL1 NSEC
这也是笔者上文所述:除非从EL3返回到非安全状态,否则安全状态不会随着Exception级别的更改而更改。
4,执行状态切换(AArch64 <=> AArch32)
用户有时候需要改变系统的执行状态,比如在一个64-bit的系统下,想跑一个在EL0下32-bit的应用程序。为了实现这种需求,系统需要切换到AArch32状态。当32-bit应用 程序执行完毕,或者执行返回到OS时,系统可以切换回AArch64。下图说明了AArch64的操作系统可以跑AArch32和AArch64的应用程序,反之不行。同样,AArch64的hypervisor支持AArch64和AArch32操作系统的切换,反之也不行。
要在同一Exception级别的执行状态之间进行更改,必须切换到更高级别异常级别,然后返回到原始异常级别。例如,用可能在64位操作系统下运行32位和64位应用程序。在这种情况下,32位应用程序可以执行并生成一个Supervisor Call (SVC)指令,或者接收一个中断,从而切换到EL1和AArch64。然后,操作系统可以在AArch64中执行任务切换并返回EL0。实际上,这意味着用户不能拥有32位和64位的混合应用程序,因为在它们之间没有直接调用的方法。
用户通过更改Exception级别来进行执行状态的切换。引发异常可能从AArch32更改为AArch64,从异常返回可能从AArch64更改为AArch32。
在EL3时,因为不能产生异常跳转到更高的异常等级,所以在EL3时不能切换执行状态(AArch32 -> AArch64)。除非采用warm resett的方式。
另一种执行状态切换的方式是 reset ,通过配置RMR寄存器,来配置reset后,处理器的执行状态,并产生一个warm reset。
以下是一些AArch32 和 AArch64状态之间切换的总结:
- AArch64和AArch32的执行状态都有异常级别,它们通常是相似的,但是在安全操作和非安全操作之间存在一些差异。生成异常时处理器所处的执行状态可以限制其他执行状态可用的exception级别。
- 从AArch 64 切换到 AArch32, 需要从 高 (higher) 异常等级 切换到 低 (lower)的异常等级。当 执行完 ERET 指令后,异常处理器 退出,状态将切换完成。
- 从 AArch32 切换到 AArch64,则需要从 低 (lower)异常等级 切换到 高 (higher)异常等级。这个异常可以是某些指令的执行结果,或者是一个外部信号。
- 如果,从一个异常切换到另一个异常,但是异常等级没有变,是相同的,这样是无法改变 执行状态的。
- 对于ARMv8处理器,在AArch32执行在某个异常级别,使用与ARMv7中相同的异常模型来处理进入该异常级别的异常。
因此,这两种执行状态之间的交互是在安全监视器(Secure monitor,EL3)、管理员程序(hypervisor,EL2)或操作系统级别(EL1)执行的。在AArch64状态下执行的hypervisor或操作系统可以在较低的权限级别上支持AArch32操作。这意味着一个操作系统正在运行AArch64可以同时承载AArch32和AArch64应用程序。类似地,AArch64 hypervisor可以同时托管AArch32和AArch64客户机操作系统。但是,32位操作系统不能承载64位应用程序,32位hypervisor不能承载64位客户机操作系统。
对于实现的最高异常级别(Cortex-A53和Cortex-A57处理器上的EL3),在获取异常时,每个异常级别使用的执行状态是固定的。异常级别只能通过重置处理器来改变。
5,状态切换后的寄存器状态
上文提到了执行状态切换的概念,下面将以寄存器为视角,讨论状态切换后,寄存器会发生哪些变化。
如果是从AArch32的异常等级切换到AArch64的异常等级:
- 之前在AArch32 任何更低的Exception级别下,可以访问的寄存器的高32位的值会变成UNKNOWN状态。
- 在AArch32执行期间不可访问的寄存器,将保留它们在AArch32执行之前的状态。
- 在EL3的异常入口时,之前一直使用AArch32 的EL2,其ELR_EL2的高32 bit的值也是未知的。
- 与异常等级相关的AArch64 Stack Pointers (SPs) and Exception Link Registers (ELRs)等寄存器,在之前AArch32状态下不可被访问,在该异常级别下,将保留它们在AArch32执行之前的状态。比如有:
- SP_EL0.
- SP_EL1.
- SP_EL2.
- ELR_EL1
通常情况下,对于应用开发,程序员只会单独考虑针对 AArch64或者 AArch32下的应用开发。只有对操作系统,才会考虑两种状态的结合以及之间的切换。
5.1 Registers at AArch32
事实上,AArch32就相当于 ARMv7,所以当使用AArch32状态时,使用的是ARMv7的32-bit通用寄存器。因此,在ARMv8架构和AArch32执行状态提供的视图之间必须有一些对应关系。
ARMv7架构具有15个通用寄存器(R0-R15),其中R0-R14用于数据存储,而R15则是相当于PC(program counter),保存着处理器即将执行的下一条指令的地址。软件也同样可以访问CPSR,还有SPSR。SPSR里保存的是来自先前的异常模式的CPSR的备份。在处理异常时,会将CPSR里的状态信息拷贝一份到当前异常模式下的SPSR,以便异常返回时,恢复上下文。
同样还有一个 banked 的概念:
这些寄存器中的哪个被访问,以及在哪里被访问,取决于软件正在执行的处理器模式和寄存器本身。(Which of these registers is accessed, and where, depends upon the processor mode the software is executing in and the register itself)
它们使用物理上不同的存储,通常只有在进程以特定模式执行时才能访问。下图中蓝色标记的寄存器即为banked寄存器。
ARMv7中使用banked功能来减少异常处理的延迟。然而,这也增加了寄存器的数量,也意味着在相当多的可能寄存器中,每次可以使用的寄存器不到一半。
相比之下,AArch64执行状态具有31 × 64位通用寄存器,可以在所有异常级别随时访问。执行状态在AArch64和AArch32切换,意味着AArch64寄存器必须映射到AArch32 (ARMv7)寄存器集。
在AArch32中执行时,AArch64寄存器的高32位是不可访问的。如果处理器在AArch32状态下运行,那它使用的是32位的W寄存器,它相当于32位ARMv7的 R 寄存器。
AArch32中的SPSR和ELR_Hyp寄存器是只能使用系统指令访问的附加寄存器。它们没有映射到AArch64架构的通用寄存器空间。其中一些寄存器在AArch32和AArch64之间映射如下:
- SPSR_svc maps to SPSR_EL1.
- SPSR_hyp maps to SPSR_EL2.
- ELR_hyp maps to ELR_EL2.
以下寄存器仅在AArch32执行期间使用。然而,由于在EL1上使用AArch64执行状态,尽管在此期间不可访问,它们仍保留其状态:
- SPSR_abt.
- SPSR_und.
- SPSR_irq.
- SPSR_fiq
SPSR寄存器只能在AArch64执行期间,在更高的Exception级别上访问,用于上下文切换。
同样,如果一个异常是从AArch32产生,并进入AArch64中,那么AArch64 状态下的ELR_ELn的前高32位都是零。
PSTATE at AArch32
在AArch64中,使用(PSTATE)来表示处理器当前的状态。在AArch32中,使用CPSR寄存器,并有额外的字段对应于ARMv7 CPSR位。
AArch32状态下的CPSR:
ARMv7架构下的CPSR:
并 提供额外的只能在AArch32下访问的PSTATE位,:
文章来源:https://www.toymoban.com/news/detail-542633.html
6,指令集的切换:Switching between the instruction sets
在单个应用程序中使用来自两个执行状态的代码是不可能实现的。在ARMv8中,A64和A32或T32指令集之间没有互通。用A64为ARMv8处理器编写的代码无法在上运行在ARMv7 Cortex-A系列处理器上。但是,为ARMv7-A处理器编写的代码可以运行在处于AArch32执行状态的ARMv8处理器上。以下是它们之间的关系:文章来源地址https://www.toymoban.com/news/detail-542633.html
到了这里,关于ARMv8的异常等级(Exception Level)以及执行状态(AArch64/AArch32)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!