图3 OS和APP的执行状态关系
EL3总是被认为是在安全状态下执行的。ARMv8-A架构里允许实现选择是否实现所有异常级别,并选择每个实现的异常级别允许执行状态。EL0和EL1是唯一必须实现的异常级别。EL2和EL3是可选的。
EL3是唯一可以更改安全状态的异常等级。如果实现选择不实现EL3,则处理器将无法支持两种安全状态。同样,EL2包含了许多虚拟化功能。如果不实现EL2,就不支持虚拟化特性。
在处理器具体实现中,还可以选择对每个异常等级有效的执行状态。如果在某个异常等级下支持AArch32,则必须在所有较低的异常级别下支持AArch32。
许多处理器实现支持所有执行状态和所有异常级别,但也有一些例外。例如,Cortex-A32只支持AArch32。例如,Cortex-A55支持所有异常等级,但只在EL0上支持AArch32,其他异常级别EL1、EL2和EL3必须是AArch64。
异常是任何可能导致当前执行程序被挂起并导致状态更改以执行代码来处理该异常的事件。在ARMv8-A架构中,中断(interrupt)是一种外部生成的异常。Armv8-A体系结构将异常分为两种广泛类型:同步异常和异步异常。
同步异常是由刚刚执行的指令引起的异常。也就是说,同步异常与指令流同步。尝试执行无效指令(当前异常等级不允许的指令或已禁用的指令)可能会导致同步异常。同步异常也可能是由内存访问引起的,这是由于地址未对齐或某个MMU权限检查失败。因为这些错误是同步的,所以可以在尝试访问内存之前执行异常。当然,内存访问还可以生成异步异常,下面会说。
ARMv8-A体系结构有一系列异常生成指令:SVC、HVC和SMC。这些指令与简单的无效指令不同,因为它们针对不同的异常等级,并且在对异常进行优先级排序时会受到不同的处理。这些指令用于实现系统调用接口,以允许特权较低的代码从更特权的代码请求服务。
某些异常是在外部产生的,因此与当前的指令流不同步。这意味着不可能确切的保证何时发生异步异常。ARMv8-A架构只要求它在有限的时间内处理。异步异常也可以临时屏蔽。
异步异常类型包括:
物理中断
•SError(系统错误)
•IRQ
•FIQ
虚拟中断
•vSError(虚拟系统错误)
•vIRQ(虚拟IRQ)
•vFIQ(虚拟FIQ)
IRQ和FIQ是用来产生外设中断的,它们有不同的路由控制。SError是为了响应错误的内存访问,而由内存系统生成的。比如,内存访问虽然通过了MMU检查,但是在总线上产生了错误。SError也可能是由于RAM的奇偶校验或ECC错误引起的。这些错误与指令流不是同步的关系。
当产生异常时,当前的程序被打断。处理器更新当前状态,切换到异常向量表(vector table),随后再切换到异常处理程序。
图4 异常处理流程
异常处理程序在内存中的存储位置称为异常向量。异常向量存储在一个表中,该表称为异常向量表。在Armv8-A中,处理器在系统寄存器中保存表的基址,并且每个异常类型都有一个定义好的基址偏移量。每个异常等级都有自己的向量表,由向量基地址寄存器VBAR_ELx定义,其中
是1、2或3。异常向量表的格式如下图:
图5 ARMv8-A异常向量表
在ARMv8-A中,可以通过指令触发异常,从而切换异常等级。
今天主要介绍的是ARMv8-A中的异常概念,其实跟SoC设计相关性并不明显,更多的是跟软件设计相关,但对于整体理解ARM架构还是有帮助的。