Linux Kernel/optee/ATF等操作系统的异常向量表的速查
引流關(guān)鍵詞: IRQ,FIQ,Serror, 中斷,同步異常,異步異常,TF-A,TF-M,ATF,TrustedFirmware,trustzone,TEE,optee,trusty,tlk,lk,armv8,armv9,arm,secureboot,BL31,BL32,BL1,BL2,hypervisor,終端安全,secureboot,security,virtulization
快速鏈接:
.
👉👉👉 個人博客筆記導(dǎo)讀目錄(全部) 👈👈👈
說明:
在默認情況下,本文講述的都是ARMV8-aarch64架構(gòu),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的向量表基地址
- 軟件:各個系統(tǒng)的異常向量表(Linux, optee, ATF ...32位/64位)
- 1、Linux Kernel 中arm64定義的向量表
- 2、Linux Kernel 中arm定義的向量表
- 3、optee中arm64定義的異常向量表
- 4、optee中arm定義的異常向量表
- 5、在ATF中arm64異常向量表的實現(xiàn)定義
- 6、、在ATF中arm64異常向量表的實現(xiàn)定義
- 軟件:各個系統(tǒng)的向量表基地址的設(shè)置(Linux, optee, ATF ...32位/64位)
- 1、linux kernel的arm64下設(shè)置向量表基地址VBAR
- 2、linux kernel的arm32下設(shè)置向量表基地址VBAR
- 3、optee中arm64設(shè)置向量表基地址VBAR_EL1
- 4、optee中arm設(shè)置向量表基地址VBAR_EL1
- 軟件中定義的向量表,是否和ARM文檔中的向量offset一致
- 向量表的基地址是否寫入到了VBAR寄存器
硬件:armv8-aarch64\arch以及armv7的向量表和基地址寄存器介紹
1、ARMV8 aarch64的異常向量表介紹
我們可以看出,實際上有四組表,每組表有四個異常入口,分別對應(yīng)同步異常,IRQ,FIQ和serror。
- 如果發(fā)生異常后并沒有exception level切換,并且發(fā)生異常之前使用的棧指針是SP_EL0,那么使用第一組異常向量表。
- 如果發(fā)生異常后并沒有exception level切換,并且發(fā)生異常之前使用的棧指針是SP_EL1/2/3,那么使用第二組異常向量表。
- 如果發(fā)生異常導(dǎo)致了exception level切換,并且發(fā)生異常之前的exception
level運行在AARCH64模式,那么使用第三組異常向量表。 - 如果發(fā)生異常導(dǎo)致了exception level切換,并且發(fā)生異常之前的exception
level運行在AARCH32模式,那么使用第四組異常向量表。
另外我們還可以看到的一點是,每一個異常入口不再僅僅占用4bytes的空間,而是占用0x80 bytes空間,也就是說,每一個異常入口可以放置多條指令,而不僅僅是一條跳轉(zhuǎn)指令
2、ARMV8 aarch32的異常向量表介紹
3、ARMV7 4的異常向量表介紹
4、ARMV8 aarch64的向量表基地址
VBAR(Vector Base Address Register)的寄存器有:
(如果是aarch64)
- VBAR_EL1
- VBAR_EL2
- VBAR_EL3
在開啟MMU的系統(tǒng),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
軟件:各個系統(tǒng)的異常向量表(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 EL1tkernel_ventry 1, irq_invalid // IRQ EL1tkernel_ventry 1, fiq_invalid // FIQ EL1tkernel_ventry 1, error_invalid // Error EL1tkernel_ventry 1, sync // Synchronous EL1hkernel_ventry 1, irq // IRQ EL1hkernel_ventry 1, fiq // FIQ EL1hkernel_ventry 1, error // Error EL1hkernel_ventry 0, sync // Synchronous 64-bit EL0kernel_ventry 0, irq // IRQ 64-bit EL0kernel_ventry 0, fiq // FIQ 64-bit EL0kernel_ventry 0, error // Error 64-bit EL0#ifdef CONFIG_COMPATkernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0kernel_ventry 0, fiq_compat, 32 // FIQ 32-bit EL0kernel_ventry 0, error_compat, 32 // Error 32-bit EL0 #elsekernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0kcernel_ventry 0, error_invalid, 32 // Error 32-bit EL0 #endif SYM_CODE_END(vectors)注意.align=7,說明該段代碼是以2^7=128字節(jié)對其的,這和向量表中每一個offset的大小是一致的
代碼看似非常復(fù)雜,其實最終跳轉(zhuǎn)到了b el\()\el\()_\label, 翻譯一下,其實就是跳轉(zhuǎn)到了如下這樣的函數(shù)中
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_fiq3、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, 3b el1_sync_abortcheck_vector_size el1_sync_sp0.align 7, INV_INSN el1_irq_sp0:store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3b elx_irqcheck_vector_size el1_irq_sp0.align 7, INV_INSN el1_fiq_sp0:store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 3b elx_fiqcheck_vector_size el1_fiq_sp0.align 7, INV_INSN el1_serror_sp0:b el1_serror_sp0check_vector_size el1_serror_sp0/* -----------------------------------------------------* Current EL with SP1: 0x200 - 0x380* -----------------------------------------------------*/.align 7, INV_INSN el1_sync_sp1:b el1_sync_sp1check_vector_size el1_sync_sp1.align 7, INV_INSN el1_irq_sp1:b el1_irq_sp1check_vector_size el1_irq_sp1.align 7, INV_INSN el1_fiq_sp1:b el1_fiq_sp1check_vector_size el1_fiq_sp1.align 7, INV_INSN el1_serror_sp1:b el1_serror_sp1check_vector_size el1_serror_sp1/* -----------------------------------------------------* Lower EL using AArch64 : 0x400 - 0x580* -----------------------------------------------------*/.align 7, INV_INSN el0_sync_a64:restore_mappingmrs x2, esr_el1mrs x3, sp_el0lsr x2, x2, #ESR_EC_SHIFTcmp x2, #ESR_EC_AARCH64_SVCb.eq el0_svcb el0_sync_abortcheck_vector_size el0_sync_a64.align 7, INV_INSN el0_irq_a64:restore_mappingb elx_irqcheck_vector_size el0_irq_a64.align 7, INV_INSN el0_fiq_a64:restore_mappingb elx_fiqcheck_vector_size el0_fiq_a64.align 7, INV_INSN el0_serror_a64:b el0_serror_a64check_vector_size el0_serror_a64/* -----------------------------------------------------* Lower EL using AArch32 : 0x0 - 0x180* -----------------------------------------------------*/.align 7, INV_INSN el0_sync_a32:restore_mappingmrs x2, esr_el1mrs x3, sp_el0lsr x2, x2, #ESR_EC_SHIFTcmp x2, #ESR_EC_AARCH32_SVCb.eq el0_svcb el0_sync_abortcheck_vector_size el0_sync_a32.align 7, INV_INSN el0_irq_a32:restore_mappingb elx_irqcheck_vector_size el0_irq_a32.align 7, INV_INSN el0_fiq_a32:restore_mappingb elx_fiqcheck_vector_size el0_fiq_a32.align 7, INV_INSN el0_serror_a32:b el0_serror_a32check_vector_size el0_serror_a32align 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個字節(jié),所以這里也是和aarch32的異常向量表的offset一一對應(yīng)的
5、在ATF中arm64異常向量表的實現(xiàn)定義
在ATF的代碼中,在不同的階段有著不同的異常向量表:
- 在bl1階段使用bl1_exceptions
- 在bl2階段使用bl2_entrypoint
- 在bl31及其之后使用runtime_exceptions
我們常說的ATF中的向量表,其實就是bl31之后使用runtime_exceptions向量表,下面重點介紹下
(注意 : 帶unhandled的都是未實現(xiàn)的)
- 用于處理EL0產(chǎn)生異常時的entire(對應(yīng)第一行向量表)
- 用于處理當(dāng)前ELx產(chǎn)生異常時的entire(對應(yīng)第二行向量表)
- 用于處理AArch64指令產(chǎn)生的異常,且發(fā)生了EL的遷移的entire(對應(yīng)第三行向量表)
- 用于處理AArch32指令產(chǎn)生的異常,且發(fā)生了EL的遷移的entire(對應(yīng)第四行向量表)
6、、在ATF中arm64異常向量表的實現(xiàn)定義
TODO
軟件:各個系統(tǒng)的向量表基地址的設(shè)置(Linux, optee, ATF …32位/64位)
1、linux kernel的arm64下設(shè)置向量表基地址VBAR
在__primary_switched將vectors寫入到了VBAR_EL1
__primary_switched:adrp x4, init_thread_unionadd sp, x4, #THREAD_SIZEadr_l x5, init_taskmsr sp_el0, x5 // Save thread_infoadr_l x8, vectors // load VBAR_EL1 with virtualmsr vbar_el1, x8 // vector table addressisbstp xzr, x30, [sp, #-16]!mov x29, spstr_l x21, __fdt_pointer, x5 // Save FDT pointer..b start_kernel ENDPROC(__primary_switched)2、linux kernel的arm32下設(shè)置向量表基地址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函數(shù)通過操作cp15協(xié)處理器來寫入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是在開機的時候調(diào)用的
setup_arch ----> arm_memblock_init ----> arm_mm_memblock_reserve ---> setup_vectors_base3、optee中arm64設(shè)置向量表基地址VBAR_EL1
get_excp_vect()函數(shù)獲取到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_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)) { #ifdef ARM32case 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:return select_vector((vaddr_t)thread_excp_vect_workaround); #ifdef ARM32case CORTEX_A15_PART_NUM:return select_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
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函數(shù)完成將基地址寫入VBAR_EL1(將參數(shù)1寫入到VBAR_EL1)
(core/arch/arm/kernel/thread_a64.S) FUNC thread_init_vbar , :msr vbar_el1, x0 ret END_FUNC thread_init_vbar4、optee中arm設(shè)置向量表基地址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 r0bx lr UNWIND( .fnend) END_FUNC thread_init_vbar總結(jié)
以上是生活随笔為你收集整理的Linux Kernel/optee/ATF等操作系统的异常向量表的速查的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ARM异常]-armv8/armv9同
- 下一篇: Could not import ext