ATF 中断向量表的定义位于文件:bl31/aarch64/runtime_exceptions.S
vector_base runtime_exceptions
/* ---------------------------------------------------------------------
* Current EL with SP_EL0 : 0x0 - 0x200
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_sp_el0
#ifdef MONITOR_TRAPS
stp x29, x30, [sp, #-16]!
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Check for BRK */
cmp x30, #EC_BRK
b.eq brk_handler
ldp x29, x30, [sp], #16
#endif /* MONITOR_TRAPS */
/* We don't expect any synchronous exceptions from EL3 */
b report_unhandled_exception
end_vector_entry sync_exception_sp_el0
vector_entry irq_sp_el0
/*
* EL3 code is non-reentrant. Any asynchronous exception is a serious
* error. Loop infinitely.
*/
b report_unhandled_interrupt
end_vector_entry irq_sp_el0
vector_entry fiq_sp_el0
b report_unhandled_interrupt
end_vector_entry fiq_sp_el0
vector_entry serror_sp_el0
no_ret plat_handle_el3_ea
end_vector_entry serror_sp_el0
/* ---------------------------------------------------------------------
* Current EL with SP_ELx: 0x200 - 0x400
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_sp_elx
/*
* This exception will trigger if anything went wrong during a previous
* exception entry or exit or while handling an earlier unexpected
* synchronous exception. There is a high probability that SP_EL3 is
* corrupted.
*/
b report_unhandled_exception
end_vector_entry sync_exception_sp_elx
vector_entry irq_sp_elx
b report_unhandled_interrupt
end_vector_entry irq_sp_elx
vector_entry fiq_sp_elx
b report_unhandled_interrupt
end_vector_entry fiq_sp_elx
vector_entry serror_sp_elx
#if !RAS_EXTENSION
check_if_serror_from_EL3
#endif
no_ret plat_handle_el3_ea
end_vector_entry serror_sp_elx
/* ---------------------------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_aarch64
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
apply_at_speculative_wa
check_and_unmask_ea
handle_sync_exception
end_vector_entry sync_exception_aarch64
vector_entry irq_aarch64
apply_at_speculative_wa
check_and_unmask_ea
handle_interrupt_exception irq_aarch64
end_vector_entry irq_aarch64
vector_entry fiq_aarch64
apply_at_speculative_wa
check_and_unmask_ea
handle_interrupt_exception fiq_aarch64
end_vector_entry fiq_aarch64
vector_entry serror_aarch64
apply_at_speculative_wa
#if RAS_EXTENSION
msr daifclr, #DAIF_ABT_BIT
b enter_lower_el_async_ea
#else
handle_async_ea
#endif
end_vector_entry serror_aarch64
/* ---------------------------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* ---------------------------------------------------------------------
*/
vector_entry sync_exception_aarch32
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
apply_at_speculative_wa
check_and_unmask_ea
handle_sync_exception
end_vector_entry sync_exception_aarch32
vector_entry irq_aarch32
apply_at_speculative_wa
check_and_unmask_ea
handle_interrupt_exception irq_aarch32
end_vector_entry irq_aarch32
vector_entry fiq_aarch32
apply_at_speculative_wa
check_and_unmask_ea
handle_interrupt_exception fiq_aarch32
end_vector_entry fiq_aarch32
vector_entry serror_aarch32
apply_at_speculative_wa
#if RAS_EXTENSION
msr daifclr, #DAIF_ABT_BIT
b enter_lower_el_async_ea
#else
handle_async_ea
#endif
end_vector_entry serror_aarch32
从 include/arch/aarch64/asm_macros.S
可以看到向量表被放到了 .vectors 段中:
/*
* Declare the exception vector table, enforcing it is aligned on a
* 2KB boundary, as required by the ARMv8 architecture.
* Use zero bytes as the fill value to be stored in the padding bytes
* so that it inserts illegal AArch64 instructions. This increases
* security, robustness and potentially facilitates debugging.
*/
.macro vector_base label, section_name=.vectors
.section \section_name, "ax"
.align 11, 0
\label:
.endm
在 bl31/aarch64/bl31_entrypoint.S
可以看到 向量表的基地址赋值给了 _exception_vectors
在 include/arch/aarch64/el3_common_macros.S
中的 el3_entrypoint_common
函数中将向量表的基地址赋值给了
.macro el3_entrypoint_common \
_init_sctlr, _warm_boot_mailbox, _secondary_cold_boot, \
_init_memory, _init_c_runtime, _exception_vectors, \
_pie_fixup_size
......
/* ---------------------------------------------------------------------
* Set the exception vectors.
* ---------------------------------------------------------------------
*/
cix_postcode_debug_asm _code=0x150
adr x0, \_exception_vectors
msr vbar_el3, x0
isb
......
对于ARMv7 的定义(include/arch/aarch32/asm_macros.S
):
/*
* Declare the exception vector table, enforcing it is aligned on a
* 32 byte boundary.
*/
.macro vector_base label
.section .vectors, "ax"
.align 5
\label:
.endm
对于ARMv8 的定义(include/arch/aarch64/asm_macros.S
):
/*
* Declare the exception vector table, enforcing it is aligned on a
* 2KB boundary, as required by the ARMv8 architecture.
* Use zero bytes as the fill value to be stored in the padding bytes
* so that it inserts illegal AArch64 instructions. This increases
* security, robustness and potentially facilitates debugging.
*/
.macro vector_base label, section_name=.vectors
.section \section_name, "ax"
.align 11, 0
\label:
.endm
在上面 1.1 节的内容中我们可以看到处理同步异常的汇编宏: handle_sync_exception
(分为AArch32 和 AArch64 两种情况,这里值讨论AArch64),由于 SMC 触发的异常属于同步异常,所以执行 SMC 指令后首先会走到 sync_exception_aarch64
处。看下 sync_exception_aarch64
在ATF 中的实现:
vector_entry sync_exception_aarch64
/*
* This exception vector will be the entry point for SMCs and traps
* that are unhandled at lower ELs most commonly. SP_EL3 should point
* to a valid cpu context where the general purpose and system register
* state can be saved.
*/
apply_at_speculative_wa
check_and_unmask_ea
handle_sync_exception
end_vector_entry sync_exception_aarch64
可以看到 SMC 异常的处理包含三部分,接下来分别对这三部做介绍。
apply_at_speculative_wa
.macro apply_at_speculative_wa
#if ERRATA_SPECULATIVE_AT
/*
* Explicitly save x30 so as to free up a register and to enable
* branching and also, save x29 which will be used in the called
* function
*/
stp x29, x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
bl save_and_update_ptw_el1_sys_regs
ldp x29, x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
.endm
首先,编译器会检查是否启用了规避异步异常的修补方案(ERRATA_SPECULATIVE_AT
)。如果启用了,就会执行一系列的指令。
首先使用STP
指令将寄存器x29
和x30
的值存储到堆栈中,以便释放这两个寄存器供后续的指令使用。
然后,它调用 save_and_update_ptw_el1_sys_regs
函数。这个函数的作用根据函数名,应该是保存并更新EL1系统寄存器的页表遍历(Page Table Walk, PTW)相关的值。这可能是由于部分ARM处理器存在的一些硬件异常,需要通过软件来进行规避。
最后,它使用LDP
指令从堆栈中恢复寄存器x29
和x30
的值。
这段代码的主要作用是,在处理异常时,如果启用了异步异常的规避措施,就保存当前的状态,调用特定的函数进行处理,然后恢复之前的状态。
/*
* For SoCs which do not implement RAS, use DSB as a barrier to
* synchronize pending external aborts.
*/
dsb sy
/* Unmask the SError interrupt */
msr daifclr, #DAIF_ABT_BIT
/* Use ISB for the above unmask operation to take effect immediately */
isb
/*
* Refer Note 1. No need to restore X30 as both handle_sync_exception
* and handle_interrupt_exception macro which follow this macro modify
* X30 anyway.
*/
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
mov x30, #1
str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]
dmb sy
msr daifclr, #DAIF_ABT_BIT
打开Abort中断,Abort中断分为两种类型:Prefetch Abort和Data Abort。对于这两种Abort中断,处理器都会进入异常处理模式,并跳转到预设的异常处理程序进行处理。
isb
:Instruction Synchronization Barrier(指令同步屏障)这条指令确保了在它之前的所有指令都完成后,才开始执行它之后的指令。在这里,它确保了打开Abort中断的设置立即生效。。str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
将寄存器x30
的值存储到堆栈的指定位置。这里的位置是基于sp(Stack Pointer,栈指针)寄存器和偏移量CTX_GPREGS_OFFSET + CTX_GPREG_LR
计算得到的。mov x30, #1
:将1写入到寄存器x30。str x30, [sp, #CTX_EL3STATE_OFFSET +CTX_IS_IN_EL3]
:将寄存器x30
的值存储到堆栈的指定位置。这里的位置是基于sp寄存器和偏移量CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3
计算得到的。这条指令标志着异常处理程序现在正在 EL3 执行级别中运行。dmb sy
:Data Memory Barrier(数据内存屏障)。这条指令确保了在它之前的内存访问指令都完成后,才会执行它之后的指令。这段代码的主要作用是打开Abort 中断,并将一些寄存器值存储到堆栈中以备后续的异常处理程序使用,同时也标志着异常处理程序正在 EL3执行级别中运行。
bl31/aarch64/runtime_exceptions.S
): /* ---------------------------------------------------------------------
* This macro handles Synchronous exceptions.
* Only SMC exceptions are supported.
* ---------------------------------------------------------------------
*/
.macro handle_sync_exception
#if ENABLE_RUNTIME_INSTRUMENTATION
/*
* Read the timestamp value and store it in per-cpu data. The value
* will be extracted from per-cpu data by the C level SMC handler and
* saved to the PMF timestamp region.
*/
mrs x30, cntpct_el0
str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
mrs x29, tpidr_el3
str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]
ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endif
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Handle SMC exceptions separately from other synchronous exceptions */
cmp x30, #EC_AARCH32_SMC
b.eq smc_handler32
cmp x30, #EC_AARCH64_SMC
b.eq smc_handler64
/* Synchronous exceptions other than the above are assumed to be EA */
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
b enter_lower_el_sync_ea
.endm
ENABLE_RUNTIME_INSTRUMENTATION
),如果启用了,就会读取物理计数器(CNTPCT_EL0
)的值,并将其保存在每个CPU的数据中。这个值会被C级别的SMC处理程序提取并保存到PMF时间戳区域。UBFX
指令从中提取异常类(EC)字段。EC_AARCH32_SMC
(0x13),表示这是一个32位的安全监视器调用(SMC),就跳转到 smc_handler32
标签处处理;如果值等于EC_AARCH64_SMC
,表示这是一个64位的SMC,就跳转到 smc_handler64
标签处处理。如果异常类字段的值既不是EC_AARCH32_SMC
,也不是EC_AARCH64_SMC
,则假定这是一个异步异常(EA),并跳转到enter_lower_el_sync_ea
标签处处理。ATF 代码中对于每个异常类型都做了对应的宏定义(include/arch/aarch64/arch.h
)
/* Exception Syndrome register bits and bobs */
#define ESR_EC_SHIFT U(26)
#define ESR_EC_MASK U(0x3f)
#define ESR_EC_LENGTH U(6)
#define ESR_ISS_SHIFT U(0)
#define ESR_ISS_LENGTH U(25)
#define EC_UNKNOWN U(0x0)
#define EC_WFE_WFI U(0x1)
#define EC_AARCH32_CP15_MRC_MCR U(0x3)
#define EC_AARCH32_CP15_MRRC_MCRR U(0x4)
#define EC_AARCH32_CP14_MRC_MCR U(0x5)
#define EC_AARCH32_CP14_LDC_STC U(0x6)
#define EC_FP_SIMD U(0x7)
#define EC_AARCH32_CP10_MRC U(0x8)
#define EC_AARCH32_CP14_MRRC_MCRR U(0xc)
#define EC_ILLEGAL U(0xe)
#define EC_AARCH32_SVC U(0x11)
#define EC_AARCH32_HVC U(0x12)
#define EC_AARCH32_SMC U(0x13)
#define EC_AARCH64_SVC U(0x15)
#define EC_AARCH64_HVC U(0x16)
#define EC_AARCH64_SMC U(0x17)
#define EC_AARCH64_SYS U(0x18)
#define EC_IABORT_LOWER_EL U(0x20)
#define EC_IABORT_CUR_EL U(0x21)
#define EC_PC_ALIGN U(0x22)
#define EC_DABORT_LOWER_EL U(0x24)
#define EC_DABORT_CUR_EL U(0x25)
#define EC_SP_ALIGN U(0x26)
#define EC_AARCH32_FP U(0x28)
#define EC_AARCH64_FP U(0x2c)
#define EC_SERROR U(0x2f)
#define EC_BRK U(0x3c)