33. secure world对smc请求的处理------invoke command操作在OP-TEE中的实现

    历经一年多时间的系统整理合补充,《手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解 》一书得以出版,书中详细介绍了TEE以及系统安全中的所有内容,全书按照从硬件到软件,从用户空间到内核空间的顺序对TEE技术详细阐述,读者可从用户空间到TEE内核一步一步了解系统安全的所有内容,同时书中也提供了相关的示例代码,读者可根据自身实际需求开发TA。目前该书已在天猫、京东、当当同步上线,链接如下:

当当购买地址

京东购买地址

天猫购买地址

非常感谢在此期间大家的支持以及各位友人的支持和帮助!!!。

  

在REE侧的CA执行open session成功之后,CA就可以使用获取到的session和command ID调用TEEC_InvokeCommand接口来实现让TA指定特定的command的操作。在REE侧调用TEEC_InvokeCommand接口之后,该函数会将调用时带入的session变量,command ID,以及需要传递給TA的参数信息通过ioctl的系统调用发送到OP-TEE的驱动中,驱动最终会调用optee_invoke_func函数会将需要传递給TA的参数信息保存在共享内存中,并触发smc操作,切换到monitor模式进行secure world端的处理,invoke command操作的smc请求最终会被作为标准smc(std smc)进行解析并建立一个专门的thread进入thread_std_smc_entry函数执行,线程运行到tee_entry_std函数时会对smc请求做出判定进入invoke command分支。出发smc之后在secure world端的完成执行流程如下图所示:

1. 切换到userspace运行

  线程会调用session中注册的ops成员中的enter_invoke_cmd指定的函数来处理invoke command操作,而enter_invoke_cmd指向user_ta_enter_invoke_cmd函数,该函数会调用user_ta_enter执行,在user_ta_enter函数中加载userspace的上下文然后调用__thread_enter_user_mode函数将该线程切换到userspace继续执行,该函数是以汇编的方式实现,其内容如下:

FUNC __thread_enter_user_mode , :
UNWIND(	.fnstart)
UNWIND(	.cantunwind)
	/*
	 * Save all registers to allow syscall_return() to resume execution
	 * as if this function would have returned. This is also used in
	 * syscall_panic().
	 *
	 * If stack usage of this function is changed
	 * thread_unwind_user_mode() has to be updated.
	 */
	push    {r4-r12,lr}	//保存r4~r12和lr寄存器中的值

/* 将用户栈地址保存到r4寄存去中 */
	ldr     r4, [sp, #(10 * 0x4)]   /* user stack pointer */

/* 将user的入口功能函数的地址保存到r5寄存器中 */
	ldr     r5, [sp, #(11 * 0x4)]   /* user function */

/* 将spsr的数据存放到r6寄存器中 */
	ldr     r6, [sp, #(12 * 0x4)]   /* spsr */

	/*
	 * Set the saved Processors Status Register to user mode to allow
	 * entry of user mode through movs below.
	 */
	msr     spsr_cxsf, r6	//设置状态保存寄存器,允许记录用户模式
	
	/*
	 * Save old user sp and set new user sp.
	 */
	cps	#CPSR_MODE_SYS	//进入SYS模式
	mov	r6, sp	//保存sys模式的sp值到r6
	mov     sp, r4	//将user的sp指针保存到sp中
	cps	#CPSR_MODE_SVC	//切换到SVC模式
	push	{r6,r7}	

	/*
	* Don't allow return from this function, return is done through
	* thread_unwind_user_mode() below.
	*/
	mov     lr, #0
	/* Call the user function with its arguments */
	movs    pc, r5	//将跳转地址存放到pc中下一指令就会跳转到指定的user functon执行
UNWIND(	.fnend)

  movs pc, r5是将参数中指定的user function的地址赋值给pc,下一条指令就会跳转到pc执行的地址继续运行,sp寄存器也被赋值成了userspace的栈指针,且spsr也被设置成了传入的r6寄存器的数据,r6存放的是spsr数据,该数据在调用__thread_enter_user_mode 之前通过执行get_spsr函数被置成user模式的spsr。所以完成了pc指针的赋值之后就能够直接进入到userspace中运行指定的user function.

2. user function的值

  user function的值就是在调用thread_enter_user_mode传入的utc->entry_func指定的函数地址。该地址在Open session的操作是调用ta_load的时候被赋值成ta_head->entry.ptr64。代码如下

utc->entry_func = ta_head->entry.ptr64;

  而ta_head即指向与uuid想对应的TA image中的ta_head段中的内容,而ta_head段中存放的内容可从user_ta_head.c文件中查看到,如下:

const struct ta_head ta_head __section(".ta_head") = {
	/* UUID, unique to each TA */
	.uuid = TA_UUID,	//该TA的UUID值
	/*
	 * According to GP Internal API, TA_FRAMEWORK_STACK_SIZE corresponds to
	 * the stack size used by the TA code itself and does not include stack
	 * space possibly used by the Trusted Core Framework.
	 * Hence, stack_size which is the size of the stack to use,
	 * must be enlarged
	 */
/* 设定该TA在userspace栈的大小 */
	.stack_size = TA_STACK_SIZE + TA_FRAMEWORK_STACK_SIZE,
	.flags = TA_FLAG_USER_MODE | TA_FLAGS, 	//设定flag
#ifdef __ILP32__
	/*
	 * This workaround is neded on 32-bit because it seems we can't
	 * initialize a 64-bit integer from the address of a function.
	 */
	.entry.ptr32 = { .lo = (uint32_t)__utee_entry },		//指定TA的entry fuctiong函数指针
#else
	.entry.ptr64 = (uint64_t)__utee_entry,
#endif
};

  为兼容64位系统,在传入user function的时候使用的是ptr64。也就是说在thread_enter_user_mode函数中传入到__thread_enter_user_mode 函数中的entry_func的值是__utee_entry函数的地址。由此可见调用完__thread_enter_user_mode之后程序是进入到__utee_entry函数继续执行。__utee_entry函数的内容如下:

void __noreturn __utee_entry(unsigned long func, unsigned long session_id,
			struct utee_params *up, unsigned long cmd_id)
{
	TEE_Result res;

/* 根据传入的func值来确定进入那个分支 */
	switch (func) {
/* 在user space中处理open session操作 */
	case UTEE_ENTRY_FUNC_OPEN_SESSION:
		res = entry_open_session(session_id, up);
		break;
/* 在user space中处理close session操作 */
	case UTEE_ENTRY_FUNC_CLOSE_SESSION:
		res = entry_close_session(session_id);
		break;
/* 在user space中处理invoke command操作 */
	case UTEE_ENTRY_FUNC_INVOKE_COMMAND:
		res = entry_invoke_command(session_id, up, cmd_id);
		break;
	default:
		res = 0xffffffff;
		TEE_Panic(0);
		break;
	}
	ta_header_save_params(0, NULL);
	utee_return(res);
}

3. user space中的entry_invoke_command

  在OP-TEE中定义了两个entry_invoke_command函数,一个是kernel space的函数,一个是user space的函数。当程序运行到user space后则会调用user space的entry_invoke_command函数执行invoke command的操作。user space的entry_invoke_command函数定义在optee_os/lib/libutee/arch/arm/user_ta_entry.c文件中,该函数内容如下:

static TEE_Result entry_invoke_command(unsigned long session_id,
			struct utee_params *up, unsigned long cmd_id)
{
	TEE_Result res;
	uint32_t param_types;
	TEE_Param params[TEE_NUM_PARAMS];
	struct ta_session *session = ta_header_get_session(session_id);

	if (!session)
		return TEE_ERROR_BAD_STATE;

/* 检查传入到user space的参数是否合法 */
	__utee_to_param(params, ¶m_types, up);
	ta_header_save_params(param_types, params);

/* 调用TA的TA_InvokeCommandEntryPoint函数 */
	res = TA_InvokeCommandEntryPoint(session->session_ctx, cmd_id,
					 param_types, params);

	__utee_from_param(up, param_types, params);
	return res;
}

  调用到user space的entry_invoke_command函数的时候,其实线程已经进入到了TA image上下文中运行了,所以当调用TA_InvokeCommandEntryPoint函数的时候就会去执行TA image中定义的TA_InvokeCommandEntryPoint函数,而该函数具体会执行什么操作就由特定的TA决定了,一般做法是根据command id的值去执行特定的操作






 

猜你喜欢

转载自blog.csdn.net/shuaifengyun/article/details/73277082