目录 硬件: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的异常向量表介绍 我们可以看出,实际上有四组表,每组表有四个异常入口,分别对应同步异常,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的异常向量表介绍 3、ARMV7 4的异常向量表介绍 4、ARMV8 aarch64的向量表基地址 VBAR(Vector Base Address Register)的寄存器有:
(如果是aarch64)
VBAR_EL1 VBAR_EL2 VBAR_EL3 在开启MMU的系统,VBAR中写入的是虚拟地址,以VBAR_EL1为例,介绍下field的使用: 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定义的向量表
- (linux/arch/arm64/kernel/entry.S)
- /*
-
- Exception vectors.
- */
- .pushsection ".entry.text", "ax"
- .align 11
- SYM_CODE_START(vectors)
-
kernel_ventry 1, sync_invalid // Synchronous EL1t
-
kernel_ventry 1, irq_invalid // IRQ EL1t
-
kernel_ventry 1, fiq_invalid // FIQ EL1t
-
kernel_ventry 1, error_invalid // Error EL1t
-
kernel_ventry 1, sync // Synchronous EL1h
-
kernel_ventry 1, irq // IRQ EL1h
-
kernel_ventry 1, fiq // FIQ EL1h
-
kernel_ventry 1, error // Error EL1h
-
kernel_ventry 0, sync // Synchronous 64-bit EL0
-
kernel_ventry 0, irq // IRQ 64-bit EL0
-
kernel_ventry 0, fiq // FIQ 64-bit EL0
-
kernel_ventry 0, error // Error 64-bit EL0
- #ifdef CONFIG_COMPAT
-
kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0
-
kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0
-
kernel_ventry 0, fiq_compat, 32 // FIQ 32-bit EL0
-
kernel_ventry 0, error_compat, 32 // Error 32-bit EL0
- #else
-
kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0
-
kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0
-
kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0
-
kcernel_ventry 0, error_invalid, 32 // Error 32-bit EL0
- #endif
- SYM_CODE_END(vectors)
注意.align=7,说明该段代码是以2^7=128字节对其的,这和向量表中每一个offset的大小是一致的 代码看似非常复杂,其实最终跳转到了b el()\el()_\label, 翻译一下,其实就是跳转到了如下这样的函数中
- el1_sync_invalid
- el1_irq_invalid
- el1_fiq_invalid
- el1_error_invalid
- el1_sync
- el1_irq
- el1_fiq
- el1_error
- el0_sync
- el0_irq
- el0_fiq
- el0_error 2、Linux Kernel 中arm定义的向量表
- .section .stubs, "ax", %progbits
- __stubs_start:
- @ This must be the first word
- .word vector_swi
- .section .vectors, "ax", %progbits
- __vectors_start:
- W(b) vector_rst
- W(b) vector_und
-
W(ldr) pc, __vectors_start + 0x1000
-
W(b) vector_pabt
-
W(b) vector_dabt
-
W(b) vector_addrexcptn
-
W(b) vector_irq
-
W(b) vector_fiq
3、optee中arm64定义的异常向量表
- (core/arch/arm/kernel/thread_a64.S)
- .section .text.thread_excp_vect
- .align 11, INV_INSN
- FUNC thread_excp_vect , :
- /* -----------------------------------------------------
-
- EL1 with SP0 : 0x0 - 0x180
-
-
- */
- .align 7, INV_INSN
- el1_sync_sp0:
-
store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
-
b el1_sync_abort
-
check_vector_size el1_sync_sp0
-
.align 7, INV_INSN
- el1_irq_sp0:
-
store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
-
b elx_irq
-
check_vector_size el1_irq_sp0
-
.align 7, INV_INSN
- el1_fiq_sp0:
-
store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3
-
b elx_fiq
-
check_vector_size el1_fiq_sp0
-
.align 7, INV_INSN
- el1_serror_sp0:
-
b el1_serror_sp0
-
check_vector_size el1_serror_sp0
-
/* -----------------------------------------------------
-
* Current EL with SP1: 0x200 - 0x380
-
* -----------------------------------------------------
-
*/
-
.align 7, INV_INSN
- el1_sync_sp1:
-
b el1_sync_sp1
-
check_vector_size el1_sync_sp1
-
.align 7, INV_INSN
- el1_irq_sp1:
-
b el1_irq_sp1
-
check_vector_size el1_irq_sp1
-
.align 7, INV_INSN
- el1_fiq_sp1:
-
b el1_fiq_sp1
-
check_vector_size el1_fiq_sp1
-
.align 7, INV_INSN
- el1_serror_sp1:
-
b el1_serror_sp1
-
check_vector_size el1_serror_sp1
-
/* -----------------------------------------------------
-
* Lower EL using AArch64 : 0x400 - 0x580
-
* -----------------------------------------------------
-
*/
-
.align 7, INV_INSN
- el0_sync_a64:
-
restore_mapping
-
mrs x2, esr_el1
-
mrs x3, sp_el0
-
lsr x2, x2, #ESR_EC_SHIFT
-
cmp x2, #ESR_EC_AARCH64_SVC
-
b.eq el0_svc
-
b el0_sync_abort
-
check_vector_size el0_sync_a64
-
.align 7, INV_INSN
- el0_irq_a64:
-
restore_mapping
-
b elx_irq
-
check_vector_size el0_irq_a64
-
.align 7, INV_INSN
- el0_fiq_a64:
-
restore_mapping
-
b elx_fiq
-
check_vector_size el0_fiq_a64
-
.align 7, INV_INSN
- el0_serror_a64:
-
b el0_serror_a64
-
check_vector_size el0_serror_a64
-
/* -----------------------------------------------------
-
* Lower EL using AArch32 : 0x0 - 0x180
-
* -----------------------------------------------------
-
*/
-
.align 7, INV_INSN
- el0_sync_a32:
-
restore_mapping
-
mrs x2, esr_el1
- mrs x3, sp_el0
- lsr x2, x2, #ESR_EC_SHIFT
- cmp x2, #ESR_EC_AARCH32_SVC
- b.eq el0_svc
- b el0_sync_abort
- check_vector_size el0_sync_a32
- .align 7, INV_INSN
- el0_irq_a32:
- restore_mapping
- b elx_irq
- check_vector_size el0_irq_a32
- .align 7, INV_INSN
- el0_fiq_a32:
- restore_mapping
- b elx_fiq
- check_vector_size el0_fiq_a32
- .align 7, INV_INSN
- el0_serror_a32:
- b el0_serror_a32
- check_vector_size el0_serror_a32 align 7,对齐方式为7,也就是0x80对齐,恰好符合armv7-aarch64中文档中的向量表的offset偏移
4、optee中arm定义的异常向量表
- (core/arch/arm/kernel/thread_a32.S)
- .section .text.thread_excp_vect
-
.align 5
- FUNC thread_excp_vect , :
- UNWIND( .fnstart)
- UNWIND( .cantunwind)
- b . /* Reset */
- b thread_und_handler /* Undefined instruction */
- b thread_svc_handler /* System call */
-
b thread_pabort_handler /* Prefetch abort */
-
b thread_dabort_handler /* Data abort */
-
b . /* Reserved */
-
b thread_irq_handler /* IRQ */
-
b thread_fiq_handler /* FIQ */
一条指令占4个字节,所以这里也是和aarch32的异常向量表的offset一一对应的
5、在ATF中arm64异常向量表的实现定义 在ATF的代码中,在不同的阶段有着不同的异常向量表:
在bl1阶段使用bl1_exceptions 在bl2阶段使用bl2_entrypoint 在bl31及其之后使用runtime_exceptions
- func bl1_entrypoint
- ......
- el3_entrypoint_common \
-
_set_endian=1 \
-
_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \
-
_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \
-
_init_memory=1 \
-
_init_c_runtime=1 \
-
_exception_vectors=bl1_exceptions
- func bl2_entrypoint
-
el3_entrypoint_common \
-
_set_endian=0 \
-
_warm_boot_mailbox=0 \
-
_secondary_cold_boot=0 \
-
_secondary_cpu = 0 \
-
_init_memory=0 \
-
_init_c_runtime=1 \
-
_exception_vectors=bl2_vector
- func bl31_entrypoint
- ......
-
el3_entrypoint_common \
-
_set_endian=0 \
-
_warm_boot_mailbox=0 \
-
_secondary_cold_boot=0 \
-
_init_memory=0 \
-
_init_c_runtime=1 \
-
_exception_vectors=runtime_exceptions
我们常说的ATF中的向量表,其实就是bl31之后使用runtime_exceptions向量表,下面重点介绍下
(注意 : 带unhandled的都是未实现的)
用于处理EL0产生异常时的entire(对应第一行向量表)
- vector_entry sync_exception_sp_el0
- b report_unhandled_exception
- check_vector_size sync_exception_sp_el0
- vector_entry irq_sp_el0
- b report_unhandled_interrupt
- check_vector_size irq_sp_el0
- vector_entry fiq_sp_el0
- b report_unhandled_interrupt
- check_vector_size fiq_sp_el0
- vector_entry serror_sp_el0
- b report_unhandled_exception
- check_vector_size serror_sp_el0 用于处理当前ELx产生异常时的entire(对应第二行向量表)
- vector_entry sync_exception_sp_elx
- b report_unhandled_exception
- check_vector_size sync_exception_sp_elx
- vector_entry irq_sp_elx
- b report_unhandled_interrupt
- check_vector_size irq_sp_elx
- vector_entry fiq_sp_elx
- b report_unhandled_interrupt
- check_vector_size fiq_sp_elx
- vector_entry serror_sp_elx
- b report_unhandled_exception
- check_vector_size serror_sp_elx 用于处理AArch64指令产生的异常,且发生了EL的迁移的entire(对应第三行向量表)
- vector_entry sync_exception_aarch64
- handle_sync_exception
- check_vector_size sync_exception_aarch64
- vector_entry irq_aarch64
- handle_interrupt_exception irq_aarch64
- check_vector_size irq_aarch64
- vector_entry fiq_aarch64
- handle_interrupt_exception fiq_aarch64
- check_vector_size fiq_aarch64
- vector_entry serror_aarch64
- b report_unhandled_exception
- check_vector_size serror_aarch64 用于处理AArch32指令产生的异常,且发生了EL的迁移的entire(对应第四行向量表)
- vector_entry sync_exception_aarch32
- handle_sync_exception
- check_vector_size sync_exception_aarch32
- vector_entry irq_aarch32
- handle_interrupt_exception irq_aarch32
- check_vector_size irq_aarch32
- vector_entry fiq_aarch32
- handle_interrupt_exception fiq_aarch32
- check_vector_size fiq_aarch32
- vector_entry serror_aarch32
- b report_unhandled_exception
- check_vector_size serror_aarch32 6、、在ATF中arm64异常向量表的实现定义 TODO
软件:各个系统的向量表基地址的设置(Linux, optee, ATF …32位/64位) 1、linux kernel的arm64下设置向量表基地址VBAR 在__primary_switched将vectors写入到了VBAR_EL1
- __primary_switched:
- adrp x4, init_thread_union
- add sp, x4, #THREAD_SIZE
- adr_l x5, init_task
- msr sp_el0, x5 // Save thread_info
- adr_l x8, vectors // load VBAR_EL1 with virtual
- msr vbar_el1, x8 // vector table address
- isb
-
stp xzr, x30, [sp, #-16]!
-
mov x29, sp
-
str_l x21, __fdt_pointer, x5 // Save FDT pointer
- ..
-
b start_kernel
- ENDPROC(__primary_switched) 2、linux kernel的arm32下设置向量表基地址VBAR Linux Kernel的arm的异常向量表定义在__vectors_start处
在vmlinux.lds.S描述了__vectors_start的起始位置,从0xffff0000开始**
- (kernel-4.14/arch/arm/kernel/vmlinux.lds.S)
- __vectors_start = .;
- .vectors 0xffff0000 : AT(__vectors_start) {
- *(.vectors)
- }
- . = __vectors_start + SIZEOF(.vectors);
- __vectors_end = .;
- __stubs_start = .;
- .stubs ADDR(.vectors) + 0x1000 : AT(__stubs_start) {
-
*(.stubs)
- }
- . = __stubs_start + SIZEOF(.stubs);
- __stubs_end = .; 在nommu.c中,setup_vectors_base函数通过操作cp15协处理器来写入VBAR.
- (kernel-4.14/arch/arm/mm/nommu.c)
- static unsigned long __init setup_vectors_base(void)
- {
- unsigned long reg = get_cr();
- set_cr(reg | CR_V); //其实就是将CR_V写入到了cp15, 也就是写入到了VBAR寄存器.
- return 0xffff0000;
- }
- static inline void set_cr(unsigned long val)
- {
- asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR"
- : : "r" (val) : "cc");
- isb();
- } 而CR_V的定义在cp15.h中,恰好就是0xffff0000(也就是最地址处,空出64KB的地方,给vector使用)
- (kernel-4.14/arch/arm/include/asm/cp15.h)
- #define CR_M (1 << 0) /* MMU enable */
- #define CR_A (1 << 1) /* Alignment abort enable */
- #define CR_C (1 << 2) /* Dcache enable */
- #define CR_W (1 << 3) /* Write buffer enable */
- #define CR_P (1 << 4) /* 32-bit exception handler */
- #define CR_D (1 << 5) /* 32-bit data address range */
- #define CR_L (1 << 6) /* Implementation defined */
- #define CR_B (1 << 7) /* Big endian */
- #define CR_S (1 << 8) /* System MMU protection */
- #define CR_R (1 << 9) /* ROM MMU protection */
- #define CR_F (1 << 10) /* Implementation defined */
- #define CR_Z (1 << 11) /* Implementation defined */
- #define CR_I (1 << 12) /* Icache enable */
- #define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */
- #define CR_RR (1 << 14) /* Round Robin cache replacement */
- #define CR_L4 (1 << 15) /* LDR pc can set T bit */
- #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
- get_excp_vect()函数获取到thread_a64.S中定义的向量表thread_excp_vect地址
- (core/arch/arm/kernel/thread.c)
- static vaddr_t get_excp_vect(void)
- {
- #ifdef CFG_CORE_WORKAROUND_SPECTRE_BP_SEC
- uint32_t midr = read_midr();
- if (get_midr_implementer(midr) != MIDR_IMPLEMENTER_ARM)
-
return (vaddr_t)thread_excp_vect;
-
switch (get_midr_primary_part(midr)) {
- #ifdef ARM32
-
case CORTEX_A8_PART_NUM:
-
case CORTEX_A9_PART_NUM:
-
case CORTEX_A17_PART_NUM:
- #endif
-
case CORTEX_A57_PART_NUM:
-
case CORTEX_A72_PART_NUM:
-
case CORTEX_A73_PART_NUM:
-
case CORTEX_A75_PART_NUM:
-
return select_vector((vaddr_t)thread_excp_vect_workaround);
- #ifdef ARM32
-
case CORTEX_A15_PART_NUM:
-
return select_vector((vaddr_t)thread_excp_vect_workaround_a15);
- #endif
-
default:
-
return (vaddr_t)thread_excp_vect;
-
}
- #endif /CFG_CORE_WORKAROUND_SPECTRE_BP_SEC/
-
return (vaddr_t)thread_excp_vect;
- } 在thread_init_per_cpu()时,将向量表基地址写入到VBAR_EL1
- void thread_init_per_cpu(void)
- {
- size_t pos = get_core_pos();
- struct thread_core_local *l = thread_get_core_local();
- init_sec_mon(pos);
- set_tmp_stack(l, GET_STACK(stack_tmp[pos]) - STACK_TMP_OFFS);
- set_abt_stack(l, GET_STACK(stack_abt[pos]));
-
thread_init_vbar(get_excp_vect());
- } thread_init_vbar函数完成将基地址写入VBAR_EL1(将参数1写入到VBAR_EL1)
- (core/arch/arm/kernel/thread_a64.S)
- FUNC thread_init_vbar , :
- msr vbar_el1, x0
- ret
- END_FUNC thread_init_vbar
4、optee中arm设置向量表基地址VBAR_EL1 其流程同aarch64的流程相同,都是thread_init_per_cpu()---->thread_init_vbar ()
- (core/arch/arm/kernel/thread_a32.S)
- FUNC thread_init_vbar , :
- UNWIND( .fnstart)
- /* Set vector (VBAR) */
- write_vbar r0
- bx lr
- UNWIND( .fnend)
- END_FUNC thread_init_vbar
扫码进交路群,一起学习、一起成长 (这是永久二维码,用不过期)