异常

异常等级:

  • el0:应用
  • el1:内核
  • el2:虚拟化
  • el3:安全

异常

异常分为两类,同步异常,异步异常;

同步异常

同步异常是可以预知的,指处理器执行某条指令而直接产生的异常,通常需要对应的修复处理后程序才能继续执行;比如缺页异常;

  • 异常的原因会存放在esr寄存器,fsr等相关寄存器中;
  • 在中断向量表中会根据异常原因,再进行对应的处理;

常见的同步异常有:

  • data abort
  • instruction abort
  • svc系统调用

异常向量表中只有一栏是针对于同步异常,具体同步异常的处理会根据ers进行对应的处理;

异步异常

异步异常是未知的,比如中断;也就是:

  • IRQ,
  • FIQ,
  • SERROR:内核并不能修复,直接报panic。do_serror

这三个异步异常在异常向量表中都有对应的类型处理;

异常处理:

异常发生

cpu会自动做如下处理

  1. 把pstate保存到对应目标异常的SPSR_Elx中;
  2. 把返回地址保存到对应目标异常的ELR_Elx中;
  3. PSTATE的D、A、I、F都设置位1,相当于把调试异常,Serror,IRQ,FIQ都关掉;
  4. 同步异常要把异常原因写入ESR_ELx;
  5. 切换SP寄存器为目标异常的SP_ELx;
  6. 切换到对应的异常等级,并且跳转到对应的异常向量表;

异常返回

异常返回就是软件调用ERET指令;
这条指令会自动完成如下处理

  1. 从ELR_Elx中恢复PC;
  2. 从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
posted @ 2024-12-25 00:17  _xingxing  阅读(75)  评论(0)    收藏  举报