本文
主要介绍AArch64的一些异常基础知识,异常向量表的设定,以及AArch64的异常处理。
版本 | 说明 |
---|---|
0.1 | 初版发布 |
关于异常的基础知识
什么是异常?
对于AArch64而言,exception是指cpu的某些异常状态或者一些系统的事件(可能来自外部,也可能来自内部),这些状态或者事件可以导致cpu去执行一些预先设定的,具有更高执行权利的软件(也叫exception handler)。执行exception handler可以进行异常的处理,从而让系统平滑的运行。exception handler执行完毕之后,需要返回发生异常的现场。
异常等级(exception level)
AArch64有四个 exception level:
- EL0 normal用户应用程序
- EL1 操作系统内核
- EL2 Hypervisor(vm 虚拟化)
- EL3 底层固件,包括secure monitor
之前说到异常发生后系统将切换到具有更高执行权限的状态,在AArch64是通过exception level来实现的。AArch64最多支持EL0~EL3四个exception level,EL0的execution privilege最低,EL3的execution privilege最高。当发生异常的时候,系统的exception会迁移(route)到更高的exception level或者维持不变,但是绝不会降低。此外,不会有任何的异常会去到EL0。而AArch32,cpu没有异常等级,而是采用processor mode模式,例如User、FIQ、IRQ、Abort、Undefined、System,这些不同的mode对应privilege(其他mode)和no-privilege(User mode)。
关于AArch64的exception level比较复杂,一般会有如下几种情况:
- 不支持security state,不支持虚拟化: AArch64有2个exception level,分别是:EL0(对应user mode的application),EL1(guest OS)。
- 不支持security state,支持虚拟化: AArch64有3个exception level,分别是:EL0(对应user mode的application),EL1(guest OS)和EL2(Hypervisor)。
- 支持security state,不支持虚拟化: AArch64有3个exception level,分别是:EL0(对应trusted service),EL1(trusted OS kernel)和EL3(Secure monitor)。
- 支持security state,支持虚拟化: AArch64有4个exception level,分别是:(对应trusted service),EL1(trusted OS kernel),EL2(Hypervisor)和EL3(Secure monitor)。
如下图:
异步异常和同步异常
-
异步异常(asynchronous exception):
- 异步异常可以理解为中断,CPU是不可预知的。
- 异常和CPU执行的指令无关。
- 返回地址是硬件保存下来并提供给handler,以便进行异常返回现场的处理。(程序也有可能不返回,跟中断处理程序有关,比如正在发送邮件,突然点击取消发送,原发送程序不再执行【个人理解,不一定准确】)
- 根据这个定义IRQ、FIQ和SError interrupt属于asynchronous exception。
-
同步异常(synchronous exception):
- 同步异常的产生是和cpu core执行的指令异常或试图改变执行权限引起的异常
- 返回地址是硬件保存下来并提供给handler,以便进行异常返回现场的处理。
- 同步异常分两种,一是abort类,例如未定义的指令、data abort、prefetch instruction abort、SP未对齐异常,debug exception等等。另一种是正常指令执行造成的,包括SVC/HVC/SMC指令,这些指令的使命就是产生异常,改变执行权限。
什么是精确异常
先来说说什么是非精确异常:在多发射乱序执行的流水线 CPU 上,从指令进入流水线到异常事件的发生,期间要经过若干流水级,此时 PC 的值已指向其后的某条指令,在实现非精确异常的 CPU 上就把此时的 PC 值作为引起异常指令的所在,也就是记录异常指令的PC并非真正的引起异常的指令所在,而是其后面的某条指令所在。
精确异常(precise exception),也就是记录异常指令的PC并非真正的引起异常的指令所在,而是其后面的某条指令所在。实现精确异常的 CPU,在最后指令提交时 (commit) 按指令流的顺序提交,异常的抛出也在该指令提交时,这样就能精确计算出引起异常的指令相对于当前 PC 的偏移,从而保证精确异常。不管是何类异常,记录异常指令的PC之前的所有指令都会被执行完成 (commit),之后的指令不会被执行。
在AArch64中,除了SError interrupt这种exception,其他的exception都是precise exception。
异常处理流程
AArch64 state异常处理流程:
流程 | 说明 |
---|---|
1、保存PSTATE 数据到SPSR_ELx,(x = 1,2,3) | 异常返回时需要从SPSR_ELx中恢复PSTATE |
2、保存异常进入地址到ELR_ELx,同步异常(und/abt等)是当前地址,而异步异常(irq/fiq等)是下一条指令地址 | 64位架构LR和ELR是独立分开的,这点和32位架构有所差别 |
3、保存异常原因信息到ESR_ELx | ESR_ELx.EC代表Exception Class,关注这个bit |
4、PE根据目标EL的异常向量表中定义的异常地址强制跳转到异常处理程序 | 跳转到哪个EL使用哪个向量偏移地址又路由关系决定 |
5、堆栈指针SP的使用由目标EL决定 | (SPSR_ELx.M[0] == 1) ? h(ELx): t(EL0) |
AArch32 state异常处理流程
流程 | 说明 |
---|---|
1、PE根据异常类型跳转到对应的异常模式x,x = {und/svc/abt/irq/fiq/hyp/mon} | PE跳转到哪一种模式通常由路由关系决定 |
2、保存异常返回地址到LR_x,用于异常返回用 | LR也是对应模式的R[14]_x寄存器,32位系统下LR和ELR是同一个寄存器,而64位是独立的 |
3、备份PSTATE 数据到SPSR_x | 异常返回时需要从SPSR_x恢复PSTATE |
4、PSTATE 操作:PSTATE.M[4:0]设置为异常模式x; PSTATE.{A,I,F} = 1; PSTATE.T = 1,强制进入A32模式; PSTATE.IT[7:2] = “00000” | PSTATE.M[4]只是对32位系统有效,64为下是保留的,因为64位下没有各种mode的概念. 异常处理都要切换到ARM下进行;进入异常时需要暂时关闭A,I,F中断; |
5、据异常模式x的向量偏移跳转到进入异常处理 | 各个mode有对应的Vector base addr + offset |
AArch64异常向量表偏移量
exception level迁移情况 | Synchronous exception的offset值 | IRQ和vIRQ exception的offset值 | FIQ和vFIQ exception的offset值 | SError和vSError exception的offset值 |
---|---|---|---|---|
同级exception level迁移,使用SP_EL0。例如EL1迁移到EL1 | 0x000 | 0x080 | 0x100 | 0x180 |
同级exception level迁移,使用SP_ELx。例如EL1迁移到EL1 | 0x200 | 0x280 | 0x300 | 0x380 |
ELx迁移到ELy,其中y>x并且ELx处于AArch64状态 | 0x400 | 0x480 | 0x500 | 0x580 |
ELx迁移到ELy,其中y>x并且ELx处于AArch32状态 | 0x600 | 0x680 | 0x700 | 0x780 |
注:每个异常等级都有一个关联的 Vector Base Address Register (VBAR),它定义了每个异常等级向量表的基址
AArch64异常返回:
- 用ELR_ELx恢复PC值
- SPSR_ELx恢复PSTATE值
AArch64异常相关寄存器
EL3异常层级系统寄存器控制:
SCR_EL3 | SCTLR_EL3 | MDCR_EL3 |
---|---|---|
NS:决定了EL1和EL0的安全状态; | {A, SA}:使能对齐检查,A-EL3访问数据时做对齐检查;SA-EL3对SP做对齐检查; | {EPMAD, EDAD}:使能外部debugger访问; |
RW:决定了低一级的异常等级运行状态; | {M, C, I, WXN}:内存系统控制位; | {SPME, SDD, SPD32}:安全debug控制; |
{EA, FIQ, IRQ}:EA-SError和同步Aborts切换到EL3;FIQ-物理FIQ切换到EL3;IRQ-物理IRQ切换到EL3; | EE:定义了端; | {TDOSA, TDA, TPM}:陷阱控制 |
SMD:禁用SMC; | ||
HCE:使能Hypervisor call异常; | ||
ST:使能secure EL1访问secure timer; | ||
SIF:安装指令获取,当secure state禁止从non secuer内存取指; | ||
TWI:陷入WFI; | ||
TWE:陷入WFE |
EL2异常层级系统寄存器控制:
HCR_EL2 | SCTLR_EL2 | MDCR_EL2 | HSTR_EL2 |
---|---|---|---|
RW:决定了低一级的异常等级运行状态; | {A, SA}:使能对齐检查 | {TDRA, TDOSA, TDA} | Tn, for values of n in the set {0-3, 5-13, 15} |
{AMO, IMO, FMO}:路由物理中断到EL2 | {M, C, I, WXN}:内存系统控制位; | TDE:路由从非安全EL0 EL1 EL2来的debug异常 | |
{VSE, VI, VF}:设置虚拟中断pending; | EE:定义了端; | {TPM, TPMCR}:陷阱控制; | |
VM: | HPMN: | ||
{SWIO, PTW, FB, BSU, DC, CD, ID}: | |||
HCD Hypervisor Call Disable | |||
{TRVM, TDZ, TVM, TTLB, TPU, TPC, TSW, TACR, TIDCP, TSC, TID1, TID2, TID3, TWE, TWI}: | |||
TGE: Trap General Exceptions |
EL1异常层级系统寄存器控制:
TLR_EL1 | MDSCR_EL1 |
---|---|
{A, SA}:使能对齐检查 | {MDE, SS} |
{M, C, I, WXN}:内存系统控制位; | KDE: |
EE:定义了端;E0E:EL0端;UMA:非特权mask访问 | TDCC: |
{SED, ITD, CP15BEN}: |
同步异常
同步异常类型
异常类型 | 描述 |
---|---|
Undefined Instruction | 未定义指令异常 |
Illegal Execution State | 非法执行状态异常 |
System Call | 系统调用指令异常(SVC/HVC/SMC) |
Misaligned PC/SP | PC/SP未对齐异常 |
Instruction Abort | 指令终止异常 |
Data Abort | 数据终止异常 |
Debug exception | 软件断点指令/断点/观察点/向量捕获/软件单步 等Debug异常 |
同步异常迁移
如果HCR_EL2.TGE为1,None-secure EL0下的异常不会传递给None-secure EL1,而是直接传递给EL2
异步异常
异步异常类型
类型 | 描述 |
---|---|
SError or vSError | 系统错误类型,包括外部数据终止 |
IRQ or vIRQ | 外部中断 or 虚拟外部中断 |
FIQ or vFIQ | 快速中断 or 虚拟快速中断 |
异步异常迁移
这里描述的是el2和el3都实现的情况:
- 若SCR_EL3.{EA, FIQ, IRQ} == 1,则所有相应的SError\FIQ\IRQ 中断都被路由到EL3;
- 若HCR_EL2.{AMO, IMO, FMO} == 1,则EL1/EL0所有对应的SError\FIQ\IRQ中断都被路由到EL2,同时使能对应的虚拟中断VSE,VI,VF;
- 若HCR_EL2.TGE == 1,那么会忽略HCR_EL2.{AMO, IMO, FMO}的具体值,直接当成1处理,则EL1/EL0所有对应的SError\FIQ\IRQ中断都被路由到EL2,同时禁止所有虚拟中断
注意: SCR_EL3.{EA, FIQ, IRQ}bit的优先级高于HCR_EL2.{AMO, IMO, FMO} bit优先级,路由优先考虑SCR_EL3
文章原创,可能存在部分错误,欢迎指正,联系邮箱 cao_arvin@163.com。