1 ATF的smc指令调用流程 在REE侧调用smc异常之后,会根据中断向量表触发cpu的同步异常sync_exception_aarch64/32,然后跳转执行到handle_sync_exception->smc_handler64/32中,最后跳转到_RT_SVC_DESCS_START_+RT_SVC_DESC_HANDLE这个工具类中执行具体的操作,最后跳转到el3_exit返回REE侧。
2.1 REE侧如何调用smc指令 在REE侧调用smc之前,需要对通用寄存器进行赋值传参x0-x8。然后通过 smc #0这一汇编指令进行smc调用
func smcldm sp, { r4, r5, r6} smc #0 endfunc smc func smcsmc #0 endfunc smc
2.2 smc同步异常 在运行时el3采用的异常向量表是runtime_exceptions
(1)首先看两个宏定义
. macro vector_base label . section . vectors, "ax" . align 11 , 0 \label: . endm. macro vector_entry label . section . vectors, "ax" . align 7 , 0 \label: . endm. macro check_vector_size since. if ( . - \since) > ( 32 * 4 ) . error "Vector exceeds 32 instructions" . endif. endm. macro no_ret _func: req, skip_nop= 0 bl \_func
vector_base runtime_exceptions vector_entry sync_exception_aarch64handle_sync_exceptioncheck_vector_size sync_exception_aarch64 vector_entry sync_exception_aarch32handle_sync_exceptioncheck_vector_size sync_exception_aarch32
这段代码等价于
. section . vectors, "ax" . align 11 , 0 runtime_exceptions: . section . vectors, "ax" . align 7 , 0 sync_exception_aarch64: handle_sync_exception. if ( . - serror_aarch64) > ( 32 * 4 ) . error "Vector exceeds 32 instructions" . endifsync_exception_aarch32handle_sync_exception. if ( . - serror_aarch64) > ( 32 * 4 ) . error "Vector exceeds 32 instructions" . endif
2.3 handle_sync_exception 这个 handle_sync_exception是一个宏定义
. macro handle_sync_exceptionmsr daifclr, #DAIF_ABT_BIT str x30, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR] # if ENABLE_RUNTIME_INSTRUMENTATION 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 cmp x30, #EC_AARCH32_SMC b. eq smc_handler32cmp x30, #EC_AARCH64_SMC b. eq smc_handler64no_ret report_unhandled_exception . endm
2.4 smc_handler32/64 以及report_unhandled_exception的执行 在2.3中有三种跳转选项其中smc_handler32/64能够正确触发异常,report_unhandled_exception则是错误的流程 这个函数主要是存储x4-x18寄存器的值,并通过x0也就是smc_id判断该指令是smc_handler32还是smc_handler64,然后进入到rt_svc_descs这个工具结构体中,执行具体的指令。
func report_unhandled_exceptionprepare_crash_buf_save_x0_x1 adr x0, excpt_msgmov sp, x0b do_crash_reporting endfunc report_unhandled_exception smc_handler32: tbnz x0, #FUNCID_CC_SHIFT, smc_prohibited stp x8, x9, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X8] stp x10, x11, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X10] stp x12, x13, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X12] stp x14, x15, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X14] stp x16, x17, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16] smc_handler64: stp x4, x5, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4] stp x6, x7, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6] save_x18_to_x29_sp_el0 mov x5, xzr mov x6, sp ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH adr x11, ( __RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) adr x14, rt_svc_descs_indices ldrb w15, [ x14, x16] ldr x12, [ x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP] tbnz w15, 7 , smc_unknown msr spsel, #0
__RT_SVC_DESCS_START__ = . ; KEEP ( * ( rt_svc_descs) ) __RT_SVC_DESCS_END__ = . ;
.macro save_x18_to_x29_sp_el0 stp x18, x19, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18] stp x20, x21, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20] stp x22, x23, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X22] stp x24, x25, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X24] stp x26, x27, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X26] stp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28] mrs x18, sp_el0 str x18, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_SP_EL0] .endm
smc_unknown: mov w0, #SMC_UNKb restore_gp_registers_callee_eret
2.5 std_svc_smc_handler (这只是调用psic的smc_handler) 在2.4 中最后跳转到x15寄存器所存储的地址
ldr x15 = [ x11, w10, uxtw] ; adr x11, ( __RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE) lsl w10, w15, #RT_SVC_SIZE_LOG2 ldrb w15, [ x14, x16] adr x14, rt_svc_descs_indices ubfx x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH orr x16, x16, x15, lsl #FUNCID_OEN_WIDTH ubfx x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
应该就是跳入了__RT_SVC_DESCS_这个的对应函数地址
uint8_t rt_svc_descs_indices[ MAX_RT_SVCS] ; static rt_svc_desc_t * rt_svc_descs;
其中 rt_svc_desc_t 的成员及初始化参数为
typedef struct rt_svc_desc { uint8_t start_oen; uint8_t end_oen; uint8_t call_type; const char * name; rt_svc_init_t init; rt_svc_handle_t handle; } rt_svc_desc_t ;
在其他博主的文章中搜索到是跳入了std_svc_smc_handler这个函数 首先通过上述函数了解到 只有初始的x0-x3的值没有压入到中断也就是只能传输过去smc_fid、x1、x2、x3这四个值
uintptr_t std_svc_smc_handler ( uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3, u_register_t x4, void * COOKIE, void * handle, u_register_t flags) { # ifdef ENABLE_QOS_SETTING unsigned long qos_ret = 0 ; # endif if ( is_psci_fid ( smc_fid) ) { uint64_t ret; # if ENABLE_RUNTIME_INSTRUMENTATION PMF_WRITE_TIMESTAMP ( rt_instr_svc, RT_INSTR_ENTER_PSCI, PMF_CACHE_MAINT, get_cpu_data ( cpu_data_pmf_ts[ CPU_DATA_PMF_TS0_IDX] ) ) ; # endif ret = psci_smc_handler ( smc_fid, x1, x2, x3, x4, COOKIE, handle, flags) ; # if ENABLE_RUNTIME_INSTRUMENTATION PMF_CAPTURE_TIMESTAMP ( rt_instr_svc, RT_INSTR_EXIT_PSCI, PMF_NO_CACHE_MAINT) ; # endif SMC_RET1 ( handle, ret) ; } switch ( smc_fid) { case ARM_STD_SVC_CALL_COUNT: SMC_RET1 ( handle, PSCI_NUM_CALLS) ; case ARM_STD_SVC_UID: SMC_UUID_RET ( handle, arm_svc_uid) ; case ARM_STD_SVC_VERSION: SMC_RET2 ( handle, STD_SVC_VERSION_MAJOR, STD_SVC_VERSION_MINOR) ; # ifdef ENABLE_QOS_SETTING case SPRD_QOS_READ: qos_ret= qos_register_read ( x1) ; SMC_RET1 ( handle, qos_ret) ; case SPRD_QOS_WRITE: qos_ret = qos_register_write ( x1, x2) ; SMC_RET1 ( handle, qos_ret) ; # endif default : WARN ( "Unimplemented Standard Service Call: 0x%x \n" , smc_fid) ; SMC_RET1 ( handle, SMC_UNK) ; } }
从这个函数中可以看出smc现在只挂载了psci相关的指令其他的都没有涉及
2.6 el3_exit 执行完std_svc_smc_handler 之后又回到原先的汇编函数接下来执行el3_exit
func el3_exitmov x17, sp msr spsel, #1 str x17, [ sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP] ldr x18, [ sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3] ldp x16, x17, [ sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3] msr scr_el3, x18 msr spsr_el3, x16 msr elr_el3, x17 b restore_gp_registers_eret endfunc el3_exit
func restore_gp_registers_eretldp x0, x1, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0] ldp x2, x3, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X2] b restore_gp_registers_callee_eret endfunc restore_gp_registers_eret func restore_gp_registers_callee_eretldp x4, x5, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4] ldp x6, x7, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6] ldp x8, x9, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X8] ldp x10, x11, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X10] ldp x12, x13, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X12] ldp x14, x15, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X14] ldp x18, x19, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18] ldp x20, x21, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20] ldp x22, x23, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X22] ldp x24, x25, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X24] ldp x26, x27, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X26] ldp x28, x29, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28] ldp x30, x17, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR] msr sp_el0, x17 ldp x16, x17, [ sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16] eret endfunc restore_gp_registers_callee_eret func ereteret endfunc eret
这里是armv8手册中用于描述eret的语句 使用ELR和SPSR返回当前异常级别。在执行的时候,PE从SPSR恢复PSTATE,和分支到ELR里的地址。 PE检查SPSR的当前异常级别是否存在非法返回事件, 如果在EL0中执行,ERET将导致未定义指令异常。 大概意思就是回到REE侧了
3 smc指令如何书写 从之前std_svc_smc_handler函数中的handle对应REE侧sp来说,程序根据SMC_RET1、SMC_RET2等参数可以将之前堆栈的中值改变,从而影响REE侧返回的x0-x12,以起堆栈中所存储的其他寄存器。 所以 REE侧触发smc #0指令时,x0-x3的值是有用的。x0代表smc_id x1-x3代表其他需要传输的形参。返回REE侧时能都返回的数据可通过handle存储到x0-x12中。
4 bl31启动的runtime_svc_init函数 在bl31中会执行runtime_svc_init函数,该函数会调用注册到EL3中所有服务的初始化函数,其中有一个服务项就是TEE服务,该服务项的初始化函数会将TEE OS的初始化函数赋值给bl32_init变量,当所有服务项执行完初始化后,在bl31中会调用b32_init执行的函数来跳转到TEE OS中并开始执行TEE OS的启动。
void bl31_main ( void ) { NOTICE ( "BL31: %s\n" , version_string) ; NOTICE ( "BL31: %s\n" , build_message) ; bl31_platform_setup ( ) ; bl31_lib_init ( ) ; INFO ( "BL31: Initializing runtime services\n" ) ; runtime_svc_init ( ) ; if ( bl32_init) { INFO ( "BL31: Initializing BL32\n" ) ; ( * bl32_init) ( ) ; } bl31_prepare_next_image_entry ( ) ; console_flush ( ) ; bl31_plat_runtime_setup ( ) ; }
void runtime_svc_init ( void ) { int rc &#61; 0 ; unsigned int index, start_idx, end_idx; assert ( ( RT_SVC_DESCS_END >&#61; RT_SVC_DESCS_START) && ( RT_SVC_DECS_NUM < MAX_RT_SVCS) ) ; if ( RT_SVC_DECS_NUM &#61;&#61; 0 ) return ; memset ( rt_svc_descs_indices, - 1 , sizeof ( rt_svc_descs_indices) ) ; rt_svc_descs &#61; ( rt_svc_desc_t * ) RT_SVC_DESCS_START; for ( index &#61; 0 ; index < RT_SVC_DECS_NUM; index&#43;&#43; ) { rt_svc_desc_t * service &#61; & rt_svc_descs[ index] ; rc &#61; validate_rt_svc_desc ( service) ; if ( rc) { ERROR ( "Invalid runtime service descriptor %p\n" , ( void * ) service) ; panic ( ) ; } if ( service-> init) { rc &#61; service-> init ( ) ; if ( rc) { ERROR ( "Error initializing runtime service %s\n" , service-> name) ; continue ; } } start_idx &#61; get_unique_oen ( rt_svc_descs[ index] . start_oen, service-> call_type) ; assert ( start_idx < MAX_RT_SVCS) ; end_idx &#61; get_unique_oen ( rt_svc_descs[ index] . end_oen, service-> call_type) ; assert ( end_idx < MAX_RT_SVCS) ; for ( ; start_idx <&#61; end_idx; start_idx&#43;&#43; ) rt_svc_descs_indices[ start_idx] &#61; index; } }
具体注册宏指令
# define DECLARE_RT_SVC ( _name, _start, _end, _type, _setup, _smch) \ static const rt_svc_desc_t __svc_desc_ ## _name \ __section ( "rt_svc_descs" ) __used &#61; { \ . start_oen &#61; _start, \ . end_oen &#61; _end, \ . call_type &#61; _type, \ . name &#61; #_name, \ . init &#61; _setup, \ . handle &#61; _smch }
然后添加服务时只需要调用这个宏指令就可以了
DECLARE_RT_SVC ( std_svc, OEN_STD_START, OEN_STD_END, SMC_TYPE_FAST, std_svc_setup, std_svc_smc_handler) ; 这个的意思就是注册static const rt_svc_desc_t __svc_desc_std_svc服务。其服务id为SMC_TYPE_FAST << 6 &#43; OEN_STD_START, 结束服务的id为SMC_TYPE_FAST << 6 &#43; OEN_STD_END