一、异常等级
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 el
mov x0, sp
mov x1, #\reason
mrs x2, esr_el1
b bad_mode
.endm
/*
vector table entry
每个表项是128字节, align 7表示128字节对齐
*/
.macro vtentry label
.align 7
b \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_invalid
vtentry el1_irq_invalid
vtentry el1_fiq_invalid
vtentry el1_error_invalid
/* Current EL with SPx
当前系统运行在EL1时使用EL1的栈指针SP
这说明系统在内核态发生了异常
Note: 我们暂时只实现IRQ中断
*/
vtentry el1_sync_invalid
vtentry el1_irq_invalid
vtentry el1_fiq_invalid
vtentry el1_error_invalid
/* Lower EL using AArch64
在用户态的aarch64的程序发生了异常
*/
vtentry el0_sync_invalid
vtentry el0_irq_invalid
vtentry el0_fiq_invalid
vtentry el0_error_invalid
/* Lower EL using AArch32
在用户态的aarch32的程序发生了异常
*/
vtentry el0_sync_invalid
vtentry el0_irq_invalid
vtentry el0_fiq_invalid
vtentry 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_entry
sub 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_SIZE
mrs x22, elr_el1
mrs 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, SPSR
msr elr_el1, x21 // set up the return data
msr spsr_el1, x22
ldp 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 sp
eret
.endm
/*
处理无效的异常向量
*/
.macro inv_entry el, reason
//kernel_entry el
mov x0, sp
mov x1, #\reason
mrs x2, esr_el1
b bad_mode
.endm
/*
vector table entry
每个表项是128字节, align 7表示128字节对齐
*/
.macro vtentry label
.align 7
b \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_invalid
vtentry el1_irq_invalid
vtentry el1_fiq_invalid
vtentry el1_error_invalid
/* Current EL with SPx
当前系统运行在EL1时使用EL1的栈指针SP
这说明系统在内核态发生了异常
Note: 我们暂时只实现IRQ中断
*/
vtentry el1_sync_invalid
vtentry el1_irq
vtentry el1_fiq_invalid
vtentry el1_error_invalid
/* Lower EL using AArch64
在用户态的aarch64的程序发生了异常
*/
vtentry el0_sync_invalid
vtentry el0_irq_invalid
vtentry el0_fiq_invalid
vtentry el0_error_invalid
/* Lower EL using AArch32
在用户态的aarch32的程序发生了异常
*/
vtentry el0_sync_invalid
vtentry el0_irq_invalid
vtentry el0_fiq_invalid
vtentry el0_error_invalid
el1_sync_invalid:
//inv_entry 1, BAD_SYNC
kernel_entry
mov x0, sp
mov x1, 0
mrs x2, esr_el1
bl bad_mode
kernel_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_entry
bl irq_handle
kernel_exit
//string_test:
// .string "t"
.global trigger_alignment
trigger_alignment:
ldr x0, =0x80002
str wzr, [x0]
ret