说明:在默认情况下,本文讲述的都是ARMV8-aarch64架构,linux kernel 5.14, optee3.14, TF-A 2.4, armv8.8
目录
硬件: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位)
软件:各个系统的向量表基地址的设置(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
硬件:armv8-aarch64\arch以及armv7的向量表和基地址寄存器介绍
1、ARMV8 aarch64的异常向量表介绍
我们可以看出,实际上有四组表,每组表有四个异常入口,分别对应同步异常,IRQ,FIQ和serror。
如果发生异常后并没有exception level切换,并且发生异常之前使用的栈指针是SP_EL0,那么使用第一组异常向量表。
如果发生异常后并没有exception level切换,并且发生异常之前使用的栈指针是SP_EL1/2/3,那么使用第二组异常向量表。
如果发生异常导致了exception level切换,并且发生异常之前的exceptionlevel运行在AARCH64模式,那么使用第三组异常向量表。
如果发生异常导致了exception level切换,并且发生异常之前的exceptionlevel运行在AARCH32模式,那么使用第四组异常向量表。
另外我们还可以看到的一点是,每一个异常入口不再仅仅占用4bytes的空间,而是占用0x80 bytes空间,也就是说,每一个异常入口可以放置多条指令,而不仅仅是一条跳转指令
2、ARMV8 aarch32的异常向量表介绍
3、ARMV7 4的异常向量表介绍
4、ARMV8 aarch64的向量表基地址
VBAR(Vector Base Address Register)的寄存器有:
(如果是aarch64)
在开启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
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 11SYM_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#ifdefCONFIG_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#endifSYM_CODE_END(vectors)
注意.align=7,说明该段代码是以2^7=128字节对其的,这和向量表中每一个offset的大小是一致的代码看似非常复杂,其实最终跳转到了b el\()\el\()_\label, 翻译一下,其实就是跳转到了如下这样的函数中
el1_sync_invalid el1_irq_invalid el1_fiq_invalid el1_error_invalidel1_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_rstW(b) vector_undW(ldr) pc, __vectors_start +0x1000W(b) vector_pabtW(b) vector_dabtW(b) vector_addrexcptnW(b) vector_irqW(b) vector_fiq
3、optee中arm64定义的异常向量表
(core/arch/arm/kernel/thread_a64.S).section .text.thread_excp_vect.align 11, INV_INSNFUNC thread_excp_vect ,:/* ----------------------------------------------------- * EL1 with SP0 : 0x0 - 0x180 * ----------------------------------------------------- */.align 7, INV_INSNel1_sync_sp0: store_xregs sp, THREAD_CORE_LOCAL_X0,0,3 b el1_sync_abort check_vector_size el1_sync_sp0.align 7, INV_INSNel1_irq_sp0: store_xregs sp, THREAD_CORE_LOCAL_X0,0,3 b elx_irq check_vector_size el1_irq_sp0.align 7, INV_INSNel1_fiq_sp0: store_xregs sp, THREAD_CORE_LOCAL_X0,0,3 b elx_fiq check_vector_size el1_fiq_sp0.align 7, INV_INSNel1_serror_sp0: b el1_serror_sp0 check_vector_size el1_serror_sp0/* ----------------------------------------------------- * Current EL with SP1: 0x200 - 0x380 * ----------------------------------------------------- */.align 7, INV_INSNel1_sync_sp1: b el1_sync_sp1 check_vector_size el1_sync_sp1.align 7, INV_INSNel1_irq_sp1: b el1_irq_sp1 check_vector_size el1_irq_sp1.align 7, INV_INSNel1_fiq_sp1: b el1_fiq_sp1 check_vector_size el1_fiq_sp1.align 7, INV_INSNel1_serror_sp1: b el1_serror_sp1 check_vector_size el1_serror_sp1/* ----------------------------------------------------- * Lower EL using AArch64 : 0x400 - 0x580 * ----------------------------------------------------- */.align 7, INV_INSNel0_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_INSNel0_irq_a64: restore_mapping b elx_irq check_vector_size el0_irq_a64.align 7, INV_INSNel0_fiq_a64: restore_mapping b elx_fiq check_vector_size el0_fiq_a64.align 7, INV_INSNel0_serror_a64: b el0_serror_a64 check_vector_size el0_serror_a64/* ----------------------------------------------------- * Lower EL using AArch32 : 0x0 - 0x180 * ----------------------------------------------------- */.align 7, INV_INSNel0_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_INSNel0_irq_a32: restore_mapping b elx_irq check_vector_size el0_irq_a32.align 7, INV_INSNel0_fiq_a32: restore_mapping b elx_fiq check_vector_size el0_fiq_a32.align 7, INV_INSNel0_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 5FUNC 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的代码中,在不同的阶段有着不同的异常向量表:
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_exceptionsfunc 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_vectorfunc 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的都是未实现的)
vector_entry sync_exception_sp_el0b report_unhandled_exceptioncheck_vector_size sync_exception_sp_el0vector_entry irq_sp_el0b report_unhandled_interruptcheck_vector_size irq_sp_el0vector_entry fiq_sp_el0b report_unhandled_interruptcheck_vector_size fiq_sp_el0vector_entry serror_sp_el0b report_unhandled_exceptioncheck_vector_size serror_sp_el0
vector_entry sync_exception_sp_elxb report_unhandled_exceptioncheck_vector_size sync_exception_sp_elxvector_entry irq_sp_elxb report_unhandled_interruptcheck_vector_size irq_sp_elxvector_entry fiq_sp_elxb report_unhandled_interruptcheck_vector_size fiq_sp_elxvector_entry serror_sp_elxb report_unhandled_exceptioncheck_vector_size serror_sp_elx
vector_entry sync_exception_aarch64handle_sync_exceptioncheck_vector_size sync_exception_aarch64vector_entry irq_aarch64handle_interrupt_exception irq_aarch64check_vector_size irq_aarch64vector_entry fiq_aarch64handle_interrupt_exception fiq_aarch64check_vector_size fiq_aarch64vector_entry serror_aarch64b report_unhandled_exceptioncheck_vector_size serror_aarch64
vector_entry sync_exception_aarch32handle_sync_exceptioncheck_vector_size sync_exception_aarch32vector_entry irq_aarch32handle_interrupt_exception irq_aarch32check_vector_size irq_aarch32vector_entry fiq_aarch32handle_interrupt_exception fiq_aarch32check_vector_size fiq_aarch32vector_entry serror_aarch32b report_unhandled_exceptioncheck_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_kernelENDPROC(__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)staticunsignedlong __init setup_vectors_base(void){unsignedlong reg =get_cr();set_cr(reg | CR_V);//其实就是将CR_V写入到了cp15, 也就是写入到了VBAR寄存器.return0xffff0000;}
staticinlinevoidset_cr(unsignedlong val){asmvolatile("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)#defineCR_M(1<<0)/* MMU enable */#defineCR_A(1<<1)/* Alignment abort enable */#defineCR_C(1<<2)/* Dcache enable */#defineCR_W(1<<3)/* Write buffer enable */#defineCR_P(1<<4)/* 32-bit exception handler */#defineCR_D(1<<5)/* 32-bit data address range */#defineCR_L(1<<6)/* Implementation defined */#defineCR_B(1<<7)/* Big endian */#defineCR_S(1<<8)/* System MMU protection */#defineCR_R(1<<9)/* ROM MMU protection */#defineCR_F(1<<10)/* Implementation defined */#defineCR_Z(1<<11)/* Implementation defined */#defineCR_I(1<<12)/* Icache enable */#defineCR_V(1<<13)/* Vectors relocated to 0xffff0000 */#defineCR_RR(1<<14)/* Round Robin cache replacement */#defineCR_L4(1<<15)/* LDR pc can set T bit */#defineCR_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)staticvaddr_tget_excp_vect(void){#ifdefCFG_CORE_WORKAROUND_SPECTRE_BP_SECuint32_t midr =read_midr();if(get_midr_implementer(midr)!= MIDR_IMPLEMENTER_ARM)return(vaddr_t)thread_excp_vect;switch(get_midr_primary_part(midr)){#ifdefARM32case CORTEX_A8_PART_NUM:case CORTEX_A9_PART_NUM:case CORTEX_A17_PART_NUM:#endifcase CORTEX_A57_PART_NUM:case CORTEX_A72_PART_NUM:case CORTEX_A73_PART_NUM:case CORTEX_A75_PART_NUM:returnselect_vector((vaddr_t)thread_excp_vect_workaround);#ifdefARM32case CORTEX_A15_PART_NUM:returnselect_vector((vaddr_t)thread_excp_vect_workaround_a15);#endifdefault: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
voidthread_init_per_cpu(void){size_t pos =get_core_pos();structthread_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 retEND_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 lrUNWIND(.fnend)END_FUNC thread_init_vbar
