目录 硬件:armv8-aarch64\arch以及armv7的向量表和基地址寄存器介绍 1、ARMV8 aarch64的异常向量表介绍 2、ARMV8 aarch32的异常向量表介绍 3、ARMV7 4的异常向量表介绍 4、ARMV8 aarch64的向量表基地址 5、ARMV8 aarch的向量表基地址 6、ARMV7的向量表基地址 软件:各个系统的异常向量表(Linux, optee, ATF ...32位/64位) 1、Linux Kernel 中arm64定义的向量表 2、Linux Kernel 中arm定义的向量表 3、optee中arm64定义的异常向量表 4、optee中arm定义的异常向量表 5、在ATF中arm64异常向量表的实现定义 6、、在ATF中arm64异常向量表的实现定义 软件:各个系统的向量表基地址的设置(Linux, optee, ATF ...32位/64位) 1、linux kernel的arm64下设置向量表基地址VBAR 2、linux kernel的arm32下设置向量表基地址VBAR 3、optee中arm64设置向量表基地址VBAR_EL1 4、optee中arm设置向量表基地址VBAR_EL1 扫码进交路群,一起学习、一起成长

软件中定义的向量表,是否和ARM文档中的向量offset一致 向量表的基地址是否写入到了VBAR寄存器 硬件:armv8-aarch64\arch以及armv7的向量表和基地址寄存器介绍 1、ARMV8 aarch64的异常向量表介绍 image.png 我们可以看出,实际上有四组表,每组表有四个异常入口,分别对应同步异常,IRQ,FIQ和serror。

如果发生异常后并没有exception level切换,并且发生异常之前使用的栈指针是SP_EL0,那么使用第一组异常向量表。 如果发生异常后并没有exception level切换,并且发生异常之前使用的栈指针是SP_EL1/2/3,那么使用第二组异常向量表。 如果发生异常导致了exception level切换,并且发生异常之前的exception level运行在AARCH64模式,那么使用第三组异常向量表。 如果发生异常导致了exception level切换,并且发生异常之前的exception level运行在AARCH32模式,那么使用第四组异常向量表。 另外我们还可以看到的一点是,每一个异常入口不再仅仅占用4bytes的空间,而是占用0x80 bytes空间,也就是说,每一个异常入口可以放置多条指令,而不仅仅是一条跳转指令 2、ARMV8 aarch32的异常向量表介绍 image.png 3、ARMV7 4的异常向量表介绍 image.png 4、ARMV8 aarch64的向量表基地址 VBAR(Vector Base Address Register)的寄存器有:

(如果是aarch64)

VBAR_EL1 VBAR_EL2 VBAR_EL3 在开启MMU的系统,VBAR中写入的是虚拟地址,以VBAR_EL1为例,介绍下field的使用: image.png Bits [10:0] reserved

Bits [11:63]: 如果不支持ARMv8.2-LVA(Large VA support:使用64kb页面时,有效虚拟地址达到52bit) (1)、如果支持tagged addresses, bits [55:48]必需都是一样的 (2)、如果不支持tagged addresses , bits [63:48] 必需都是一样的 如果支持ARMv8.2-LVA(Large VA support:使用64kb页面时,有效虚拟地址达到52bit) (1)、如果支持tagged addresses , bits [55:52] 必需都是一样的 (2)、如果不支持tagged addresses , bits [63:52] 必需都是一样的

该寄存器的低11bit是reserve的,11~63表示了Vector Base Address,因此这里的异常向量表基地址是2K对齐的

5、ARMV8 aarch的向量表基地址 如果是aarch32

VBAR HVBAR MVBAR 6、ARMV7的向量表基地址 TODO

软件:各个系统的异常向量表(Linux, optee, ATF …32位/64位) 1、Linux Kernel 中arm64定义的向量表

  1. (linux/arch/arm64/kernel/entry.S)
  2. /*
    • Exception vectors.
  3. */
  4. .pushsection ".entry.text", "ax"
  5. .align 11
  6. SYM_CODE_START(vectors)
  7. kernel_ventry	1, sync_invalid			// Synchronous EL1t
    
  8. kernel_ventry	1, irq_invalid			// IRQ EL1t
    
  9. kernel_ventry	1, fiq_invalid			// FIQ EL1t
    
  10. kernel_ventry	1, error_invalid		// Error EL1t
    
  11. kernel_ventry	1, sync				// Synchronous EL1h
    
  12. kernel_ventry	1, irq				// IRQ EL1h
    
  13. kernel_ventry	1, fiq				// FIQ EL1h
    
  14. kernel_ventry	1, error			// Error EL1h
    
  15. kernel_ventry	0, sync				// Synchronous 64-bit EL0
    
  16. kernel_ventry	0, irq				// IRQ 64-bit EL0
    
  17. kernel_ventry	0, fiq				// FIQ 64-bit EL0
    
  18. kernel_ventry	0, error			// Error 64-bit EL0
    
  19. #ifdef CONFIG_COMPAT
  20. kernel_ventry	0, sync_compat, 32		// Synchronous 32-bit EL0
    
  21. kernel_ventry	0, irq_compat, 32		// IRQ 32-bit EL0
    
  22. kernel_ventry	0, fiq_compat, 32		// FIQ 32-bit EL0
    
  23. kernel_ventry	0, error_compat, 32		// Error 32-bit EL0
    
  24. #else
  25. kernel_ventry	0, sync_invalid, 32		// Synchronous 32-bit EL0
    
  26. kernel_ventry	0, irq_invalid, 32		// IRQ 32-bit EL0
    
  27. kernel_ventry	0, fiq_invalid, 32		// FIQ 32-bit EL0
    
  28. kcernel_ventry	0, error_invalid, 32		// Error 32-bit EL0
    
  29. #endif
  30. SYM_CODE_END(vectors)

注意.align=7,说明该段代码是以2^7=128字节对其的,这和向量表中每一个offset的大小是一致的 代码看似非常复杂,其实最终跳转到了b el()\el()_\label, 翻译一下,其实就是跳转到了如下这样的函数中

  1. el1_sync_invalid
  2. el1_irq_invalid
  3. el1_fiq_invalid
  4. el1_error_invalid
  5. el1_sync
  6. el1_irq
  7. el1_fiq
  8. el1_error
  9. el0_sync
  10. el0_irq
  11. el0_fiq
  12. el0_error 2、Linux Kernel 中arm定义的向量表
  13. .section .stubs, "ax", %progbits
  14. __stubs_start:
  15. @ This must be the first word
  16. .word vector_swi
  17. .section .vectors, "ax", %progbits
  18. __vectors_start:
  19. W(b) vector_rst
  20. W(b) vector_und
  21. W(ldr)	pc, __vectors_start + 0x1000
    
  22. W(b)	vector_pabt
    
  23. W(b)	vector_dabt
    
  24. W(b)	vector_addrexcptn
    
  25. W(b)	vector_irq
    
  26. W(b)	vector_fiq
    

3、optee中arm64定义的异常向量表

  1. (core/arch/arm/kernel/thread_a64.S)
  2. .section .text.thread_excp_vect
  3. .align 11, INV_INSN
  4. FUNC thread_excp_vect , :
  5. /* -----------------------------------------------------
    • EL1 with SP0 : 0x0 - 0x180

  6. */
  7. .align 7, INV_INSN
  8. el1_sync_sp0:
  9. store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
    
  10. b	el1_sync_abort
    
  11. check_vector_size el1_sync_sp0
    
  12. .align	7, INV_INSN
    
  13. el1_irq_sp0:
  14. store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
    
  15. b	elx_irq
    
  16. check_vector_size el1_irq_sp0
    
  17. .align	7, INV_INSN
    
  18. el1_fiq_sp0:
  19. store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
    
  20. b	elx_fiq
    
  21. check_vector_size el1_fiq_sp0
    
  22. .align	7, INV_INSN
    
  23. el1_serror_sp0:
  24. b	el1_serror_sp0
    
  25. check_vector_size el1_serror_sp0
    
  26. /* -----------------------------------------------------
    
  27.  * Current EL with SP1: 0x200 - 0x380
    
  28.  * -----------------------------------------------------
    
  29.  */
    
  30. .align	7, INV_INSN
    
  31. el1_sync_sp1:
  32. b	el1_sync_sp1
    
  33. check_vector_size el1_sync_sp1
    
  34. .align	7, INV_INSN
    
  35. el1_irq_sp1:
  36. b	el1_irq_sp1
    
  37. check_vector_size el1_irq_sp1
    
  38. .align	7, INV_INSN
    
  39. el1_fiq_sp1:
  40. b	el1_fiq_sp1
    
  41. check_vector_size el1_fiq_sp1
    
  42. .align	7, INV_INSN
    
  43. el1_serror_sp1:
  44. b	el1_serror_sp1
    
  45. check_vector_size el1_serror_sp1
    
  46. /* -----------------------------------------------------
    
  47.  * Lower EL using AArch64 : 0x400 - 0x580
    
  48.  * -----------------------------------------------------
    
  49.  */
    
  50. .align	7, INV_INSN
    
  51. el0_sync_a64:
  52. restore_mapping
    
  53. mrs	x2, esr_el1
    
  54. mrs	x3, sp_el0
    
  55. lsr	x2, x2, #ESR_EC_SHIFT
    
  56. cmp	x2, #ESR_EC_AARCH64_SVC
    
  57. b.eq	el0_svc
    
  58. b	el0_sync_abort
    
  59. check_vector_size el0_sync_a64
    
  60. .align	7, INV_INSN
    
  61. el0_irq_a64:
  62. restore_mapping
    
  63. b	elx_irq
    
  64. check_vector_size el0_irq_a64
    
  65. .align	7, INV_INSN
    
  66. el0_fiq_a64:
  67. restore_mapping
    
  68. b	elx_fiq
    
  69. check_vector_size el0_fiq_a64
    
  70. .align	7, INV_INSN
    
  71. el0_serror_a64:
  72. b   	el0_serror_a64
    
  73. check_vector_size el0_serror_a64
    
  74. /* -----------------------------------------------------
    
  75.  * Lower EL using AArch32 : 0x0 - 0x180
    
  76.  * -----------------------------------------------------
    
  77.  */
    
  78. .align	7, INV_INSN
    
  79. el0_sync_a32:
  80. restore_mapping
    
  81. mrs	x2, esr_el1
    
  82. mrs x3, sp_el0
  83. lsr x2, x2, #ESR_EC_SHIFT
  84. cmp x2, #ESR_EC_AARCH32_SVC
  85. b.eq el0_svc
  86. b el0_sync_abort
  87. check_vector_size el0_sync_a32
  88. .align 7, INV_INSN
  89. el0_irq_a32:
  90. restore_mapping
  91. b elx_irq
  92. check_vector_size el0_irq_a32
  93. .align 7, INV_INSN
  94. el0_fiq_a32:
  95. restore_mapping
  96. b elx_fiq
  97. check_vector_size el0_fiq_a32
  98. .align 7, INV_INSN
  99. el0_serror_a32:
  100. b el0_serror_a32
  101. check_vector_size el0_serror_a32 align 7,对齐方式为7,也就是0x80对齐,恰好符合armv7-aarch64中文档中的向量表的offset偏移

4、optee中arm定义的异常向量表

  1. (core/arch/arm/kernel/thread_a32.S)
  2. .section .text.thread_excp_vect
  3.     .align	5
    
  4. FUNC thread_excp_vect , :
  5. UNWIND( .fnstart)
  6. UNWIND( .cantunwind)
  7. b . /* Reset */
  8. b thread_und_handler /* Undefined instruction */
  9. b thread_svc_handler /* System call */
  10. b	thread_pabort_handler	/* Prefetch abort		*/
    
  11. b	thread_dabort_handler	/* Data abort			*/
    
  12. b	.			/* Reserved			*/
    
  13. b	thread_irq_handler	/* IRQ				*/
    
  14. b	thread_fiq_handler	/* FIQ				*/
    

一条指令占4个字节,所以这里也是和aarch32的异常向量表的offset一一对应的

5、在ATF中arm64异常向量表的实现定义 在ATF的代码中,在不同的阶段有着不同的异常向量表:

在bl1阶段使用bl1_exceptions 在bl2阶段使用bl2_entrypoint 在bl31及其之后使用runtime_exceptions

  1. func bl1_entrypoint
  2. ......
  3. el3_entrypoint_common \
  4.  _set_endian=1					\
    
  5.  _warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS	\
    
  6.  _secondary_cold_boot=!COLD_BOOT_SINGLE_CPU	\
    
  7.  _init_memory=1					\
    
  8.  _init_c_runtime=1				\
    
  9.  _exception_vectors=bl1_exceptions
    
  10. func bl2_entrypoint
  11. el3_entrypoint_common					\
    
  12. 	_set_endian=0					\
    
  13. 	_warm_boot_mailbox=0				\
    
  14. 	_secondary_cold_boot=0				\
    
  15. 	_secondary_cpu = 0				\
    
  16. 	_init_memory=0					\
    
  17. 	_init_c_runtime=1				\
    
  18. 	_exception_vectors=bl2_vector
    
  19. func bl31_entrypoint
  20. ......
  21. el3_entrypoint_common					\
    
  22. 	_set_endian=0					\
    
  23. 	_warm_boot_mailbox=0				\
    
  24. 	_secondary_cold_boot=0				\
    
  25. 	_init_memory=0					\
    
  26. 	_init_c_runtime=1				\
    
  27. 	_exception_vectors=runtime_exceptions
    

我们常说的ATF中的向量表,其实就是bl31之后使用runtime_exceptions向量表,下面重点介绍下

(注意 : 带unhandled的都是未实现的)

用于处理EL0产生异常时的entire(对应第一行向量表)

  1. vector_entry sync_exception_sp_el0
  2. b report_unhandled_exception
  3. check_vector_size sync_exception_sp_el0
  4. vector_entry irq_sp_el0
  5. b report_unhandled_interrupt
  6. check_vector_size irq_sp_el0
  7. vector_entry fiq_sp_el0
  8. b report_unhandled_interrupt
  9. check_vector_size fiq_sp_el0
  10. vector_entry serror_sp_el0
  11. b report_unhandled_exception
  12. check_vector_size serror_sp_el0 用于处理当前ELx产生异常时的entire(对应第二行向量表)
  13. vector_entry sync_exception_sp_elx
  14. b report_unhandled_exception
  15. check_vector_size sync_exception_sp_elx
  16. vector_entry irq_sp_elx
  17. b report_unhandled_interrupt
  18. check_vector_size irq_sp_elx
  19. vector_entry fiq_sp_elx
  20. b report_unhandled_interrupt
  21. check_vector_size fiq_sp_elx
  22. vector_entry serror_sp_elx
  23. b report_unhandled_exception
  24. check_vector_size serror_sp_elx 用于处理AArch64指令产生的异常,且发生了EL的迁移的entire(对应第三行向量表)
  25. vector_entry sync_exception_aarch64
  26. handle_sync_exception
  27. check_vector_size sync_exception_aarch64
  28. vector_entry irq_aarch64
  29. handle_interrupt_exception irq_aarch64
  30. check_vector_size irq_aarch64
  31. vector_entry fiq_aarch64
  32. handle_interrupt_exception fiq_aarch64
  33. check_vector_size fiq_aarch64
  34. vector_entry serror_aarch64
  35. b report_unhandled_exception
  36. check_vector_size serror_aarch64 用于处理AArch32指令产生的异常,且发生了EL的迁移的entire(对应第四行向量表)
  37. vector_entry sync_exception_aarch32
  38. handle_sync_exception
  39. check_vector_size sync_exception_aarch32
  40. vector_entry irq_aarch32
  41. handle_interrupt_exception irq_aarch32
  42. check_vector_size irq_aarch32
  43. vector_entry fiq_aarch32
  44. handle_interrupt_exception fiq_aarch32
  45. check_vector_size fiq_aarch32
  46. vector_entry serror_aarch32
  47. b report_unhandled_exception
  48. check_vector_size serror_aarch32 6、、在ATF中arm64异常向量表的实现定义 TODO

软件:各个系统的向量表基地址的设置(Linux, optee, ATF …32位/64位) 1、linux kernel的arm64下设置向量表基地址VBAR 在__primary_switched将vectors写入到了VBAR_EL1

  1. __primary_switched:
  2. adrp x4, init_thread_union
  3. add sp, x4, #THREAD_SIZE
  4. adr_l x5, init_task
  5. msr sp_el0, x5 // Save thread_info
  6. adr_l x8, vectors // load VBAR_EL1 with virtual
  7. msr vbar_el1, x8 // vector table address
  8. isb
  9. stp	xzr, x30, [sp, #-16]!
    
  10. mov	x29, sp
    
  11. str_l	x21, __fdt_pointer, x5		// Save FDT pointer
    
  12. ..
  13. b	start_kernel
    
  14. ENDPROC(__primary_switched) 2、linux kernel的arm32下设置向量表基地址VBAR Linux Kernel的arm的异常向量表定义在__vectors_start处

在vmlinux.lds.S描述了__vectors_start的起始位置,从0xffff0000开始**

  1. (kernel-4.14/arch/arm/kernel/vmlinux.lds.S)
  2. __vectors_start = .;
  3. .vectors 0xffff0000 : AT(__vectors_start) {
  4. *(.vectors)
  5. }
  6. . = __vectors_start + SIZEOF(.vectors);
  7. __vectors_end = .;
  8. __stubs_start = .;
  9. .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {
  10. *(.stubs)
    
  11. }
  12. . = __stubs_start + SIZEOF(.stubs);
  13. __stubs_end = .; 在nommu.c中,setup_vectors_base函数通过操作cp15协处理器来写入VBAR.
  14. (kernel-4.14/arch/arm/mm/nommu.c)
  15. static unsigned long __init setup_vectors_base(void)
  16. {
  17. unsigned long reg = get_cr();
  18. set_cr(reg | CR_V); //其实就是将CR_V写入到了cp15, 也就是写入到了VBAR寄存器.
  19. return 0xffff0000;
  20. }
  21. static inline void set_cr(unsigned long val)
  22. {
  23. asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR"
  24. : : "r" (val) : "cc");
  25. isb();
  26. } 而CR_V的定义在cp15.h中,恰好就是0xffff0000(也就是最地址处,空出64KB的地方,给vector使用)
  27. (kernel-4.14/arch/arm/include/asm/cp15.h)
  28. #define CR_M (1 << 0) /* MMU enable */
  29. #define CR_A (1 << 1) /* Alignment abort enable */
  30. #define CR_C (1 << 2) /* Dcache enable */
  31. #define CR_W (1 << 3) /* Write buffer enable */
  32. #define CR_P (1 << 4) /* 32-bit exception handler */
  33. #define CR_D (1 << 5) /* 32-bit data address range */
  34. #define CR_L (1 << 6) /* Implementation defined */
  35. #define CR_B (1 << 7) /* Big endian */
  36. #define CR_S (1 << 8) /* System MMU protection */
  37. #define CR_R (1 << 9) /* ROM MMU protection */
  38. #define CR_F (1 << 10) /* Implementation defined */
  39. #define CR_Z (1 << 11) /* Implementation defined */
  40. #define CR_I (1 << 12) /* Icache enable */
  41. #define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */
  42. #define CR_RR (1 << 14) /* Round Robin cache replacement */
  43. #define CR_L4 (1 << 15) /* LDR pc can set T bit */
  44. #define CR_DT (1 << 16) setup_vectors_base是在开机的时候调用的 setup_arch ----> arm_memblock_init ----> arm_mm_memblock_reserve ---> setup_vectors_base 3、optee中arm64设置向量表基地址VBAR_EL1
  45. get_excp_vect()函数获取到thread_a64.S中定义的向量表thread_excp_vect地址
  46. (core/arch/arm/kernel/thread.c)
  47. static vaddr_t get_excp_vect(void)
  48. {
  49. #ifdef CFG_CORE_WORKAROUND_SPECTRE_BP_SEC
  50. uint32_t midr = read_midr();
  51. if (get_midr_implementer(midr) != MIDR_IMPLEMENTER_ARM)
  52.  return (vaddr_t)thread_excp_vect;
    
  53. switch (get_midr_primary_part(midr)) {
    
  54. #ifdef ARM32
  55. case CORTEX_A8_PART_NUM:
    
  56. case CORTEX_A9_PART_NUM:
    
  57. case CORTEX_A17_PART_NUM:
    
  58. #endif
  59. case CORTEX_A57_PART_NUM:
    
  60. case CORTEX_A72_PART_NUM:
    
  61. case CORTEX_A73_PART_NUM:
    
  62. case CORTEX_A75_PART_NUM:
    
  63. 	return select_vector((vaddr_t)thread_excp_vect_workaround);
    
  64. #ifdef ARM32
  65. case CORTEX_A15_PART_NUM:
    
  66. 	return select_vector((vaddr_t)thread_excp_vect_workaround_a15);
    
  67. #endif
  68. default:
    
  69. 	return (vaddr_t)thread_excp_vect;
    
  70. }
    
  71. #endif /CFG_CORE_WORKAROUND_SPECTRE_BP_SEC/
  72. return (vaddr_t)thread_excp_vect;
    
  73. } 在thread_init_per_cpu()时,将向量表基地址写入到VBAR_EL1
  74. void thread_init_per_cpu(void)
  75. {
  76. size_t pos = get_core_pos();
  77. struct thread_core_local *l = thread_get_core_local();
  78. init_sec_mon(pos);
  79. set_tmp_stack(l, GET_STACK(stack_tmp[pos]) - STACK_TMP_OFFS);
  80. set_abt_stack(l, GET_STACK(stack_abt[pos]));
  81. thread_init_vbar(get_excp_vect());
    
  82. } thread_init_vbar函数完成将基地址写入VBAR_EL1(将参数1写入到VBAR_EL1)
  83. (core/arch/arm/kernel/thread_a64.S)
  84. FUNC thread_init_vbar , :
  85. msr vbar_el1, x0
  86. ret
  87. END_FUNC thread_init_vbar

4、optee中arm设置向量表基地址VBAR_EL1 其流程同aarch64的流程相同,都是thread_init_per_cpu()---->thread_init_vbar ()

  1. (core/arch/arm/kernel/thread_a32.S)
  2. FUNC thread_init_vbar , :
  3. UNWIND( .fnstart)
  4. /* Set vector (VBAR) */
  5. write_vbar r0
  6. bx lr
  7. UNWIND( .fnend)
  8. END_FUNC thread_init_vbar

扫码进交路群,一起学习、一起成长 (这是永久二维码,用不过期) image.png