ARM64 kernel exception vectors

描述

当 PE 将异常处理到使用 AArch64 的异常级别时,将强制执行到作为异常的异常向量的地址。 异常向量存在于异常被处理到的异常级别的向量表中。
从向量基地址开始,向量表在内存中占用多个连续的字对齐地址。
每个异常级别都有一个关联的向量基地址寄存器 (VBAR),它定义了该异常级别的表的异常基地址。

对于 AArch64 状态的异常,向量表提供以下信息:
1.异常是否为以下之一:

  • Synchronous exception
  • SError
  • IRQ
  • FIQ

2.有关异常来自的异常级别的信息,结合有关正在使用的堆栈指针的信息,以及寄存器的状态。

Vector offsets from vector table base address
在这里插入图片描述

VBAR寄存器

Exception level的Vector Base Address Register (VBAR)寄存器,该寄存器保存了各个exception level的异常向量表的基地址。该寄存器有三个,分别是VBAR_EL1,VBAR_EL2,VBAR_EL3。
linux初始化的时候会配置每一个PE的vbar_el1设置为linux的中断向量表。

SYM_FUNC_START_LOCAL(__primary_switched)
adr_l	x8, vectors			// load VBAR_EL1 with virtual
msr	vbar_el1, x8			// vector table address
isb
SYM_FUNC_START_LOCAL(__secondary_switched)
adr_l	x5, vectors
msr	vbar_el1, x5
isb

涉及文件
linux/arch/arm64/kernel/head.S

中断向量表在kernel中的实现

495 /*
496  * Exception vectors.
497  */
498   .pushsection ".entry.text", "ax"  //表明编译到.entry.text 段 ax的意思是a (可分配)、 w(可写)、r (可读)、x (可执行),
499
500   .align  11 //2K地址对齐
501 SYM_CODE_START(vectors) //中断向量表,主要通过宏kernel_ventry来实现。
502   kernel_ventry 1, sync_invalid     // Synchronous EL1t  EL1t的中断向量为invalid
503   kernel_ventry 1, irq_invalid      // IRQ EL1t
504   kernel_ventry 1, fiq_invalid      // FIQ EL1t
505   kernel_ventry 1, error_invalid    // Error EL1t
506
507   kernel_ventry 1, sync       // Synchronous EL1h  EL1h的同步异常
508   kernel_ventry 1, irq        // IRQ EL1h  EL1h的irq
509   kernel_ventry 1, fiq_invalid      // FIQ EL1h EL1h的fiq为invalid
510   kernel_ventry 1, error      // Error EL1h EL1h的error
511
512   kernel_ventry 0, sync       // Synchronous 64-bit EL0  EL0的同步异常
513   kernel_ventry 0, irq        // IRQ 64-bit EL0  EL0的irq
514   kernel_ventry 0, fiq_invalid      // FIQ 64-bit EL0 EL0的fiq为invalid
515   kernel_ventry 0, error      // Error 64-bit EL0 EL0的error
516
517 #ifdef CONFIG_COMPAT  //El0运行在aarch32状态的中断的支持
518                                                                                                                                                   
519   kernel_ventry 0, irq_compat, 32   // IRQ 32-bit EL0
520   kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0
521   kernel_ventry 0, error_compat, 32   // Error 32-bit EL0
522 #else
523   kernel_ventry 0, sync_invalid, 32   // Synchronous 32-bit EL0
524   kernel_ventry 0, irq_invalid, 32    // IRQ 32-bit EL0
525   kernel_ventry 0, fiq_invalid, 32    // FIQ 32-bit EL0
526   kernel_ventry 0, error_invalid, 32    // Error 32-bit EL0
527 #endif
528 SYM_CODE_END(vectors)

关于该向量表的解释参考armv8手册
D1.10.2章节

其中EL1t和ELL1h的差别描述如下:

加粗样式在这里插入图片描述

kernel_ventry

kernel_ventry是个宏实现如下,主要做了:

  1. sub sp, sp, #S_FRAME_SIZE—sp减去S_FRAME_SIZE用 保存当前pt_regs 用来恢复callstack。注意对于运行在用户态的用户进程来说这个sp是sp_el1,sp_el1这个时候指向用户进程内核栈的最低端(地址最大处)。对于内核进程,或者运行在内核态的用户进程sp指向内核栈的使用的栈顶。
  2. b el()\el()_\label —跳转到对应异常类型的处理。
.macro kernel_ventry, el, label, regsize = 64 //el代表异常等级。lable中断触发的方式irq fiq error sync还有invalid。 regsize默认是64位。
.align 7 //对齐
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0 //KPTI方案的补丁,这里略过
.if	\el == 0
alternative_if ARM64_UNMAP_KERNEL_AT_EL0
.if	\regsize == 64
mrs	x30, tpidrro_el0
msr	tpidrro_el0, xzr
.else
mov	x30, xzr
.endif
alternative_else_nop_endif
.endif
#endif
sub	sp, sp, #S_FRAME_SIZE  //sp减去S_FRAME_SIZE用 保存当前pt_regs 用来恢复callstack

#ifdef CONFIG_VMAP_STACK //虚拟映射栈,这里略过。
/*
* Test whether the SP has overflowed, without corrupting a GPR.
* Task and IRQ stacks are aligned so that SP & (1 << THREAD_SHIFT)
* should always be zero.
*/
add	sp, sp, x0			// sp' = sp + x0
sub	x0, sp, x0			// x0' = sp' - x0 = (sp + x0) - x0 = sp
tbnz	x0, #THREAD_SHIFT, 0f
sub	x0, sp, x0			// x0'' = sp' - x0' = (sp + x0) - sp = x0
sub	sp, sp, x0			// sp'' = sp' - x0 = (sp + x0) - x0 = sp
b	el()\el()_\label
0:
/*
* Either we've just detected an overflow, or we've taken an exception
* while on the overflow stack. Either way, we won't return to
* userspace, and can clobber EL0 registers to free up GPRs.
*/
/* Stash the original SP (minus S_FRAME_SIZE) in tpidr_el0. */
msr	tpidr_el0, x0

/* Recover the original x0 value and stash it in tpidrro_el0 */
sub	x0, sp, x0
msr	tpidrro_el0, x0

/* Switch to the overflow stack */
adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0

/*
 * Check whether we were already on the overflow stack. This may happen
 * after panic() re-enables interrupts.
 */
mrs	x0, tpidr_el0			// sp of interrupted context
sub	x0, sp, x0			// delta with top of overflow stack
tst	x0, #~(OVERFLOW_STACK_SIZE - 1)	// within range?
b.ne	__bad_stack			// no? -> bad stack pointer

/* We were already on the overflow stack. Restore sp/x0 and carry on. */
sub	sp, sp, x0
mrs	x0, tpidrro_el0

#endif
b	el()\el()_\label //跳转到 eln_lable 例如el1_irq,第一个el()是字符串,\el()是传递的参数,\label也是参数。
.endm

可知,根据kernel的中断向量表根据中断来源的不同,实现了以下异常类型
在这里插入图片描述
涉及文件
linux/arch/arm64/kernel/entry.S

参考 armv8手册 :DDI0487D_a_armv8_arm-20181102S
D1.10 Exception entry
D1.10.2 Exception vectors

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值