异常
异常等级:
- el0:应用
- el1:内核
- el2:虚拟化
- el3:安全
异常
异常分为两类,同步异常,异步异常;
同步异常
同步异常是可以预知的,指处理器执行某条指令而直接产生的异常,通常需要对应的修复处理后程序才能继续执行;比如缺页异常;
- 异常的原因会存放在esr寄存器,fsr等相关寄存器中;
- 在中断向量表中会根据异常原因,再进行对应的处理;
常见的同步异常有:
- data abort
- instruction abort
- svc系统调用
异常向量表中只有一栏是针对于同步异常,具体同步异常的处理会根据ers进行对应的处理;
异步异常
异步异常是未知的,比如中断;也就是:
- IRQ,
- FIQ,
- SERROR:内核并不能修复,直接报panic。
do_serror
这三个异步异常在异常向量表中都有对应的类型处理;
异常处理:
异常发生
cpu会自动做如下处理:
- 把pstate保存到对应目标异常的SPSR_Elx中;
- 把返回地址保存到对应目标异常的ELR_Elx中;
- PSTATE的D、A、I、F都设置位1,相当于把调试异常,Serror,IRQ,FIQ都关掉;
- 同步异常要把异常原因写入ESR_ELx;
- 切换SP寄存器为目标异常的SP_ELx;
- 切换到对应的异常等级,并且跳转到对应的异常向量表;
异常返回
异常返回就是软件调用ERET指令;
这条指令会自动完成如下处理:
- 从ELR_Elx中恢复PC;
- 从SPER_Elx中恢复Pstate;这一步也相当于把D、A、I、F中断打开;
异常返回地址
注意区分LR,和ELR;
- LR:是子程序的返回地址
- ELR:是发生异常时返回的地址;
ELR指向哪一条指令呢?
异步异常
elr指向由于中断没有执行,或者没有成功执行的指令
同步异常
- 不是系统调用的同步异常:指向错误的那条指令,修复后继续返回那条指令继续执行;比如访问地址错误,内核修复了以后应该重新继续执行这条指令
- 系统调用: 指向系统调用的下一条指令;
异常处理路由:应该到哪个el的异常向量表执行
- el0不能处理异常;
- 根据SCR,HCR配置控制;uboot是路由到了el3;
异常时栈的选择
SPSel寄存器决定;
- 0表示使用SP_EL0
- 1表示使用SP_ELx;cpu会自动根据目标异常等级选择栈指针
- 操作系统保证每个异常等级对应的栈空间是可用的;
内核配置如下:
ENTRY(el2_setup)
msr SPsel, #1 // We want to use SP_EL{1,2}
执行状态
HCR_EL2 RW域控制
异常返回的执行状态
SPSR决定
异常向量表
- 每个等级都有自己的异常向量表
- 每个表项128字节,指令32位(4 byte),也就是可以存放32条指令;
- 通过vbar寄存器设置向量表基地址
adr_l x8, vectors // load VBAR_EL1 with virtual msr vbar_el1, x8 // vector table address /* 异常向量表 */ isb
kernel
ENTRY(vectors)
kernel_ventry 1, sync_invalid // Synchronous EL1t /* 对于这里的处理都是无效的,触发前是el0,触发后也是el0,应该没有这个情况吧,既然有设置,是什么时候会发生呢? */
kernel_ventry 1, irq_invalid // IRQ EL1t
kernel_ventry 1, fiq_invalid // FIQ EL1t
kernel_ventry 1, error_invalid // Error EL1t
kernel_ventry 1, sync // Synchronous EL1h
kernel_ventry 1, irq // IRQ EL1h
kernel_ventry 1, fiq_invalid // FIQ EL1h
kernel_ventry 1, error // Error EL1h
kernel_ventry 0, sync // Synchronous 64-bit EL0 /* 异常等级会发生切换,也就是el0->el1,也就是用户层触发异常时会调用到这里 */
kernel_ventry 0, irq // IRQ 64-bit EL0
kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0
kernel_ventry 0, error // Error 64-bit EL0
#ifdef CONFIG_COMPAT
kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0
kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0
kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0
kernel_ventry 0, error_compat, 32 // Error 32-bit EL0
#else
kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0
kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0
kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0
kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0
#endif
END(vectors)
所以当一个异常发生时应该到哪一个描述对应的表中执行呢?
- el是否切换
- 不切换,看SPSel配置SP_ELx,还是SP_EL0;
- 切换,看执行态(aarch32,还是aarch64)
同步异常如何解析:
ESR,ISS。kernel如下:
mrs x1, esr_el1 // read the syndrome register /* 将esr的值保存进x1,对应函数中的第2个参数 */
lsr x24, x1, #ESR_ELx_EC_SHIFT // exception class /* 右移,获取ec字段的值 */
cmp x24, #ESR_ELx_EC_DABT_CUR // data abort in EL1 /* 根据不同的EC值进行跳转处理 */
b.eq el1_da /* 数据异常 */
cmp x24, #ESR_ELx_EC_IABT_CUR // instruction abort in EL1
b.eq el1_ia
cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el1_undef
cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
b.eq el1_sp_pc
cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
b.eq el1_sp_pc
cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL1
b.eq el1_undef
cmp x24, #ESR_ELx_EC_BREAKPT_CUR // debug exception in EL1
b.ge el1_dbg
b el1_inv

浙公网安备 33010602011771号