Linux内核深度解析之中断、异常和系统调用——ARM64异常处理

ARM64异常处理

1. 异常级别

ARM64处理器定义了4个异常级别:0~3。通常ARM64处理器在异常级别0执行进程,在异常级别1执行内核。ARM64处理器的异常级别0就是我们常说的用户模式,异常级别1就是我们常说的内核模式。

2. 异常分类

在ARM64体系结构中,异常分为同步异常和异步异常。

同步异常:

(1)系统调用

(2)数据中止

(3)指令中止

(4)栈指针或指令地址没有对齐

(5)没有定义的指令

(6)调试异常

异步异常:

(1)中断(IRQ)

(2)快速中断(FIQ)

(3)系统错误

3. 异常向量表

当异常发生的时候,处理器需要执行异常的处理程序。存储异常处理程序的内存位置称为异常向量,通常把所有异常向量存放在一张表中,称为异常向量表。

对于ARM64处理器的异常级别1、2和3,每个异常级别都有自己的异常向量表,异常向量表的起始虚拟地址存放在寄存器VBAR_ELn(向量基准地址寄存器,Vector Based Address Register)中。

ARM64架构内核定义的异常级别1的异常向量表如下:

arch/arm64/kernel/entry.S
// 宏kernel_ventry
	.macro kernel_ventry, el, label, regsize = 64
	.align 7
	sub	sp, sp, #S_FRAME_SIZE
	b	el\()\el\()_\label
	.endm


/*
 * Exception vectors.
 */
	.pushsection ".entry.text", "ax"

	.align	11
ENTRY(vectors)
	kernel_ventry	1, sync_invalid			// 异常级别1生成的同步异常,使用栈指针寄存器SP_EL0
	kernel_ventry	1, irq_invalid			// 异常级别1生成的中断,使用栈指针寄存器SP_EL0
	kernel_ventry	1, fiq_invalid			// 异常级别1生成的快速中断,使用栈指针寄存器SP_EL0
	kernel_ventry	1, error_invalid		// 异常级别1生成的系统错误,使用栈指针寄存器SP_EL0

	kernel_ventry	1, sync				// 异常级别1生成的同步异常,使用栈指针寄存器SP_EL1
	kernel_ventry	1, irq				// 异常级别1生成的中断,使用栈指针寄存器SP_EL1
	kernel_ventry	1, fiq_invalid                  // 异常级别1生成的快速中断,使用栈指针寄存器SP_EL1
	kernel_ventry	1, error			// 异常级别1生成的系统错误,使用栈指针寄存器SP_EL1

	kernel_ventry	0, sync				// 64位应用程序在异常级别0生成的同步异常
	kernel_ventry	0, irq				// 64位应用程序在异常级别0生成的中断
	kernel_ventry	0, fiq_invalid                  // 64位应用程序在异常级别0生成的快速中断
	kernel_ventry	0, error			// 64位应用程序在异常级别0生成的系统错误

#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)

把“kernel_ventry 1, sync”展开以后是:

.align 7                        // 表示下一条指令的地址对齐到2的7次方,即对齐到128
sub	sp, sp, #S_FRAME_SIZE
b	el1_sync                // 跳转到标号el1_sync

在启动过程中,0号处理器称为引导处理器,其他处理器称为从处理器。引导处理器在函数__primary_switched()中把寄存器VBAR_EL1设置为异常级别1的异常向量表的起始虚拟地址:

_head()  ->  stext()  ->  __primary_switch()  ->  __primary_switched()

arch/arm64/kernel/head.S
__primary_switched:
    ...

    adr_l	x8, vectors
    msr	vbar_el1, x8			// 把寄存器VBAL_EL1设置为异常向量表的起始虚拟地址
    isb
    ...

从处理器在函数__secondary_switched()中把寄存器VBAR_EL1设置为异常级别1的异常向量表的起始虚拟地址。

secondary_entry()  ->  secondary_startup()  ->  __secondary_switched()

arch/arm64/kernel/head.S
__secondary_switched:
    adr_l	x5, vectors
    msr	vbar_el1, x5
    isb
    ...
ENDPROC(__secondary_switched)

4.1.4 异常处理

当处理器取出异常处理的时候,自动执行下面的操作:

(1)把当前的处理器状态(Processor State,PSTATE)保存在寄存器SPSR_EL1(保存程序状态寄存器,Saved Program Status Register)中

(2)把返回地址保存在寄存器ELR_EL1(异常链接寄存器,Exception Link Register)中

  • 如果是系统调用,那么返回地址是系统调用指令后面的指令
  • 如果是除系统调用外的同步异常,那么返回地址是生成的异常指令,因为执行完异常处理程序以后需要重新执行生成异常的指令
  • 如果是同步异常,那么返回地址是没有执行的第一条指令

(3)把处理器状态DAIF这4个异常掩码位都设置为1,禁止这4种异常,D是调试掩码位(Debug mask bit),A是系统错误掩码位(SError mask bit),I是中断掩码位(IRQ mask bit),F是快速中断掩码位(FIQ mask bit)

(4)如果是同步异常或系统错误异常,把生成异常的原因保存在寄存器ESR_EL1(异常症状寄存器,Exception Syndrome Register)中

(5)如果是同步异常,把错误地址保存在寄存器FAR_EL1(错误地址寄存器,Fault Address Register)中

(6)如果处理器处于用户模式(异常级别0),那么把异常级别提升到1

(7)根据向量基准地址寄存器VBAR_EL1、异常类型和生成异常的异常级别计算出异常向量的虚拟地址,执行异常向量

对于ARM64位应用程序在用户模式(异常级别0)下生成的同步异常,入口是el0_sync,其代码如下:

/*
 * EL0 mode handlers.
 */
	.align	6
el0_sync:
	kernel_entry 0		// 把所有通用寄存器的值保存在当前进程的内核栈中
	mrs	x25, esr_el1			// read the syndrome register
	lsr	x24, x25, #ESR_ELx_EC_SHIFT	// exception class
	cmp	x24, #ESR_ELx_EC_SVC64		// SVC in 64-bit state		系统调用
	b.eq	el0_svc
	cmp	x24, #ESR_ELx_EC_DABT_LOW	// data abort in EL0		数据中止
	b.eq	el0_da
	cmp	x24, #ESR_ELx_EC_IABT_LOW	// instruction abort in EL0		指令中止
	b.eq	el0_ia
	cmp	x24, #ESR_ELx_EC_FP_ASIMD	// FP/ASIMD access		访问浮点或高级SIMD
	b.eq	el0_fpsimd_acc
	cmp	x24, #ESR_ELx_EC_SVE		// SVE access		浮点或高级SIMD异常
	b.eq	el0_sve_acc
	cmp	x24, #ESR_ELx_EC_FP_EXC64	// FP/ASIMD exception
	b.eq	el0_fpsimd_exc
	cmp	x24, #ESR_ELx_EC_SYS64		// configurable trap
	b.eq	el0_sys
	cmp	x24, #ESR_ELx_EC_SP_ALIGN	// stack alignment exception
	b.eq	el0_sp_pc
	cmp	x24, #ESR_ELx_EC_PC_ALIGN	// pc alignment exception
	b.eq	el0_sp_pc
	cmp	x24, #ESR_ELx_EC_UNKNOWN	// unknown exception in EL0
	b.eq	el0_undef
	cmp	x24, #ESR_ELx_EC_BREAKPT_LOW	// debug exception in EL0
	b.ge	el0_dbg
	b	el0_inv

猜你喜欢

转载自blog.csdn.net/linuxweiyh/article/details/106745629