背景: 异常(Exception)发生是针对CPU的,本来CPU就在不同异常等级(exception level)运行。 ARM64中有4个异常等级,分别是EL0, EL1, EL2, EL3 注意区分:异常发生的等级,以及跃迁到的目标异常等级,目标异常等级大于等于异常发生的等级,EL0不会是目标异常等级 异常类型分为同步异常和异步异常, 同步异常只有Synchronous,其它3个类型都是异步异常。
异常发生时,硬件做的事情: (1)CPU core感知到异常发生,生成一个目标异常等级 (2)把PSTATE寄存器里的值保存到对应目标异常等级的SPSR_ELx寄存器 便于恢复时使用 (3)把返回地址保存在对应目标异常等级的ELR寄存器中 若是同步异常,则ELR寄存器存的是aborted PC (4)把PSTATE寄存器里的DAIF字段都mark掉 相当于把本地CPU的中断关闭,但是同步异常不受影响,还是有机会继续发生。 (5)对于同步异常,把异常的原因写入到ESR_ELx寄存器 (6)设置SP,指向目标异常等级里的栈,自动切换SP到SP_ELx寄存器中 (7)异常等级切换到目标异常等级,PC指针切换到对应的异常向量表去执行
软件处理是从对应的异常等级的向量表vectors开始的: (1)先kernel_ventry 会分配栈,以及检查栈是否溢出 第一句就是分配pt_regs的栈: sub sp, sp, #PT_REGS_SIZE (2)转到对应的子类型,先做kernel_entry来保存pt_regs, 也会做current->addr_limit设置 kernel_entry 做好后的主要寄存器: /* * Registers that may be useful after this macro is invoked: * * x20 - ICC_PMR_EL1 * x21 - aborted SP * x22 - aborted PC * x23 - aborted PSTATE */ (3)继续做对应后续处理 (4)若从处理里返回,先kernel_exit, 再用eret指令从目标异常返回 eret指令自动完成: 1)从ELR_ELx寄存器中恢复PC指针 2)从SPSR_ELx寄存器中恢复CPU状态
异常向量表补充: 每个异常等级的异常向量表根据原异常等级,使用的SP以及ARM状态有4组, 每组又有4个类型,包括Synchronous, IRQ, FIQ, Error 异常向量表的内存布局,每个类型之间的偏差值需要符合ARM spec标准。向量表的起始地址存在vector base register里。
Linux kernel 5.14 rc6 arch/arm64/kernel/entry.S /*
-
Exception vectors. */ .pushsection “.entry.text”, “ax” .align 11 SYM_CODE_START(vectors) kernel_ventry 1, t, 64, sync // Synchronous EL1t kernel_ventry 1, t, 64, irq // IRQ EL1t kernel_ventry 1, t, 64, fiq // FIQ EL1h kernel_ventry 1, t, 64, error // Error EL1t kernel_ventry 1, h, 64, sync // Synchronous EL1h kernel_ventry 1, h, 64, irq // IRQ EL1h kernel_ventry 1, h, 64, fiq // FIQ EL1h kernel_ventry 1, h, 64, error // Error EL1h kernel_ventry 0, t, 64, sync // Synchronous 64-bit EL0 kernel_ventry 0, t, 64, irq // IRQ 64-bit EL0 kernel_ventry 0, t, 64, fiq // FIQ 64-bit EL0 kernel_ventry 0, t, 64, error // Error 64-bit EL0 kernel_ventry 0, t, 32, sync // Synchronous 32-bit EL0 kernel_ventry 0, t, 32, irq // IRQ 32-bit EL0 kernel_ventry 0, t, 32, fiq // FIQ 32-bit EL0 kernel_ventry 0, t, 32, error // Error 32-bit EL0 SYM_CODE_END(vectors)
|