异常处理指的是处理器在运行过程中发生了外部事件,导致处理器需要中断当前执行流程转而去处理异常事件的一种机制。在Intel处理器的术语中,中断与异常被分开来描述,但在ARMv8体系结构中,异常和中断统一被称为异常处理。
在ARMv8体系结构中,广义上的异常可以分成同步异常和异步异常两种,其中:
ARMv8体系结构的异常来源于特定的几类事件,分别为中断、终止(Abort)、复位和系统调用。
在ARM64处理器中,中断请求分成普通中断请求(Interrupt Request, IRQ)和快速中断请求(Fast Interrupt Request, FIQ)两种。其中,FIQ的优先级要高于IRQ。在芯片内部,分别有连接到处理器内部的IRQ和F1Q两根中断线。通常系统级芯片内部会有一个中断控制器,众多 的外部设备的中断引脚会连接到中断控制器 ,由中断控制器负责中断优先级调度,然后发送中断信号给ARM处理器。
终止主要有指令终止(instruction abort)和数据终止(data abort)两种。它们通常是指访 问内存地址时发生了错误(如缺页等),处理器内部的MMU捕获这些错误并且报告给处理器。 指令终止是指当处理器尝试执行某条指令时发生了错误,而数据终止是指使用加载或者存 储指令读写外部存储单元时发生了错误 。
复位(reset)操作是最高特权等级的异常,并且不能被屏蔽。复位操作通常用于让CPU复位引脚产生复位信号,让CPU进入复位状态,并重新启动。
系统调用允许应用程序通过特殊指令提升运行时的特权等级或请求高异常等级的程序所提供的服务。ARMv8定义的系统调用指令包括以下三类:
ARMv8处理器执行程序时,只有进入异常处理或者从异常返回时才能够切换异常等级。进入异常处理时,异常等级可以保持不变或者提升,但不允许降低;相反,从异常返回时,异常等级可以保持不变或者降低,但不允许提升。
当一个异常发生时,CPU会感知到异常,并跳转到目标异常等级执行异常处理。在进入异常处理时,CPU会自动执行以下操作:
异常向量表通常由操作系统在启动时进行设置,异常向量表的每个项都会保存一条跳转指令,CPU根据异常类型跳转到恰当的异常处理函数并处理异常。
当操作系统的异常处理完成后,执行ERET指令即可从异常返回。ERET指令会自动完成如下工作:
ARMv8处理器使用ELR_ELx寄存器存放异常返回的地址,即发生异常那一瞬间的地址,它可能是在用户空 间中扇地址,也可能是在内核空间中的地址,不管它在哪个空间,执行ERET指令就可以返回异常现场。
既然ELR_Elx保存了异常返回地址,那么这个返回地址是指向发生异常时的指令还是下一条指令呢?我们需要区分不同的情况:
在ARMv8体系结构中,每个异常等级都有对应的栈指针(SP)寄存器,记为SP_ELx。当CPU运行在任何一个异常等级时,可以配置SP使用SP_EL0或者对应等级的SP_ELx寄存器。
ARMv8通过SPSel寄存器来配置异常等级使用的SP寄存器,当SPSel寄存器中的SP字段设置为0表示在所有的异常等级中,使用SP_EL0作为SP寄存器;设置为1表示使用SP_ELx作为SP寄存器。
异常向量表用于存储异常发生时需要执行处理程序的地址,对于ARM64处理器的异常等级EL1、EL2和EL3都有自己的异常向量表。每个异常向量表有16项,每项的长度是128字节,可以存放32条指令。对于异常向量表的每一项定义如下:
每张异常向量表都可以分为4组,每组包含4项,依次对应同步异常、IRQ、FIQ和系统错误这四种异常的处理入口。对于异常向量的选择,则取决于异常发生的异常等级、异常将使用的堆栈指针以及所处的执行状态(AArch64或AArch32)等因素,具体来说
ARMv8体系结构提供了一个向量基址寄存器(Vector Base Address Register)VBAR_ELx寄存器来设置异常向量表的地址。
ARMv8体系结构中有一个与访问失效相关的寄存器——异常综合信息寄存器(Exception Syndrome Register, ESR )。
当异常发生时,软件通过读取ESR_ELx以知道当前发生异常的类型,然后再解析ISS字段。不同的异常类型有不同的ISS编码,需要根据异常类型解析ISS字段。