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