1. SMC指令介绍
CPU执行SMC指令将会进入EL3异常,SMC指令带一个16bit的立即数,异常中可以通过ESR_ELX寄存器的ISS字段得到这个立即数;
UBOOT中SMC调用相关代码如下,立即数为0,ATF中没有使用,SMC FUNCID通过x0寄存器传递,后面的x1到x6寄存器依次传递FUNC的函数参数;
void smc_call(struct pt_regs *args)
{
asm volatile(
"ldr x0, %0\n"
"ldr x1, %1\n"
"ldr x2, %2\n"
"ldr x3, %3\n"
"ldr x4, %4\n"
"ldr x5, %5\n"
"ldr x6, %6\n"
"smc #0\n"
"str x0, %0\n"
"str x1, %1\n"
"str x2, %2\n"
"str x3, %3\n"
: "+m" (args->regs[0]), "+m" (args->regs[1]),
"+m" (args->regs[2]), "+m" (args->regs[3])
: "m" (args->regs[4]), "m" (args->regs[5]),
"m" (args->regs[6])
: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
"x16", "x17");
}
2. SMC命令处理流程
执行SMC指令后,进入Lower EL using AArch64的异常向量入口,在ATF的runtime_exception.s中,相关代码如下:
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.
*/
save_x30
apply_at_speculative_wa
sync_and_handle_pending_serror
unmask_async_ea
handle_sync_exception
end_vector_entry sync_exception_aarch64
handle_sync_exception是一个宏,在这个宏里面调用sync_handler64继续处理,根据x0寄存器的FUNCID查找对应的service函数地址并调用,相关处理代码如下:
/*
* Per SMCCC documentation, bits [23:17] must be zero for Fast
* SMCs. Other values are reserved for future use. Ensure that
* these bits are zeroes, if not report as unknown SMC.
*/
tbz x0, #FUNCID_TYPE_SHIFT, 2f /* Skip check if its a Yield Call*/
tst x0, #(FUNCID_FC_RESERVED_MASK << FUNCID_FC_RESERVED_SHIFT)
b.ne smc_unknown
/*
* Per SMCCCv1.3 a caller can set the SVE hint bit in the SMC FID
* passed through x0. Copy the SVE hint bit to flags and mask the
* bit in smc_fid passed to the standard service dispatcher.
* A service/dispatcher can retrieve the SVE hint bit state from
* flags using the appropriate helper.
*/
2:
and x16, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT)
orr x7, x7, x16
bic x0, x0, #(FUNCID_SVE_HINT_MASK << FUNCID_SVE_HINT_SHIFT)
/* Get the unique owning entity number */
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
/* Load descriptor index from array of indices */
adrp x14, rt_svc_descs_indices
add x14, x14, :lo12:rt_svc_descs_indices
ldrb w15, [x14, x16]
/* Any index greater than 127 is invalid. Check bit 7. */
tbnz w15, 7, smc_unknown
/*
* Get the descriptor using the index
* x11 = (base + off), w15 = index
*
* handler = (base + off) + (index << log2(size))
*/
adr x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
lsl w10, w15, #RT_SVC_SIZE_LOG2
ldr x15, [x11, w10, uxtw]
/*
* Call the Secure Monitor Call handler and then drop directly into
* el3_exit() which will program any remaining architectural state
* prior to issuing the ERET to the desired lower EL.
*/
#if DEBUG
cbz x15, rt_svc_fw_critical_error
#endif
blr x15
ATF通过宏定义 DECLARE_RT_SVC 注册一个服务;ATF将FUNC_TYPE和FUNC_OEN组成的ID与SVC服务的index建立一个映射存储在rt_svc_descs_indices数组中,sync_handler64中通过此数组查找到对应的SVC结构体指针并得到RT_SVC_DESC_HANDLE指针,然后调用此函数回调;
/* Register Standard Service Calls as runtime service */
DECLARE_RT_SVC(
std_svc,
OEN_STD_START,
OEN_STD_END,
SMC_TYPE_FAST,
std_svc_setup,
std_svc_smc_handler
);
std_svc_smc_handler为smc消息处理函数,第一个参数smc_fid为消息id;
消息id格式为:
- bit[31] = 1:Fast call;
- bit[30] = 1:SMC64 call convention;
- bit[29 : 24] = 4:Standard service call;
- bit[15 : 0] = 3:该类型的 call type 下的 function number;