optee学习笔记_2_demo源码分析

OP-TEE demo源码分析

接上一节的分析,我们在这里详细的分析DEMO中CA和TA的源码,整个分析过程以捋清楚整个流程为主,在整个过程中,涉及的点很多,后面章节会再一一详解,如optee os的启动、driver的注册、以及守护进程tee_supplicant。


源码git地址:https://github.com/4LogCoder/optee.git

 

在ca中,我主要封装了三个函数,分别是:

TEEC_Result ca_demo_sha_open(void);
TEEC_Result ca_demo_sha(void* in, unsigned int size, void *out, SHA_MODE mode);
void ca_demo_sha_close(void);

在main函数中,我们首先调用ca_demo_sha_open()函数,建立和打开一个session,建立CA和TA的联系,然后通过ca_demo_sha()函数发起一次SHA计算。

首先我们先从ca_demo_open开始

TEEC_Result ca_demo_sha_open(void)
{
	TEEC_Result ret;
	TEEC_UUID id = TA_DEMO_UUID;
	//printf("ca_demo_sha_open\n",ret);
	ret = TEEC_InitializeContext(NULL, &context);
	if(ret != TEEC_SUCCESS){
		printf("ca_demo_sha_open:TEEC_InitializeContext failed %x\n",ret);
		goto exit_1;
	}
	ret = TEEC_OpenSession(&context, &session, &id, TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
	if(ret != TEEC_SUCCESS){
		printf("ca_demo_sha_open:TEEC_OpenSession failed %x\n",ret);
		goto finalizeContext;
	}
	sharedMem.size = 1024 + 1024;
	sharedMem.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
	ret = TEEC_AllocateSharedMemory(&context, &sharedMem);
	if(ret != TEEC_SUCCESS){
		printf("ca_demo_sha_open:TEEC_AllocateSharedMemory failed %x\n",ret);
		goto closeSession;
	}
	valid_session = 1;
	return ret;
closeSession:
	TEEC_CloseSession(&session);
finalizeContext:
	TEEC_FinalizeContext(&context);
exit_1:
	return ret;
}

在该函数中,我们依次调用了

TEEC_InitializeContext(NULL, &context);

TEEC_OpenSession(&context, &session, &id, TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);

TEEC_AllocateSharedMemory(&context, &sharedMem);

这三个函数均由libteec库实现,下面我们一一分析这三个函数都干了些什么

TEEC_InitializeContext

首先是调用TEEC_InitializeContext函数

函数原型:TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)

功能:初始化一个TEEC_Context变量

详解:在这函数中,首先拼接出devname字符串,然后调用teec_open_dev()函数,这个函数则主要是通过调用open()函数,打开对应的device,并通过ioctl获取版本等相关信息,进行相应的判断后,将打开的device的句柄fd放入TEEC_Context变量之中。

TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)
{
	char devname[PATH_MAX] = { 0 };
	int fd = 0;
	size_t n = 0;
	if (!ctx)
		return TEEC_ERROR_BAD_PARAMETERS;
	for (n = 0; n < TEEC_MAX_DEV_SEQ; n++) {
		uint32_t gen_caps = 0;
		snprintf(devname, sizeof(devname), "/dev/tee%zu", n);
		fd = teec_open_dev(devname, name, &gen_caps);
		if (fd >= 0) {
			ctx->fd = fd;
			ctx->reg_mem = gen_caps & TEE_GEN_CAP_REG_MEM;
			return TEEC_SUCCESS;
		}
	}
	return TEEC_ERROR_ITEM_NOT_FOUND;
}

static int teec_open_dev(const char *devname, const char *capabilities,
			 uint32_t *gen_caps)
{
	int fd = 0;
	struct tee_ioctl_version_data vers; 
	memset(&vers, 0, sizeof(vers));
	fd = open(devname, O_RDWR);
	if (fd < 0)
		return -1;
	if (ioctl(fd, TEE_IOC_VERSION, &vers)) {
		EMSG("TEE_IOC_VERSION failed");
		goto err;
	}
	/* We can only handle GP TEEs */
	if (!(vers.gen_caps & TEE_GEN_CAP_GP))
		goto err;
	if (capabilities) {
		if (strcmp(capabilities, "optee-tz") == 0) {
			if (vers.impl_id != TEE_IMPL_ID_OPTEE)
				goto err;
			if (!(vers.impl_caps & TEE_OPTEE_CAP_TZ))
				goto err;
		} else {
			/* Unrecognized capability requested */
			goto err;
		}
	}
	*gen_caps = vers.gen_caps;
	return fd;
err:
	close(fd);
	return -1;
}

TEEC_InitializeContext函数小结:这一函数主要是通过打开对应的设备文件,获取对应设备文件的fd,并进行版本检查,将fd填充至context变量。在整个过程中,并没有和某个特定的TA联系。此外这一函数必须是CA调用libteec库的第一个API,因为只用通过这一函数,才打开了对应的设备文件,和optee_linuxdriver建立了联系。

 

TEEC_OpenSession

调用TEEC_InitializeContext函数之后,再调用TEEC_OpenSession()函数

函数原型:

TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session,
			const TEEC_UUID *destination,
			uint32_t connection_method, const void *connection_data,
			TEEC_Operation *operation, uint32_t *ret_origin)

函数功能:在当前context中为CA和TA建立一个session,具体和那个TA建立session由TEEC_UUID变量决定

详解:在这一函数中,首先定义了一块sharememory用于存放ioctl的相关参数,并进行相应的预处理,最终通过调用ioctl发送TEE_IOC_OPEN_SESSION command,陷入正常世界的内核空间,到达tee driver中

TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session,
			const TEEC_UUID *destination,
			uint32_t connection_method, const void *connection_data,
			TEEC_Operation *operation, uint32_t *ret_origin)
{
	struct tee_ioctl_open_session_arg *arg = NULL;
	struct tee_ioctl_param *params = NULL;
	TEEC_Result res = TEEC_ERROR_GENERIC;
	uint32_t eorig = 0;
	int rc = 0;
	const size_t arg_size = sizeof(struct tee_ioctl_open_session_arg) +
				TEEC_CONFIG_PAYLOAD_REF_COUNT *
					sizeof(struct tee_ioctl_param);
	union {
		struct tee_ioctl_open_session_arg arg;
		uint8_t data[arg_size];
	} buf;
	struct tee_ioctl_buf_data buf_data;
	TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];
	memset(&buf, 0, sizeof(buf));
	memset(&shm, 0, sizeof(shm));
	memset(&buf_data, 0, sizeof(buf_data));
	(void)&connection_data;
	if (!ctx || !session) {
		eorig = TEEC_ORIGIN_API;
		res = TEEC_ERROR_BAD_PARAMETERS;
		goto out;
	}
	buf_data.buf_ptr = (uintptr_t)&buf;
	buf_data.buf_len = sizeof(buf);
	arg = &buf.arg;
	arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT;
	params = (struct tee_ioctl_param *)(arg + 1);
	uuid_to_octets(arg->uuid, destination);
	arg->clnt_login = connection_method;
	res = teec_pre_process_operation(ctx, operation, params, shm);
	if (res != TEEC_SUCCESS) {
		eorig = TEEC_ORIGIN_API;
		goto out_free_temp_refs;
	}
	rc = ioctl(ctx->fd, TEE_IOC_OPEN_SESSION, &buf_data);
	if (rc) {
		EMSG("TEE_IOC_OPEN_SESSION failed");
		eorig = TEEC_ORIGIN_COMMS;
		res = ioctl_errno_to_res(errno);
		goto out_free_temp_refs;
	}
	res = arg->ret;
	eorig = arg->ret_origin;
	if (res == TEEC_SUCCESS) {
		session->ctx = ctx;
		session->session_id = arg->session;
	}
	teec_post_process_operation(operation, params, shm);
out_free_temp_refs:
	teec_free_temp_refs(operation, shm);
out:
	if (ret_origin)
		*ret_origin = eorig;
	return res;
}

下面我们看一下ioctl的TEE_IOC_OPEN_SESSION command具体干了些啥

源码位置: tee/core.c

首先通过tee_ioctl调用tee_ioctl_open_session()函数,在该函数中,首先将用户空间的参数拷贝到正常世界的内核空间

static int tee_ioctl_open_session(struct tee_context *ctx,
				  struct tee_ioctl_buf_data __user *ubuf)
{
	int rc;
	size_t n;
	struct tee_ioctl_buf_data buf;
	struct tee_ioctl_open_session_arg __user *uarg;
	struct tee_ioctl_open_session_arg arg;
	struct tee_ioctl_param __user *uparams = NULL;
	struct tee_param *params = NULL;
	bool have_session = false;
	if (!ctx->teedev->desc->ops->open_session)
		return -EINVAL;
	if (copy_from_user(&buf, ubuf, sizeof(buf)))
		return -EFAULT;
	if (buf.buf_len > TEE_MAX_ARG_SIZE ||
	    buf.buf_len < sizeof(struct tee_ioctl_open_session_arg))
		return -EINVAL;

	uarg = u64_to_user_ptr(buf.buf_ptr);
	if (copy_from_user(&arg, uarg, sizeof(arg)))
		return -EFAULT;
	if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) != buf.buf_len)
		return -EINVAL;
	if (arg.num_params) {
		params = kcalloc(arg.num_params, sizeof(struct tee_param),
				 GFP_KERNEL);
		if (!params)
			return -ENOMEM;
		uparams = uarg->params;
		rc = params_from_user(ctx, params, arg.num_params, uparams);
		if (rc)
			goto out;
	}
	rc = ctx->teedev->desc->ops->open_session(ctx, &arg, params);
	if (rc)
		goto out;
	have_session = true;
	if (put_user(arg.session, &uarg->session) ||
	    put_user(arg.ret, &uarg->ret) ||
	    put_user(arg.ret_origin, &uarg->ret_origin)) {
		rc = -EFAULT;
		goto out;
	}
	rc = params_to_user(uparams, arg.num_params, params);
out:
	/*
	 * If we've succeeded to open the session but failed to communicate
	 * it back to user space, close the session again to avoid leakage.
	 */
	if (rc && have_session && ctx->teedev->desc->ops->close_session)
		ctx->teedev->desc->ops->close_session(ctx, arg.session);
	if (params) {
		/* Decrease ref count for all valid shared memory pointers */
		for (n = 0; n < arg.num_params; n++)
			if (tee_param_is_memref(params + n) &&
			    params[n].u.memref.shm)
				tee_shm_put(params[n].u.memref.shm);
		kfree(params);
	}
	return rc;
}

然后调用fops:的open_session变量,即optee_open_session函数,在optee_open_session函数中,通过get_msg_arg分配一块sharememory,并将参数填充至sharememory中,最后调用optee_do_call_with_arg函数,在这一函数中,通过optee->invoke_fn触发smc调用。

int optee_open_session(struct tee_context *ctx,
		       struct tee_ioctl_open_session_arg *arg,
		       struct tee_param *param)
{
	struct optee_context_data *ctxdata = ctx->data;
	int rc;
	struct tee_shm *shm;
	struct optee_msg_arg *msg_arg;
	phys_addr_t msg_parg;
	struct optee_session *sess = NULL;

	/* +2 for the meta parameters added below */
	shm = get_msg_arg(ctx, arg->num_params + 2, &msg_arg, &msg_parg);
	if (IS_ERR(shm))
		return PTR_ERR(shm);

	msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION;
	msg_arg->cancel_id = arg->cancel_id;

	/*
	 * Initialize and add the meta parameters needed when opening a
	 * session.
	 */
	msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
				  OPTEE_MSG_ATTR_META;
	msg_arg->params[1].attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT |
				  OPTEE_MSG_ATTR_META;
	memcpy(&msg_arg->params[0].u.value, arg->uuid, sizeof(arg->uuid));
	memcpy(&msg_arg->params[1].u.value, arg->uuid, sizeof(arg->clnt_uuid));
	msg_arg->params[1].u.value.c = arg->clnt_login;
	rc = optee_to_msg_param(msg_arg->params + 2, arg->num_params, param);
	if (rc)
		goto out;
	sess = kzalloc(sizeof(*sess), GFP_KERNEL);
	if (!sess) {
		rc = -ENOMEM;
		goto out;
	}

	if (optee_do_call_with_arg(ctx, msg_parg)) {
		msg_arg->ret = TEEC_ERROR_COMMUNICATION;
		msg_arg->ret_origin = TEEC_ORIGIN_COMMS;
	}

	if (msg_arg->ret == TEEC_SUCCESS) {
		/* A new session has been created, add it to the list. */
		sess->session_id = msg_arg->session;
		mutex_lock(&ctxdata->mutex);
		list_add(&sess->list_node, &ctxdata->sess_list);
		mutex_unlock(&ctxdata->mutex);
	} else {
		kfree(sess);
	}
	if (optee_from_msg_param(param, arg->num_params, msg_arg->params + 2)) {
		arg->ret = TEEC_ERROR_COMMUNICATION;
		arg->ret_origin = TEEC_ORIGIN_COMMS;
		/* Close session again to avoid leakage */
		optee_close_session(ctx, msg_arg->session);
	} else {
		arg->session = msg_arg->session;
		arg->ret = msg_arg->ret;
		arg->ret_origin = msg_arg->ret_origin;
	}
out:
	tee_shm_free(shm);
	return rc;
}

在optee_do_call_with_arg函数中,通过invoke_fn函数触发smc调用

u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
{
	struct optee *optee = tee_get_drvdata(ctx->teedev);
	struct optee_call_waiter w;
	struct optee_rpc_param param = { };
	struct optee_call_ctx call_ctx = { };
	u32 ret;

	param.a0 = OPTEE_SMC_CALL_WITH_ARG;
	reg_pair_from_64(¶m.a1, ¶m.a2, parg);
	/* Initialize waiter */
	optee_cq_wait_init(&optee->call_queue, &w);
	while (true) {
		struct arm_smccc_res res;

		optee->invoke_fn(param.a0, param.a1, param.a2, param.a3,
				 param.a4, param.a5, param.a6, param.a7,
				 &res);

		if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) {
			/*
			 * Out of threads in secure world, wait for a thread
			 * become available.
			 */
			optee_cq_wait_for_completion(&optee->call_queue, &w);
		} else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
			param.a0 = res.a0;
			param.a1 = res.a1;
			param.a2 = res.a2;
			param.a3 = res.a3;
			optee_handle_rpc(ctx, ¶m, &call_ctx);
		} else {
			ret = res.a0;
			break;
		}
	}

	optee_rpc_finalize_call(&call_ctx);
	/*
	 * We're done with our thread in secure world, if there's any
	 * thread waiters wake up one.
	 */
	optee_cq_wait_final(&optee->call_queue, &w);

	return ret;
}

在这里具体是怎么触发smc调用的,先大致介绍下,后面章节会详细讲解。这个函数指针是通过get_invoke_func函数遍历设备树,获取相应的smc调用方式

/* Simple wrapper functions to be able to use a function pointer */
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);
}

static void optee_smccc_hvc(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_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}
static optee_invoke_fn *get_invoke_func(struct device_node *np)
{
	const char *method;
	pr_info("probing for conduit method from DT.\n");
	if (of_property_read_string(np, "method", &method)) {
		pr_warn("missing \"method\" property\n");
		return ERR_PTR(-ENXIO);
	}

	if (!strcmp("hvc", method))
		return optee_smccc_hvc;
	else if (!strcmp("smc", method))
		return optee_smccc_smc;
	pr_warn("invalid \"method\" property: %s\n", method);
	return ERR_PTR(-EINVAL);
}

触发smc调用之后,处理器就进入Monitor模式,并将一系列的寄存器值更新,获取MVBAR寄存器中异常向量表中的sm_vect_table的基地址,并命中smc异常,从而进入sm_smc_entry函数。函数位置:optee_os-master\core\arch\arm\sm\sm_a32.S,然后更新一些寄存器值,最后依次调用smc_from_nsec -> sm_from_nsec

uint32_t sm_from_nsec(struct sm_ctx *ctx)
{
	uint32_t *nsec_r0 = (uint32_t *)(&ctx->nsec.r0);
	/*
	 * Check that struct sm_ctx has the different parts properly
	 * aligned since the stack pointer will be updated to point at
	 * different parts of this struct.
	 */
	COMPILE_TIME_ASSERT(!(offsetof(struct sm_ctx, sec.r0) % 8));
	COMPILE_TIME_ASSERT(!(offsetof(struct sm_ctx, nsec.r0) % 8));
	COMPILE_TIME_ASSERT(!(sizeof(struct sm_ctx) % 8));

#ifdef CFG_SM_PLATFORM_HANDLER
	if (sm_platform_handler(ctx) == SM_HANDLER_SMC_HANDLED)
		return SM_EXIT_TO_NON_SECURE;
#endif
#ifdef CFG_PSCI_ARM32
	if (OPTEE_SMC_OWNER_NUM(*nsec_r0) == OPTEE_SMC_OWNER_STANDARD) {
		smc_std_handler((struct thread_smc_args *)nsec_r0, &ctx->nsec);
		return SM_EXIT_TO_NON_SECURE;
	}
#endif
	sm_save_unbanked_regs(&ctx->nsec.ub_regs);
	sm_restore_unbanked_regs(&ctx->sec.ub_regs);
	memcpy(&ctx->sec.r0, nsec_r0, sizeof(uint32_t) * 8);
	if (OPTEE_SMC_IS_FAST_CALL(ctx->sec.r0))
		ctx->sec.mon_lr = (uint32_t)&thread_vector_table.fast_smc_entry;
	else
		ctx->sec.mon_lr = (uint32_t)&thread_vector_table.std_smc_entry;
	return SM_EXIT_TO_SECURE;
}

在sm_from_nsec函数中,通过判断,最后进入std_smc_entry,调用entry_open_session()处理,最后通过一系列的调用,直到调用到ta_load()函数。

static TEE_Result ta_load(struct tee_tadb_ta_read *ta)
{
	TEE_Result res;
	const size_t sz = ta->entry.prop.custom_size + ta->entry.prop.bin_size;

	if (ta->ta_mobj)
		return TEE_SUCCESS;

	ta->ta_mobj = thread_rpc_alloc_payload(sz);
	if (!ta->ta_mobj)
		return TEE_ERROR_OUT_OF_MEMORY;

	ta->ta_buf = mobj_get_va(ta->ta_mobj, 0);
	assert(ta->ta_buf);

	struct thread_param params[] = {
		[0] = THREAD_PARAM_VALUE(IN, OPTEE_RPC_FS_READ, ta->fd, 0),
		[1] = THREAD_PARAM_MEMREF(OUT, ta->ta_mobj, 0, sz),
	};

	res = thread_rpc_cmd(OPTEE_RPC_CMD_FS, ARRAY_SIZE(params), params);
	if (res) {
		thread_rpc_free_payload(ta->ta_mobj);
		ta->ta_mobj = NULL;
	}
	return res;
}

在ta_load函数中,主要通过调用thread_rpc_cmd函数,再在thread_rpc_cmd函数中调用thread_rpc函数,发起RPC请求,其中thread_rpc由汇编实现,通过smc指令,将系统从secure world转换至Normal World

FUNC thread_smc , :
	smc	#0
	ret
END_FUNC thread_smc

在linux kernel tee driver中,driver将会通知supplicant完成TA文件的加载(后面章节中,再详细讲解这一过程)。

 

至此,我们已经通过调用TEEC_OpenSession完成了相应TA文件的加载。

TEEC_OpenSession小结:在这一函数中,我们通过ioctl调用,陷入内核空间,然后调用smc指令,从NormalWorld转换至SecureWorld,转换至secureWorld后,由optee os解析command,准备load相应的TA文件,在这一步骤中,还需要调用smc指令,转换至NormalWorld,请求tee_supplicat进程在文件系统中找到对应UUID的TA文件,并传至secureWorld中。至此TEEC_OpenSession函数才全部执行完成。

 

AllocateSharedMemory

此后,我们再调用AllocateSharedMemory函数,分配一块shareMemory,存放待计算的明文。

通过上述三个函数的准备工作之后,我们再在main函数中,调用ca_demo_sha()函数,发起一次计算请求。

TEEC_Result ca_demo_sha(void* in, unsigned int size, void *out, SHA_MODE mode)
{
	TEEC_Result ret;
	TEEC_Operation op;
	uint32_t err_origin;
	
	//printf("ca_demo_sha\n");
	if(!valid_session)
		return TEEC_ERROR_BAD_STATE;
	if(size > 1024)
		return TEEC_ERROR_BAD_PARAMETERS;

	memcpy(sharedMem.buffer, in, size);
	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_MEMREF_PARTIAL_INPUT,
										TEEC_MEMREF_PARTIAL_OUTPUT, TEEC_NONE);
	op.started = 1;
	op.params[0].value.a = mode;
	op.params[1].memref.parent = &sharedMem;
	op.params[1].memref.offset = 0;
	op.params[1].memref.size = size;

	op.params[2].memref.parent = &sharedMem;
	op.params[2].memref.offset = 1024;
	op.params[2].memref.size = 1024;
	
	ret = TEEC_InvokeCommand(&session, CA_DEMO_CMD_SHA, &op, &err_origin);
	if(ret != TEEC_SUCCESS){
		printf("invoke faild %d\n",ret);
		return ret;
	}
	
	memcpy(out, (void*)((char *)sharedMem.buffer + 1024), op.params[2].memref.size);
	uint32_t len = op.params[2].memref.size;
	printf("ca_demo_sha: len:%d sha:\n", len);
	int i;
	char* p = out;
	for(i= 0; i

在这一过程中,我们首先需要构建填充好相应的参数(具体可参考官方文档),然后调用TEEC_InvokeCommand函数,正式发起一次计算command。

整个过程和TEEC_InvokeCommand类似,主要是通过ioctl陷入内核,然后调用smc指令,转换至secureWorld,并由optee os解析指令,最终调用到TA文件中的TA_InvokeCommandEntryPoint函数

TEE_Result TA_InvokeCommandEntryPoint(void *pSessionContext,uint32_t nCommandID,
                                    uint32_t nParamTypes, TEE_Param pParams[4])

至此,就开始执行用户设定的一些code.

TEE_Result TA_InvokeCommandEntryPoint(void *pSessionContext,uint32_t nCommandID,
                                    uint32_t nParamTypes, TEE_Param pParams[4])
{
	DMSG("TA Invoke Command Entry Point for %s commandId: %d\n",TA_NAME,nCommandID);
	switch(nCommandID){
		case TA_DEMO_CMD_SHA:
			return ta_demo_entry_sha(nParamTypes, pParams);
		case TA_DEMO_CMD_RSA:
			return ta_demo_entry_rsa(nParamTypes, pParams);
		default:
			return TEE_ERROR_BAD_PARAMETERS;
	}
}

在此计算完成之后,就会通过类似于RPC请求的方式,将结果返回值NormalWorld.

然后再在main函数中,调用ca_demo_close()函数,释放相应的session和设备。在这里就不再详细介绍了。

 

总结下,整个demo主要就是封装了ca_demo_sha_open(),ca_demo_sha(),ca_demo_sha_close,首先在ca_demo_sha_oopen函数中,调用了TEEC_InitializeContext、TEEC_OpenSession、TEEC_AllocateSharedMemory三个函数,打开了相应的tee设备文件,并将指定的TA load进了内存之中,并和当前CA进行了关联;然后通过在ca_demo_sha()中调用TEEC_InvokeCommand函数,最终调用到TA文件中的TEE_Result TA_InvokeCommandEntryPoint(void *pSessionContext,uint32_t nCommandID, uint32_t  nParamTypes,  TEE_Param  pParams[4])函数,在这一函数中执行相应的SHA操作,并将计算结果放在sharememory中,返回至NormalWorld中。

你可能感兴趣的:(OP-TEE)