文章目录
以thread_a64.S为例,thread_a32.S暂不分析
1、 optee中的异常向量表thread_excp_vect
其中el0_sync_a64和el0_sync_a32是同步异常处理函数,当执行svc指令是会调用该函数;
#define INV_INSN 0
.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
中断向量表的注册:
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;
}
当中断来时,根据向量表中的跳转 ,会调用到elx_irq和elx_fiq函数
1.1、异步异常elx_irq、elx_fiq中断的处理
foreign是外部中断处理和native内部中断处理:
LOCAL_FUNC elx_irq , :
#if defined(CFG_ARM_GICV3)
native_intr_handler irq
#else
foreign_intr_handler irq
#endif
END_FUNC elx_irq
LOCAL_FUNC elx_fiq , :
#if defined(CFG_ARM_GICV3)
foreign_intr_handler fiq
#else
native_intr_handler fiq
#endif
END_FUNC elx_fiq
gicv3和gicv2的中断处理有什么不同呢?
- 在gicv2中,IRQ固定是给REE用的,FIQ固定是给TEE用的,所以如果在TEE中来了一个IRQ,则会调用foreign_intr_handler irq,foreign前缀是外部的意思,TEE中的处理,就是该函数中调用SMC,切回到REE,让REE来处理这个中断;
- 在gicv3中,在TEE中响应的IRQ是给TEE用的,在TEE中响应的FIQ是给REE用的,所以如果在TEE中来了一个FIQ,则会调用foreign_intr_handler fiq,foreign前缀是外部的意思,TEE中的处理,就是该函数中调用SMC,切回到REE,让REE来处理这个中断;
1.2、同步异常el0_sync_a64、el0_sync_a32中断的处理
.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
LOCAL_FUNC el0_svc , :
/* get pointer to current thread context in x0 */
get_thread_ctx sp, 0, 1, 2
/* load saved kernel sp */
ldr x0, [x0, #THREAD_CTX_KERN_SP]
/* Keep pointer to initial recod in x1 */
mov x1, sp
/* Switch to SP_EL0 and restore kernel sp */
msr spsel, #0
mov x2, sp /* Save SP_EL0 */
mov sp, x0
/* Make room for struct thread_svc_regs */
sub sp, sp, #THREAD_SVC_REG_SIZE
stp x30,x2, [sp, #THREAD_SVC_REG_X30]
/* Restore x0-x3 */
ldp x2, x3, [x1, #THREAD_CORE_LOCAL_X2]
ldp x0, x1, [x1, #THREAD_CORE_LOCAL_X0]
/* Prepare the argument for the handler */
store_xregs sp, THREAD_SVC_REG_X0, 0, 14
mrs x0, elr_el1
mrs x1, spsr_el1
store_xregs sp, THREAD_SVC_REG_ELR, 0, 1
mov x0, sp
/*
* Unmask native interrupts, Serror, and debug exceptions since we have
* nothing left in sp_el1. Note that the SVC handler is excepted to
* re-enable foreign interrupts by itself.
*/
#if defined(CFG_ARM_GICV3)
msr daifclr, #(DAIFBIT_IRQ | DAIFBIT_ABT | DAIFBIT_DBG)
#else
msr daifclr, #(DAIFBIT_FIQ | DAIFBIT_ABT | DAIFBIT_DBG)
#endif
/* Call the handler */
bl tee_svc_handler
/* Mask all maskable exceptions since we're switching back to sp_el1 */
msr daifset, #DAIFBIT_ALL
/*
* Save kernel sp we'll had at the beginning of this function.
* This is when this TA has called another TA because
* __thread_enter_user_mode() also saves the stack pointer in this
* field.
*/
msr spsel, #1
get_thread_ctx sp, 0, 1, 2
msr spsel, #0
add x1, sp, #THREAD_SVC_REG_SIZE
str x1, [x0, #THREAD_CTX_KERN_SP]
/* Restore registers to the required state and return*/
load_xregs sp, THREAD_SVC_REG_ELR, 0, 1
msr elr_el1, x0
msr spsr_el1, x1
load_xregs sp, THREAD_SVC_REG_X2, 2, 14
mov x30, sp
ldr x0, [x30, #THREAD_SVC_REG_SP_EL0]
mov sp, x0
b_if_spsr_is_el0 w1, 1f
ldp x0, x1, [x30, THREAD_SVC_REG_X0]
ldr x30, [x30, #THREAD_SVC_REG_X30]
eret
1: ldp x0, x1, [x30, THREAD_SVC_REG_X0]
ldr x30, [x30, #THREAD_SVC_REG_X30]
msr spsel, #1
store_xregs sp, THREAD_CORE_LOCAL_X0, 0, 1
b eret_to_el0
END_FUNC el0_svc