ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[ATF]-smc指令详解

2021-06-21 15:02:32  阅读:674  来源: 互联网

标签:ATF OPTEE smc args fast 详解 call SMC


文章目录


★★★ 友情链接 : 个人博客导读首页—点击此处 ★★★

思考:
(1)、在linux中执行smc指令后,是如何调用到ATF中的opteed_smc_handler函数的?
(2)、ATF又是如何返回到linux的?
(3)、fast call和std call又是怎样区分的?

1、在linux中发起smc的调用

SMCCC是一个宏,( \instr #0 )这一行其实就是( smc #0),就是smc调用

在调用smc之前,x0-x8值对应的分别是arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res)中的参数。

.macro SMCCC instr
.cfi_startproc
\instr	#0
ldr	x4, [sp]
stp	x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
stp	x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
ret
.cfi_endproc
.endm
/*
 * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
 *		  unsigned long a3, unsigned long a4, unsigned long a5,
 *		  unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
 */
ENTRY(arm_smccc_smc)
	SMCCC	smc
ENDPROC(arm_smccc_smc)
static void optee_smccc_smc(unsigned long a0, unsigned long a1,
			    unsigned long a2, unsigned long a3,
			    unsigned long a4, unsigned long a5,
			    unsigned long a6, unsigned long a7,
			    struct arm_smccc_res *res)
{
	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}

invoke_fn = get_invoke_func(np); 这里会指向optee_smccc_smc

2、陷入ATF的smc同步异常后,调用handler和exit_el3返回linux

调用smc之后,cpu触发同步异常,进入ATF的sync_exception_aarch64—>handle_sync_exception—>smc_handler64处理函数
在这里插入图片描述
smc_handler64片段:

smc_handler64:
......
	adr	x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)
.....
	ldr	x15, [x11, w10, uxtw]
.....
	blr	x15

	b	el3_exit

RT_SVC_DESCS_START + RT_SVC_DESC_HANDLE指向我们在opteed_main.c中注册的handler函数

DECLARE_RT_SVC(
	opteed_fast,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_FAST,
	opteed_setup,
	opteed_smc_handler
);

/* Define an OPTEED runtime service descriptor for standard SMC calls */
DECLARE_RT_SVC(
	opteed_std,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_STD,
	NULL,
	opteed_smc_handler
);

在执行完handler函数后,el3_exit调用ERET指令,恢复异常前的PC指针和PSTATE,回到EL1

3、fast call和std call的定义

(1)、在linux的optee_smc.h中,定义了fast call和std call的funcid

#define OPTEE_SMC_STD_CALL_VAL(func_num) \
	ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \
			   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))
#define OPTEE_SMC_FAST_CALL_VAL(func_num) \
	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
			   ARM_SMCCC_OWNER_TRUSTED_OS, (func_num))

在构造funcid宏时,如果是std call,cmd_id的31位需是0,如果是fast call,funcid的31位需是1

#define ARM_SMCCC_STD_CALL		0
#define ARM_SMCCC_FAST_CALL		1
#define ARM_SMCCC_TYPE_SHIFT		31

(2)、在optee中 ,对应的也定义了fast和std的type

#define SMC_TYPE_FAST			1
#define SMC_TYPE_STD			0

并且分别注册了std服务和fast服务,虽然指向的是同一个函数

/* Define an OPTEED runtime service descriptor for fast SMC calls */
DECLARE_RT_SVC(
	opteed_fast,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_FAST,
	opteed_setup,
	opteed_smc_handler
);

/* Define an OPTEED runtime service descriptor for standard SMC calls */
DECLARE_RT_SVC(
	opteed_std,

	OEN_TOS_START,
	OEN_TOS_END,
	SMC_TYPE_STD,
	NULL,
	opteed_smc_handler
);

然后我们再看看,同步异常中断中,跳转的时候,如何解析TYPE的

代码片段

smc_handler64:
......
	/* Get the unique owning entity number */
	ubfx	x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH
	ubfx	x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH
......
	ldr	x15, [x11, w10, uxtw]
......
	blr	x15

	b	el3_exit

使用ubfx指令,将FUNCID_TYPE_SHIFT和FUNCID_TYPE_WIDTH解析出来,放在了x15中
(3)、fast call和std call的funcid的定义,在ARM文档中有规定

4、fast call和std call有什么不同?

linux—>ATF—>optee的过程中,有fast call和std call,那么在这fast和std中有什么不同呢
(1)、在ATF中,将optee传过来的线程向量表中fast_smc_entry或std_smc_entry写入到ELR_EL3中

if (GET_SMC_TYPE(smc_fid) == SMC_TYPE_FAST) {
	cm_set_elr_el3(SECURE, (uint64_t)
			&optee_vectors->fast_smc_entry);
} else {
	cm_set_elr_el3(SECURE, (uint64_t)
			&optee_vectors->std_smc_entry);
}

(2)、在optee中 fast call会执行thread_handle_fast_smc函数,然后立即执行funcid对应的函数
例如在我们的optee中,定义了如下fast call:

void tee_entry_fast(struct thread_smc_args *args)
{
	switch (args->a0) {

	/* Generic functions */
	case OPTEE_SMC_CALLS_COUNT:
		tee_entry_get_api_call_count(args);
		break;
	case OPTEE_SMC_CALLS_UID:
		tee_entry_get_api_uuid(args);
		break;
	case OPTEE_SMC_CALLS_REVISION:
		tee_entry_get_api_revision(args);
		break;
	case OPTEE_SMC_CALL_GET_OS_UUID:
		tee_entry_get_os_uuid(args);
		break;
	case OPTEE_SMC_CALL_GET_OS_REVISION:
		tee_entry_get_os_revision(args);
		break;

	/* OP-TEE specific SMC functions */
	case OPTEE_SMC_GET_SHM_CONFIG:
		tee_entry_get_shm_config(args);
		break;
	case OPTEE_SMC_L2CC_MUTEX:
		tee_entry_fastcall_l2cc_mutex(args);
		break;
	case OPTEE_SMC_EXCHANGE_CAPABILITIES:
		tee_entry_exchange_capabilities(args);
		break;
	case OPTEE_SMC_DISABLE_SHM_CACHE:
		tee_entry_disable_shm_cache(args);
		break;
	case OPTEE_SMC_ENABLE_SHM_CACHE:
		tee_entry_enable_shm_cache(args);
		break;
	case OPTEE_SMC_BOOT_SECONDARY:
		tee_entry_boot_secondary(args);
		break;

	default:
		args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION;
		break;
	}
}

std call执行thread_handle_std_smc函数,该函数中不会立即执行funcid对应的函数,会进行调度等
在我们的optee中,有如下是std call:

void __weak tee_entry_std(struct thread_smc_args *smc_args)
{
	paddr_t parg;
	struct optee_msg_arg *arg = NULL;	/* fix gcc warning */
	uint32_t num_params = 0;		/* fix gcc warning */
	struct mobj *mobj;

	if (smc_args->a0 != OPTEE_SMC_CALL_WITH_ARG) {
		EMSG("Unknown SMC 0x%" PRIx64, (uint64_t)smc_args->a0);
		DMSG("Expected 0x%x\n", OPTEE_SMC_CALL_WITH_ARG);
		smc_args->a0 = OPTEE_SMC_RETURN_EBADCMD;
		return;
	}
	parg = (uint64_t)smc_args->a1 << 32 | smc_args->a2;

	/* Check if this region is in static shared space */
	if (core_pbuf_is(CORE_MEM_NSEC_SHM, parg,
			  sizeof(struct optee_msg_arg))) {
		mobj = get_cmd_buffer(parg, &num_params);
	} else {
		if (parg & SMALL_PAGE_MASK) {
			smc_args->a0 = OPTEE_SMC_RETURN_EBADADDR;
			return;
		}
		mobj = map_cmd_buffer(parg, &num_params);
	}

	if (!mobj || !ALIGNMENT_IS_OK(parg, struct optee_msg_arg)) {
		EMSG("Bad arg address 0x%" PRIxPA, parg);
		smc_args->a0 = OPTEE_SMC_RETURN_EBADADDR;
		mobj_free(mobj);
		return;
	}

	arg = mobj_get_va(mobj, 0);
	assert(arg && mobj_is_nonsec(mobj));

	/* Enable foreign interrupts for STD calls */
	thread_set_foreign_intr(true);
	switch (arg->cmd) {
	case OPTEE_MSG_CMD_OPEN_SESSION:
		entry_open_session(smc_args, arg, num_params);
		break;
	case OPTEE_MSG_CMD_CLOSE_SESSION:
		entry_close_session(smc_args, arg, num_params);
		break;
	case OPTEE_MSG_CMD_INVOKE_COMMAND:
		entry_invoke_command(smc_args, arg, num_params);
		break;
	case OPTEE_MSG_CMD_CANCEL:
		entry_cancel(smc_args, arg, num_params);
		break;
	case OPTEE_MSG_CMD_REGISTER_SHM:
		register_shm(smc_args, arg, num_params);
		break;
	case OPTEE_MSG_CMD_UNREGISTER_SHM:
		unregister_shm(smc_args, arg, num_params);
		break;

	default:
		EMSG("Unknown cmd 0x%x\n", arg->cmd);
		smc_args->a0 = OPTEE_SMC_RETURN_EBADCMD;
	}
	mobj_free(mobj);
}

总结,CA和TA的通信,都是std call,如open invoke close…,其它的基本是fast call

标签:ATF,OPTEE,smc,args,fast,详解,call,SMC
来源: https://blog.51cto.com/u_15278218/2931177

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有