一、异常等级
EL0 非特权模式,用于运行应用程序
EL1为特权模式,用于运行操作系统内核
EL2用于运行虚拟化管理程序
EL3用于运行安全世界的管理程序
二、同步异常与异步异常
1. 同步异常:处理器执行某条指令而直接导致的异常,往往需要在异常函数里处理该异常之后,处理器才能继续运行,常见的同步异常如下:
-
尝试访问异常等级不恰当的寄存器
-
尝试执行关闭或没有定义的指令
-
使用没有对齐的SP
-
尝试执行与PC指针没有对齐的指令
-
软件产生的异常,如执行SVC,HVC或SMC指令
-
地址翻译或者权限等导致的数据异常
-
地址翻译或者权限等导致的指令异常
-
调试导致的异常,如断点异常,观察点异常,软件单步异常等
2. 异步异常:异常触发的原因与处理器当前正在执行的指令无关的异常,中断属于异步异常的一种,常见的异步异常包括物理中断和虚拟中断
-
物理中断分为3种:分别是SError、IRQ、FIQ
-
虚拟中断分为3种:分别是vSError、vIRQ、vFIQ
三、异常处理与返回
-
异常入口
处理器检测到异常后自动做的事
-
把PSTATE寄存器的值保存到对应目标异常等级的SPSR_ELx中
-
把返回等级保存到对应目标异常等级的ELR_RLx中
-
把PSTATE寄存器中的D、A、I、F标志位都设置为1,相当于把调试异常,SError、IRQ、FIQ都关闭
-
对于同步异常,要分析异常的原因,把具体的原因写入ESR_ELx中
-
切换SP寄存器未目标等级的SP_ELx或者SP_EL0
-
从异常发生现场的异常等级切换到对应目标异常等级,然后跳转到异常向量表中
操作系统做的事
-
从中断向量表开始,根据异常发生的类型,跳转到合适的异常向量表。异常向量表的每个项都会保存一条跳转指令,然后跳转到恰当的异常处理函数并处理异常
2. 异常返回
当操作系统的异常处理完成后,执行一条ERET指令即可从异常返回,这条指令会自动处理如下工作
-
从ELR_EL中恢复PC指针
-
从SPSR_ELx中恢复PSTATE寄存器的状态
四、异常处理的路由 SCR_EL3
HCR_EL2
栈的选择 我们可以通过SPSel寄存器来配置SP。SPSel寄存器中的SP字段设置为0表示所有的EL中使用SP_EL0作为栈指针寄存器,设置为1表示使用SP_ELx作为栈指针寄存器 ARMV8异常向量表
ARMV8向量表有如下特点
-
除EL0之后,每个EL都有自己的异常向量表
-
异常向量表的基地址需要设置到VBAR_ELx中
-
异常向量表的起始地址必须以2KB字节对齐
-
每个表项可以存放32条指令,一共128字节
从EL2切换到EL1相关寄存器
#define BAD_SYNC 0
#define BAD_IRQ 1
#define BAD_FIQ 2
#define BAD_ERROR 3
/*处理无效的异常向量*/.macro inv_entry el, reason//kernel_entry elmov x0, spmov x1, #\reasonmrs x2, esr_el1b bad_mode.endm
/*vector table entry每个表项是128字节, align 7表示128字节对齐*/.macro vtentry label.align 7b \label.endm
/** Vector Table** ARM64的异常向量表一共占用2048个字节* 分成4组,每组4个表项,每个表项占128字节* 参见ARMv8 spec v8.6第D1.10节* align 11表示2048字节对齐*/
.align 11
.global vectors
vectors:/* Current EL with SP0当前系统运行在EL1时使用EL0的栈指针SP这是一种异常错误的类型*/vtentry el1_sync_invalidvtentry el1_irq_invalidvtentry el1_fiq_invalidvtentry el1_error_invalid/* Current EL with SPx当前系统运行在EL1时使用EL1的栈指针SP这说明系统在内核态发生了异常Note: 我们暂时只实现IRQ中断*/vtentry el1_sync_invalidvtentry el1_irq_invalidvtentry el1_fiq_invalidvtentry el1_error_invalid/* Lower EL using AArch64在用户态的aarch64的程序发生了异常*/vtentry el0_sync_invalidvtentry el0_irq_invalidvtentry el0_fiq_invalidvtentry el0_error_invalid/* Lower EL using AArch32在用户态的aarch32的程序发生了异常*/vtentry el0_sync_invalidvtentry el0_irq_invalidvtentry el0_fiq_invalidvtentry el0_error_invalid
el1_sync_invalid:inv_entry 1, BAD_SYNC
el1_irq_invalid:inv_entry 1, BAD_IRQ
el1_fiq_invalid:inv_entry 1, BAD_FIQ
el1_error_invalid:inv_entry 1, BAD_ERROR
el0_sync_invalid:inv_entry 0, BAD_SYNC
el0_irq_invalid:inv_entry 0, BAD_IRQ
el0_fiq_invalid:inv_entry 0, BAD_FIQ
el0_error_invalid:inv_entry 0, BAD_ERROR
五、中断处理
#define S_FRAME_SIZE 272 /* sizeof(struct pt_regs) // */
#define S_X0 0 /* offsetof(struct pt_regs, regs[0]) // */
#define S_X1 8 /* offsetof(struct pt_regs, regs[1]) // */
#define S_X2 16 /* offsetof(struct pt_regs, regs[2]) // */
#define S_X3 24 /* offsetof(struct pt_regs, regs[3]) // */
#define S_X4 32 /* offsetof(struct pt_regs, regs[4]) // */
#define S_X5 40 /* offsetof(struct pt_regs, regs[5]) // */
#define S_X6 48 /* offsetof(struct pt_regs, regs[6]) // */
#define S_X7 56 /* offsetof(struct pt_regs, regs[7]) // */
#define S_X8 64 /* offsetof(struct pt_regs, regs[8]) // */
#define S_X10 80 /* offsetof(struct pt_regs, regs[10]) // */
#define S_X12 96 /* offsetof(struct pt_regs, regs[12]) // */
#define S_X14 112 /* offsetof(struct pt_regs, regs[14]) // */
#define S_X16 128 /* offsetof(struct pt_regs, regs[16]) // */
#define S_X18 144 /* offsetof(struct pt_regs, regs[18]) // */
#define S_X20 160 /* offsetof(struct pt_regs, regs[20]) // */
#define S_X22 176 /* offsetof(struct pt_regs, regs[22]) // */
#define S_X24 192 /* offsetof(struct pt_regs, regs[24]) // */
#define S_X26 208 /* offsetof(struct pt_regs, regs[26]) // */
#define S_X28 224 /* offsetof(struct pt_regs, regs[28]) // */
#define S_FP 232 /* offsetof(struct pt_regs, regs[29]) // */
#define S_LR 240 /* offsetof(struct pt_regs, regs[30]) // */
#define S_SP 248 /* offsetof(struct pt_regs, sp) // */
#define S_PC 256 /* offsetof(struct pt_regs, pc) // */
#define S_PSTATE 264 /* offsetof(struct pt_regs, pstate) // */
#define BAD_SYNC 0
#define BAD_IRQ 1
#define BAD_FIQ 2
#define BAD_ERROR 3.macro kernel_entrysub sp, sp, #S_FRAME_SIZE/*保存通用寄存器x0~x29到栈框里pt_regs->x0~x29*/stp x0, x1, [sp, #16 *0]stp x2, x3, [sp, #16 *1]stp x4, x5, [sp, #16 *2]stp x6, x7, [sp, #16 *3]stp x8, x9, [sp, #16 *4]stp x10, x11, [sp, #16 *5]stp x12, x13, [sp, #16 *6]stp x14, x15, [sp, #16 *7]stp x16, x17, [sp, #16 *8]stp x18, x19, [sp, #16 *9]stp x20, x21, [sp, #16 *10]stp x22, x23, [sp, #16 *11]stp x24, x25, [sp, #16 *12]stp x26, x27, [sp, #16 *13]stp x28, x29, [sp, #16 *14]/* x21: 栈顶 的位置*/add x21, sp, #S_FRAME_SIZEmrs x22, elr_el1mrs x23, spsr_el1/* 把lr保存到pt_regs->lr, 把sp保存到pt_regs->sp位置*/stp lr, x21, [sp, #S_LR]/* 把elr_el1保存到pt_regs->pc中把spsr_elr保存到pt_regs->pstate中*/stp x22, x23, [sp, #S_PC].endm.macro kernel_exit/* 从pt_regs->pc中恢复elr_el1,从pt_regs->pstate中恢复spsr_el1*/ldp x21, x22, [sp, #S_PC] // load ELR, SPSRmsr elr_el1, x21 // set up the return datamsr spsr_el1, x22ldp x0, x1, [sp, #16 * 0]ldp x2, x3, [sp, #16 * 1]ldp x4, x5, [sp, #16 * 2]ldp x6, x7, [sp, #16 * 3]ldp x8, x9, [sp, #16 * 4]ldp x10, x11, [sp, #16 * 5]ldp x12, x13, [sp, #16 * 6]ldp x14, x15, [sp, #16 * 7]ldp x16, x17, [sp, #16 * 8]ldp x18, x19, [sp, #16 * 9]ldp x20, x21, [sp, #16 * 10]ldp x22, x23, [sp, #16 * 11]ldp x24, x25, [sp, #16 * 12]ldp x26, x27, [sp, #16 * 13]ldp x28, x29, [sp, #16 * 14]/* 从pt_regs->lr中恢复lr*/ldr lr, [sp, #S_LR]add sp, sp, #S_FRAME_SIZE // restore speret.endm
/*处理无效的异常向量*/.macro inv_entry el, reason//kernel_entry elmov x0, spmov x1, #\reasonmrs x2, esr_el1b bad_mode.endm
/*vector table entry每个表项是128字节, align 7表示128字节对齐*/.macro vtentry label.align 7b \label.endm
/** Vector Table** ARM64的异常向量表一共占用2048个字节* 分成4组,每组4个表项,每个表项占128字节* 参见ARMv8 spec v8.6第D1.10节* align 11表示2048字节对齐*/
.align 11
.global vectors
vectors:/* Current EL with SP0当前系统运行在EL1时使用EL0的栈指针SP这是一种异常错误的类型*/vtentry el1_sync_invalidvtentry el1_irq_invalidvtentry el1_fiq_invalidvtentry el1_error_invalid/* Current EL with SPx当前系统运行在EL1时使用EL1的栈指针SP这说明系统在内核态发生了异常Note: 我们暂时只实现IRQ中断*/vtentry el1_sync_invalidvtentry el1_irqvtentry el1_fiq_invalidvtentry el1_error_invalid/* Lower EL using AArch64在用户态的aarch64的程序发生了异常*/vtentry el0_sync_invalidvtentry el0_irq_invalidvtentry el0_fiq_invalidvtentry el0_error_invalid/* Lower EL using AArch32在用户态的aarch32的程序发生了异常*/vtentry el0_sync_invalidvtentry el0_irq_invalidvtentry el0_fiq_invalidvtentry el0_error_invalid
el1_sync_invalid://inv_entry 1, BAD_SYNCkernel_entrymov x0, spmov x1, 0mrs x2, esr_el1bl bad_modekernel_exit
el1_irq_invalid:inv_entry 1, BAD_IRQ
el1_fiq_invalid:inv_entry 1, BAD_FIQ
el1_error_invalid:inv_entry 1, BAD_ERROR
el0_sync_invalid:inv_entry 0, BAD_SYNC
el0_irq_invalid:inv_entry 0, BAD_IRQ
el0_fiq_invalid:inv_entry 0, BAD_FIQ
el0_error_invalid:inv_entry 0, BAD_ERROR
el1_irq:kernel_entrybl irq_handlekernel_exit
//string_test:
// .string "t"
.global trigger_alignment
trigger_alignment:ldr x0, =0x80002str wzr, [x0]ret