arm64异常向量表

ARM64架构中的异常向量表用于存储异常处理代码,每个异常级别都有自己的向量表。LinuxARM64的异常向量表由kernel_ventry宏定义,处理不同类型的异常。VBAR_ELx寄存器保存异常向量表的基地址。异常向量表的保存涉及主核和副核的启动流程,分别由__primary_switched和__secondary_switched函数处理。
摘要由CSDN通过智能技术生成

1 arm64异常向量表

在这里插入图片描述
When an exception occurs, the processor must execute handler code which corresponds to the exception. The location in memory where the handler is stored is called the exception vector. In the ARM architecture, exception vectors are stored in a table, called the exception vector table. Each Exception level has its own vector table, that is, there is one for each of EL3, EL2 and EL1. The table contains instructions to be executed, rather than a set of addresses. Vectors for individual exceptions are located at fixed offsets from the beginning of the table. The virtual address of each table base is set by the Vector Based Address Registers VBAR_EL3, VBAR_EL2 and VBAR_EL1.
Each entry in the vector table is 16 instructions long. This in itself represents a significant change compared to ARMv7, where each entry was 4 bytes. This spacing of the ARMv7 vector table meant that each entry would almost always be some form of branch to the actual exception handler elsewhere in memory. In AArch64, the vectors are spaced more widely, so that the
top-level handler can be written directly in the vector table.
The base address is given by VBAR_ELn and then each entry has a defined offset from this base address. Each table has 16 entries, with each entry being 128 bytes (32 instructions) in size. The table effectively consists of 4 sets of 4 entries.
Which entry is used depends upon a number of factors:

  • The type of exception (SError, FIQ, IRQ or Synchronous)
  • If the exception is being taken at the same Exception level, the Stack Pointer to be used (SP0 or SPx)
  • If the exception is being taken at a lower Exception level, the execution state of the next lower level (AArch64 or AArch32)
    Considering an example might make this easier to understand.
    If kernel code is executing at EL1 and an IRQ interrupt is signaled, an IRQ exception occurs. This particular interrupt is not associated with the hypervisor or secure environment and is also handled within the kernel, also at SP_EL1, and the SPSel bit is set, so you are using SP_EL1. Execution is therefore from address VBAR_EL1 + 0x280.
    In the absence of LDR PC, [PC, #offset] in the ARMv8-A architecture, you must use more instructions to enable the destination to be read from a table of registers. The choice of spacing of the vectors is designed to avoid cache pollution for typical sized instruction cache lines from vectors that are not being used. The Reset Address is a completely separate address, which is
    IMPLEMENTATION DEFINED, and is typically set by hardwired configuration within the core. This address is visible in the RVBAR_EL1/2/3 register.
    Having a separate exception vector for each exception, either from the current Exception level or from the lower Exception level, gives the flexibility for the OS or hypervisor to determine the AArch64 and AArch32 state of the lower Exception levels. The SP_ELn is used for exceptions generated from lower levels. However, the software can switch to use SP_EL0 inside the
    handler. When you use this mechanism, it facilitates access to the values from the thread in the handler.

2 linux arm64异常向量表

/*                          
 * 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 EL1t 
        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)
  • .pushsection “.entry.text”, “ax” 表示要将当前的异常向量表放到.entry.text段,属性为"ax"
  • .align 11表示异常向量表的起始地址要2K对齐(2^11 = 2048),这和VBAR_ELx的寄存器定义是一致的。
  • SYM_CODE_START(vectors) 声明异常向量表

3 kernel_ventry宏

  • kernel_ventry是异常向量表的一部分通用处理宏,会依据参数跳转到不同的异常处理函数,对于kernel_ventry 1, h, 64, irq,则会跳转到el1h_64_irq处理函数流程中。
  • .align 7 表示当前的入口要按照128B对齐的方式去存放,这和异常向量表每个entry的大小为128B是匹配的
	.macro kernel_ventry, el:req, ht:req, regsize:req, label:req
	.align 7
.Lventry_start\@:
	.if	\el == 0
	/*
	 * This must be the first instruction of the EL0 vector entries. It is
	 * skipped by the trampoline vectors, to trigger the cleanup.
	 */
	b	.Lskip_tramp_vectors_cleanup\@
	.if	\regsize == 64
	mrs	x30, tpidrro_el0
	msr	tpidrro_el0, xzr
	.else
	mov	x30, xzr
	.endif
.Lskip_tramp_vectors_cleanup\@:
	.endif

	sub	sp, sp, #PT_REGS_SIZE
#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\ht\()_\regsize\()_\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 PT_REGS_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\ht\()_\regsize\()_\label
.org .Lventry_start\@ + 128	// Did we overflow the ventry slot?
	.endm

4 异常向量表的保存

异常向量表的保存分为两个部分,一个是主核的启动流程,另一个是副核的启动流程处理的。其处理函数分别为__primary_switched和__primary_switched

4. VBAR_ELx寄存器

VBAR_EL1, Vector Base Address Register (EL1),Holds the vector base address for any exception that is taken to EL1.
在这里插入图片描述

  • Bits [63:11]
    • Vector Base Address. Base address of the exception vectors for exceptions taken to EL1.
    • If the implementation does not support ARMv8.2-LVA, then:
      • If tagged addresses are being used, bits [55:48] of VBAR_EL1 must be the same or else the use of the vector address will result in a recursive exception.
      • If tagged addresses are not being used, bits [63:48] of VBAR_EL1 must be the same or else the use of the vector address will result in a recursive exception.
    • If the implementation supports ARMv8.2-LVA, then:
      • If tagged addresses are being used, bits [55:52] of VBAR_EL1 must be the same or else the use of the vector address will result in a recursive exception.
      • If tagged addresses are not being used, bits [63:52] of VBAR_EL1 must be the same or else the use of the vector address will result in a recursive exception.
        其访问方式如下所示:
  • MRS , VBAR_EL1 读取VBAR_EL1寄存器中保存的异常向量表地址到xt寄存器中
  • MSR VBAR_EL1, 将xt寄存器中保存的异常向量表地址写入VBAR_EL1寄存器中。

4.2 __primary_switched

对于主核的启动流程来说,其处理是如下两步,首先将vectors地址加载到x8寄存器中,随后通过msr指令,将其值写入vbar_el1寄存器中。

  • adr_l x8, vectors // load VBAR_EL1 with virtual
  • msr vbar_el1, x8 // vector table address
/*
 * The following fragment of code is executed with the MMU enabled.
 *
 *   x0 = __pa(KERNEL_START)
 */
SYM_FUNC_START_LOCAL(__primary_switched)
	adr_l	x4, init_task
	init_cpu_task x4, x5, x6

	adr_l	x8, vectors			// load VBAR_EL1 with virtual
	msr	vbar_el1, x8			// vector table address
	isb

	stp	x29, x30, [sp, #-16]!
	mov	x29, sp

	str_l	x21, __fdt_pointer, x5		// Save FDT pointer

	ldr_l	x4, kimage_vaddr		// Save the offset between
	sub	x4, x4, x0			// the kernel virtual and
	str_l	x4, kimage_voffset, x5		// physical mappings

	mov	x0, x20
	bl	set_cpu_boot_mode_flag

	// Clear BSS
	adr_l	x0, __bss_start
	mov	x1, xzr
	adr_l	x2, __bss_stop
	sub	x2, x2, x0
	bl	__pi_memset
	dsb	ishst				// Make zero page visible to PTW

#if VA_BITS > 48
	adr_l	x8, vabits_actual		// Set this early so KASAN early init
	str	x25, [x8]			// ... observes the correct value
	dc	civac, x8			// Make visible to booting secondaries
#endif

#ifdef CONFIG_RANDOMIZE_BASE
	adrp	x5, memstart_offset_seed	// Save KASLR linear map seed
	strh	w24, [x5, :lo12:memstart_offset_seed]
#endif
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
	bl	kasan_early_init
#endif
	mov	x0, x21				// pass FDT address in x0
	bl	early_fdt_map			// Try mapping the FDT early
	mov	x0, x20				// pass the full boot status
	bl	init_feature_override		// Parse cpu feature overrides
	mov	x0, x20
	bl	finalise_el2			// Prefer VHE if possible
	ldp	x29, x30, [sp], #16
	bl	start_kernel
	ASM_BUG()
SYM_FUNC_END(__primary_switched)

4.3 __primary_switched

对于副核的启动流程来说,其处理是如下两步,首先将vectors地址加载到x5寄存器中,随后通过msr指令,将其值写入vbar_el1寄存器中。

  • adr_l x5, vectors
  • msr vbar_el1, x5
SYM_FUNC_START_LOCAL(__secondary_switched)
	mov	x0, x20
	bl	set_cpu_boot_mode_flag
	str_l	xzr, __early_cpu_boot_status, x3
	adr_l	x5, vectors
	msr	vbar_el1, x5
	isb

	adr_l	x0, secondary_data
	ldr	x2, [x0, #CPU_BOOT_TASK]
	cbz	x2, __secondary_too_slow

	init_cpu_task x2, x1, x3

#ifdef CONFIG_ARM64_PTR_AUTH
	ptrauth_keys_init_cpu x2, x3, x4, x5
#endif

	bl	secondary_start_kernel
	ASM_BUG()
SYM_FUNC_END(__secondary_switched)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值