【ARM64 ATF 系列 1 -- ATF 中断向量表及SMC 处理流程】
文章目录
- 1.1 Trusted Firmware Project
- 1.1.1 TF-A 介绍
- 1.1.2 TF-A 启动流程
- 1.2 ATF 中断向量表
- 1.2.1 ATF 汇编宏 vector_base
- 1.3 ATF SMC 中断处理流程
1.1 Trusted Firmware Project
Trusted Firmware Project是一个开源软件项目,最初由ARM管理,后来移交给Linaro管理。Trusted Firmware Project旨在为ARM A-Profile和M-Profile架构处理器提供安全软件的参考实现。
Trusted Firmware Project包括如下项目;TF-A、TF-M、MCUboot、TF-RMM、OP-TEE、
Mbed TLS、Hafnium、Trusted Services、Open CI。
1.1.1 TF-A 介绍
TF-A 是Trusted Firmware-A 的简称,支持的处理器包括 Cortex-A 和 Neoverse系列,为 ARM AProfile 架构(包括 Armv7-A、Armv8-A和Armv9-A)处理器提供了安全软件的参考实现;TF-A支持代码复用,方便移植到不同芯片厂商的模型和硬件平台上,移植工作只需添加平台相关代码;TFA包括了执行在EL3异常级别的Secure Monitor,并支持 AArch32 和 AArch64 执行状态。
TF-A实现了如下ARM 接口标准:
- The Power State Coordination Interface (PSCI);
- Trusted Board Boot Requirements CLIENT (TBBR-CLIENT);
- SMC Calling Convention;
- System Control and Management Interface (SCMI);
- Software Delegated Exception Interface (SDEI)
- PSA FW update specification。
1.1.2 TF-A 启动流程
对于AArch64 状态的 Cold boot,从CPU Reset开始,TF-A按如下启动顺序工作:
- Boot Loader stage 1 (BL1)
AP Trusted ROM; - Boot Loader stage 2 (BL2)
Trusted Boot Firmware; - Boot Loader stage 3-1 (BL31)
EL3 Runtime Software; - Boot Loader stage 3-2 (BL32)
Secure-EL1 Payload (optional); - Boot Loader stage 3-3 (BL33)
Non-trusted Firmware;
bl1:Bootrom,BL1 是启动的第一阶段,该镜像必须要存储在可直接执行的介质中。若芯片支持 XIP 启动方式,其可被存储在片外可直接执行的介质中(如norflash)。若不支持 XIP,则需要存储在芯片的片内 ROM 中,此时在芯片出厂后该部分代码就将被固化,后续再也不能被修改和升级。若芯片要支持安全启动,则需要将 bootrom 作为启动时的信任根。
bl2:BL2 镜像由 BL1加载,此时 DDR 还没有被初始化,因此它需要被加载到片内的 SRAM 中执行,一般在 BL2 中完成 DDR 的初始化,因此后面的镜像都可以被加载到 DDR 中。BL31、BL32 和 BL33 都是由 BL2 加载的,其中 BL31 和 BL32是可选的,若系统不支持 TRUST OS,则可去掉 BL32,若不支持 EL3 异常等级及 secure monitor,则可去掉 BL31。此外 BL2 还会进行完成 USB 配置,PMIC 的配置 UART 配置,另外,BL2 运行在 EL3 级别。
bl2 有的公司叫做
preboot, 在有的公司称为SPL
b31:称为 ATF,主要完成双系统切换的工作,有的公司称其为 Sloader。sloader 对应的 ATF代码 (也就 arm trust firmware,现在最新版本已经改名) 。
ATF 代码可以从ARM官网 download,再加入一些自己的特有设计。
ATF 的功能主要是负责 tee\ree 切换;运行在EL3级别,由 BL2 (有的公司称其为SPL ) 加载并调用初始化。
BL2 执行完成后需要跳转到 BL31,由于 BL31 运行在 EL3 异常等级,而 BL2根据需求不同可能运行于secure EL1或EL3。当BL2运行于EL3时可直接通过ERET方式跳转到BL31中,但若其运行在secure EL1时,则只能通过smc异常触发进入EL3异常等级。 显然,此时 BL31 由于尚未设置其自身的SMC 异常处理程序而无法直接处理该异常,因此,为了完成跳转流程,BL1 需要先代理该异常的处理。因此 BL1 在退出之前先设置 SMC 异常处理函数,BL2 触发smc启动 BL31 时,BL1 捕获该异常并根据 BL2 传入的参数设置 BL31 的入口地址和系统初始状态,并通过 ERET 跳转到 BL31 的入口地址处执行。
当异常发生时 PE 会根据异常的类型选择一个合适返回地址保存到
ELR_ELn中,n的含义和签名SPSR_ELn类似。等到异常处理完毕、软件主动调用ERET指令,这个指令会将SPSR_ELn恢复到 PSATE寄存器 中 (严格来说PSTATE不是一个寄存器,是一组表示PE状态寄存器的组合)ELR_ELn的内容更新到PC指针,实现异常的返回跳转。
b32:包括 TEE OS(OpenTee), 其中 TE OS 运行在安全模式下的 EL1级别。BL31 可根据其镜像加载信息设置入口地址以及其它状态,并完成跳转。BL32 加载完成后将通过 SMC 返回到 BL31,然后由 BL31 跳转到 non secure EL1 或 non secure EL2 以执行 BL33。
bl33: 一般是指uboot, 有的会包括(bootloader)uboot->kernel->Anriod流程, uboot 运行在non-secure mode下的EL1级别。
ATF 里面分为BL1,BL2,BL31,BL32,BL33,一般SoC厂商会从BL31添加自己的代码,其他的都是ARM默认的,没有人去改。bl1/bl2 一般都是平台自己实现。
本篇文章主要介绍 ATF。
1.2 ATF 中断向量表
如果触发异常,程序会跳转到异常向量表去执行。整个异常向量表的大小为 0x800字节,每一个entry都是0x80对齐。异常总共有 4种:
- synchronous;
- IRQ;
- FIQ;
- SError。
根据异常发生的 exception level又分为4类:
- 1)发生异常的exception level栈指针使用 SP_EL0;
- 2)发生异常的exception level栈指针使用 SP_ELx;
- 3)低level触发异常进入高level,而且低 level使用的是64位架构;
- 4)低level触发异常进入高level,而且低 level使用的是32位架构。
以 linux kernel为例:
- kernel 中收到中断将进入第2类异常入口;
- 32位应用程序系统调用进入第3类异常入口;
- 64位应用程序系统调用进入第4类异常入口。
ATF 中断向量表的定义位于文件:bl31/aarch64/runtime_exceptions.S, 基地址为 runtime_exceptions:
vector_base runtime_exceptions/* ---------------------------------------------------------------------* Current EL with SP_EL0 : 0x0 - 0x200* ---------------------------------------------------------------------*/
vector_entry sync_exception_sp_el0
#ifdef MONITOR_TRAPSstp x29, x30, [sp, #-16]!mrs x30, esr_el3ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH/* Check for BRK */cmp x30, #EC_BRKb.eq brk_handlerldp 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_el0vector_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_el0vector_entry fiq_sp_el0b report_unhandled_interrupt
end_vector_entry fiq_sp_el0
vector_entry serror_sp_el0no_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_elxvector_entry irq_sp_elxb report_unhandled_interrupt
end_vector_entry irq_sp_elxvector_entry fiq_sp_elxb report_unhandled_interrupt
end_vector_entry fiq_sp_elxvector_entry serror_sp_elx
#if !RAS_EXTENSIONcheck_if_serror_from_EL3
#endifno_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_wacheck_and_unmask_eahandle_sync_exception
end_vector_entry sync_exception_aarch64vector_entry irq_aarch64apply_at_speculative_wacheck_and_unmask_eahandle_interrupt_exception irq_aarch64
end_vector_entry irq_aarch64vector_entry fiq_aarch64apply_at_speculative_wacheck_and_unmask_eahandle_interrupt_exception fiq_aarch64
end_vector_entry fiq_aarch64vector_entry serror_aarch64apply_at_speculative_wa
#if RAS_EXTENSIONmsr daifclr, #DAIF_ABT_BITb enter_lower_el_async_ea
#elsehandle_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_wacheck_and_unmask_eahandle_sync_exception
end_vector_entry sync_exception_aarch32vector_entry irq_aarch32apply_at_speculative_wacheck_and_unmask_eahandle_interrupt_exception irq_aarch32
end_vector_entry irq_aarch32vector_entry fiq_aarch32apply_at_speculative_wacheck_and_unmask_eahandle_interrupt_exception fiq_aarch32
end_vector_entry fiq_aarch32vector_entry serror_aarch32apply_at_speculative_wa
#if RAS_EXTENSIONmsr daifclr, #DAIF_ABT_BITb enter_lower_el_async_ea
#elsehandle_async_ea
#endif
end_vector_entry serror_aarch32
与 32 位架构不同,64 位架构的异常向量表地址不固定,在启动的时候由软件将异常向量表的地址设置到专用寄存器VBAR(Vector Base Address Register)中。
从 include/arch/aarch64/asm_macros.S 可以看到宏 vector_base 将向量表放到了 .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:
/* ---------------------------------------------------------------------* For RESET_TO_BL31 systems which have a programmable reset address,* bl31_entrypoint() is executed only on the cold boot path so we can* skip the warm boot mailbox mechanism.* ---------------------------------------------------------------------*/el3_entrypoint_common \_init_sctlr=1 \_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS \_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU \_init_memory=1 \_init_c_runtime=1 \_exception_vectors=runtime_exceptions \_pie_fixup_size=BL31_LIMIT - BL31_BASE
在 include/arch/aarch64/el3_common_macros.S 中的 el3_entrypoint_common 函数中将向量表的基地址赋值给了 vbar_el3((Vector Base Address Register, Exception Level 3)) 寄存器, 用于指定异常向量表的基地址。通过修改VBAR_EL3寄存器的值,可以改变异常处理程序的入口地址。
.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=0x150adr x0, \_exception_vectorsmsr vbar_el3, x0isb......
在ARM Trusted Firmware(ATF)中,el3_entrypoint_common是一个通用的入口点函数,用于处理从非安全世界(例如:EL2, EL1)或安全世界(EL3)进入EL3时的一些初始化工作。
以下是el3_entrypoint_common函数的一些常见作用:
-
保存当前的执行环境,包括当前异常级别下的系统寄存器。
-
设置新的执行环境,包括初始化栈,设置新的异常向量表地址,等等。
-
根据需要,可能会进行一些硬件初始化或配置,例如初始化内存管理单元(MMU)。
-
最后,el3_entrypoint_common函数通常会跳转到其他函数进行进一步的初始化,这可能包括启动操作系统,进入休眠状态,或者处理更具体的异常。
需要注意的是,el3_entrypoint_common函数的具体实现可能会根据具体的硬件平台和ATF版本有所不同,因此上述的作用可能会有所差异。
1.2.1 ATF 汇编宏 vector_base
对于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.3 ATF SMC 中断处理流程
在上面 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_wacheck_and_unmask_eahandle_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_regsldp 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的值。
这段代码的主要作用是,在处理异常时,如果启用了异步异常的规避措施,就保存当前的状态,调用特定的函数进行处理,然后恢复之前的状态。
- check_and_unmask_ea
/** 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, #1str x30, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]dmb sy
msr daifclr, #DAIF_ABT_BIT打开Abort中断,Abort中断分为两种类型:Prefetch Abort和Data Abort。对于这两种Abort中断,处理器都会进入异常处理模式,并跳转到预设的异常处理程序进行处理。- Prefetch Abort:这种中断是由于指令预取阶段出现的错误触发的,例如尝试执行一个非法地址中的指令或者违反内存保护策略等。
- Data 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执行级别中运行。
- SMC 中断处理(
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_el0str x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]mrs x29, tpidr_el3str x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]ldr x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]
#endifmrs x30, esr_el3ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH/* Handle SMC exceptions separately from other synchronous exceptions */cmp x30, #EC_AARCH32_SMCb.eq smc_handler32cmp x30, #EC_AARCH64_SMCb.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时间戳区域。 - 然后,读取当前异常状态寄存器(ESR_EL3)的值,使用
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标签处处理。
关于 ESR_EL3 EC 的字段:

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)
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
