当前位置:   article > 正文

ARM64 kernel exception vectors_el1h和el1t

el1h和el1t

描述

当 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
  • 1
  • 2
  • 3
  • 4
SYM_FUNC_START_LOCAL(__secondary_switched)
adr_l	x5, vectors
msr	vbar_el1, x5
isb
  • 1
  • 2
  • 3
  • 4

涉及文件
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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

关于该向量表的解释参考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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/973653
推荐阅读
相关标签
  

闽ICP备14008679号