Blog
Blog copied to clipboard
aarch64体系结构与编程6--ARM64的异常处理(Exception Model)
ARM64的异常等级
ARM64异常等级:
- EL0:非特权模式,例如应用程序
- EL1:特权模式,例如OS内核
- EL2:虚拟化监控程序,例如hypervisor
- EL3:安全模式,例如secure monitor
ARM64异常的一些基本概念
- Taking an exception:正在处理一个异常
- Returning from an exception:从一个异常中返回
- Exception levels:异常等级
- Precise exception:精准异常
- Synchronous and asynchronous exception:同步异常与异步异常
同步异常和异步异常
同步异常:
- 系统调用:svc,hvc,SMC等
- MMU引发的异常
- SP和PC对齐检查
- 未分配的指令
异步异常:
- IRQ中断
- FIQ中断
- SError
异常入口(Exception entry)
当异常发生时刻,CPU硬件做了哪些事情?
- PSTATE保存到SPSR_ELx
- 返回地址保存ELR_ELx
- PSTATE寄存器里的DAIF域都设置为1,相当于把调试异常、系统错误(SError)、IRQ中断以及FIQ中断都关闭了
- 更新了ESR_ELx寄存器,里面包含了同步异常发生的原因
- SP切换到SP_ELx
- 切换到对应的EL,然后跳转到异常向量表里执行
当异常发生后,操作系统需要做哪些事情?
操作系统需要做事情:根据异常发生的类型,跳转到合适的异常向量表。 异常向量表的每个表项会保存一个异常处理的跳转函数,然后跳转到恰当的异常处理函数并处理异常。
异常返回
操作系统执行一条eret语句, 硬件会做如下事情:
- 从ELR_ELx寄存器中恢复PC指针
- 从SPSR_ELx寄存器恢复处理器的状态
异常返回地址
返回地址两个寄存器:
- X30:子函数的返回地址。使用ret指令来返回
- ELR_Elx:异常返回地址。使用eret指令来返回 ELR_Elx寄存器保存了异常返回地址:
- 对于异步异常,它的返回地址是中断发生时的下一条指令,或者没有执行的第一条指令
- 对于不是system call的同步异常,返回的是 触发同步异常的那一条指令
- 对于system call,它是返回svc指令的下一条指令
异常处理的路由
- 异常发生的时候,异常处理可以在当前EL也可以在更高的EL
- EL0不能用来处理异常
- 同步异常是可以在当前的EL里处理的,比如在EL1里发生了同步异常
- 对于异步异常,可以路由到EL1, EL2, EL3处理,需要配置HCR以及SCR相关寄存器
栈的选择
- 每个异常等级EL都有对应栈指针寄存器SP(SP_EL0,SP_EL1,SP_EL2,SP_EL3)
- 栈必须16字节对齐。硬件可以检测栈指针是否对齐
- 当异常发生时,并跳转到目标异常等级时,硬件会自动选择SP_ELx
- 操作系统负责分配和保证 每个异常等级EL对应的栈,是可用的
异常处理的执行模式
- 当异常发生时,切换到高级别的EL,这个EL运行在哪个模式?Aarch64 or aarch32?
- HCR_EL2.RW记录了 EL1要运行在哪个模式? 1:表示aarch64, 0:表示aarch32
- 当异常发生后,执行模式可以发生改变 一个aarch32位的应用程序正在运行,这时候来了一个中断,它可能会跑到 aarch64执行状态下的 EL1里处理 这个中断
异常返回的执行模式
从一个异常返回是,SPSR寄存器记录了:
- 返回到哪个EL? SPSR.M[3:0]
- 返回目标EL的执行模式?SPSR.M[4]
- M[4] = 0,表示aarch64;M[4] = 1,表示aarch32
See armv8.6手册的第D1.6.4章
异常向量表(Exception vectors)
- 每个异常等级EL都有自己的异常向量表,EL0除外
- 异常向量表的基地址需要设置到VBAR_ELx寄存器中
- VBAR_EL1寄存器见armv8.6手册的第D13.2.137章
VBAR寄存器的Bit[10:0] 是保留的, 也就是说 异常向量表的起始地址必须以2KB字节对齐
- 每个表项可用存放32条指令,一共128个字节
异常向量表:
对应内核代码实现:
- kernel_ventry是一个宏,它的实现在同一个文件里,简化后的代码片段如下
- align 7表示按照2的7次方大小来对齐,2的7次方是128个字节。
- sub指令是让栈指针sp减去一个S_FRAME_SIZE,其中S_FRAME_SIZE称为寄存器框架大小,也就是struct pt_regs数据结构的大小
- 以发生在EL1的IRQ中断为例,这条语句变成了“b el1_irq”
保存异常上下文
栈框:Linux内核中定义了一个struct pt_regs的数据结构来描述内核栈上保存寄存器的排列信息,通常用于保存中断上下文等信息。
栈框保存
同步异常的解析
异常综合信息寄存器ESR_ELx
见armv8.6手册的第D13.2.36章
ESR寄存器一共包含4个字段(域),其中:
- Bit 32~63,是保留的比特位
- Bit 26-31,是异常类型(Exception Class,简称EC),这个字段指示发生异常的类型,同时用来索引ISS域(Bit 0-24)
- Bit 25,IL,表示同步异常的指令长度
- Bit 0~24,ISS(Instruction Specific Syndrome)具体的异常指令编码。这个异常指令编码表依赖不同的异常类型,不同的异常类型有不同的编码格式
异常类型EC
比如 EC == 0b000000 Unknown reason. EC == 0b000001 Trapped WFI or WFE instruction execution. ... EC == 0b100000 Instruction Abort from a lower Exception level. EC == 0b100001 Instruction Abort taken without a change in Exception level. EC == 0b100100 Data Abort from a lower Exception level
ISS字段的编码方式
每个异常类型(EC)对应不同的ISS字段的编码方式
数据异常的ISS域的解析
比如: EC == 0b100100
Data Abort from a lower Exception level.
失效地址寄存器FAR寄存器
FAR寄存器保存了发生异常时刻的虚拟地址。