从trust zone之我见知道,支持trustzone的芯片会跑在两个世界。
普通世界、安全世界,对应高通这边是HLOS,QSEE。
如下图:
![[转]简谈高通Trustzone的实现_第1张图片](http://img.e-com-net.com/image/info8/49e62b555bac414ca304e05aa663e549.png)
如下是HLOS与QSEE的软件架构图
![[转]简谈高通Trustzone的实现_第2张图片](http://img.e-com-net.com/image/info8/a5d5e6c3b45549bda4f5b4754eeb7b5e.jpg)
HLOS这两分为kernel层,user层。user层的通过qseecom提供的API起动trustzone那边的app。
qseecom driver 除了提供API,还调用scm函数做世界切换。
scm driver 那边接到qseecom的调用后,会把HLOS相关数据(包括指令参数)放入指它buffer,然后执行scm调用。
qsapp通过qsee提供的api接受来自HLOS那边的请求,并把执行结果返回HLOS。
qsee除了提供API,还与从monitor把来自HLOS的数据传给qsapp,然后把qsapp的数据返回给HLOS。
monitor就不用说了,切换世界用的,还处理shared buffer的内容。
是大概的架构图,细节比较复杂,没有开元。
下面通过一个简单的qseecom_security_test代码来说明整个调用流程。
如下图:
![[转]简谈高通Trustzone的实现_第3张图片](http://img.e-com-net.com/image/info8/1f909cf7d5034796b30388a6f8ca7275.png)
qseecom_security_test.c
- int main( int argc, char *argv[] )
- {
- ....
-
-
- memset( g_qseeCommHandles, 0, sizeof(g_qseeCommHandles) );
- memset( g_xors, 0, sizeof(g_xors) );
先初始化全局变量g_qseeCommHandles
- for( j = 0; j < NUM_CLIENTS; j++ ) {
-
-
-
- ret = sem_init( &barrier[j], 0, 0 );
- if( ret ) {
- LOGD( "barrier init failed %i, %i", ret, errno );
- g_err = -1;
- break;
- }
-
- ret = pthread_create( &threads[j], NULL, &test_thread, (void*)j );
- }
初始化一个barrier信号变量,用于线程创建时的同步
然后调用pthread_create()函数创建test_thread线程,该线程将会起动QSApp。
- void *test_thread( void* threadid )
- {
- ...
- do {
- .....
- LOGD( "T%#X: Starting QSApp...", (uint32_t)threadid );
- ret = QSEECom_start_app( &g_qseeCommHandles[tid][0], "/firmware/image",
- "securitytest", sizeof(qseecom_req_res_t)*2 );
- LOGD( "T%#X: Started QSApp...", (uint32_t)threadid );
- CHECK_RETURN( ret, __LINE__ );
跟着来到test_thread线程
调用QSEECom_start_app()函数起动QSApp。
这个函数在kernel实现 如下:
qseecom.c
- static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)
- {
- ...
-
- ihandle = ion_import_dma_buf(qseecom.ion_clnt,
- load_img_req.ifd_data_fd);
- ...
-
- ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,
- sizeof(struct qseecom_load_app_ireq),
- &resp, sizeof(resp));
Get shared buf fd,用于与安全世界通信
调用scm_call()来陷入安全世界。
scm_call()实现如下:
arch/arm/mach-msm/scm.c
- int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
- void *resp_buf, size_t resp_len)
- {
- ...
-
- ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,
- resp_len, cmd, len);
- kfree(cmd);
- return ret;
- }
scm_call_common的实现如下:
- static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf,
- size_t cmd_len, void *resp_buf, size_t resp_len,
- struct scm_command *scm_buf,
- size_t scm_buf_length)
- {
- ....
-
- mutex_lock(&scm_lock);
- ret = __scm_call(scm_buf);
- mutex_unlock(&scm_lock);
- if (ret)
- return ret;
-
- rsp = scm_command_to_response(scm_buf);
- start = (unsigned long)rsp;
-
- do {
- scm_inv_range(start, start + sizeof(*rsp));
- } while (!rsp->is_complete);
-
- end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;
- scm_inv_range(start, end);
-
- if (resp_buf)
- memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
-
- return ret;
- }
调用__scm_call()陷入安全世界,回来后调用scm_get_response_buffer()获取安全世界返回的信息供上面QSApp client用
__scm_call实现如下:
- static int __scm_call(const struct scm_command *cmd)
- {
- ...
-
- ret = smc(cmd_addr);
- ...
-
- return ret;
- }
smc实现如下:
- static u32 smc(u32 cmd_addr)
- {
- int context_id;
- register u32 r0 asm("r0") = 1;
- register u32 r1 asm("r1") = (u32)&context_id;
- register u32 r2 asm("r2") = cmd_addr;
- do {
- asm volatile(
- __asmeq("%0", "r0")
- __asmeq("%1", "r0")
- __asmeq("%2", "r1")
- __asmeq("%3", "r2")
- #ifdef REQUIRES_SEC
- ".arch_extension sec\n"
- #endif
- "smc #0 @ switch to secure world\n"
- : "=r" (r0)
- : "r" (r0), "r" (r1), "r" (r2)
- : "r3");
- } while (r0 == SCM_INTERRUPTED);
-
- return r0;
- }
是一段汇编程序,好吧,安全世界的QSApp已经运行起来了,当QSApp完成相应服务后就会返回数据。这个函数就会返回。
Starting QSApp已经完成,下面就注册listener,这个listener用于监听QSApp那边的请求。因为有时QSApp也需要HLOS这边做一些事。
实现如下:
- void *listener_thread( void* threadid )
- {
- ....
-
- do {
- ...
-
- LOGD( "L%#X: Registering as listener with QSApp...", (uint32_t)threadid );
- ret = QSEECom_register_listener( &g_qseeCommHandles[parent_tid][tid], GET_LSTNR_SVC_ID(parent_tid, tid),
- sizeof(qseecom_req_res_t), 0 );
-
- ....
-
- for( ;; ) {
-
- ret = QSEECom_receive_req( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );
- if( ret ) break;
-
- ....
-
-
- ret = QSEECom_send_resp( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );
- CHECK_RETURN( ret, __LINE__ );
- }
- } while( 0 );
- ...
- }
这个函数比较长,简化一下,分步来看
首先调用QSEECom_register_listener()函数来注册监听,告诉QSApp,我可以接收你的申请。
再次看到for循环没有,这就是一直等待QSApp那边的消息,一但有消息QSEECom_reveive_req就返回,这边处理完之后。
再调用qSEECom_send_resp()发送response给QSApp。
无论是起动QSApp,还是注册listener都是线程中执行,一但所有线程都退出后就会调用QSEECom_shutdown_app()函数停止QSApp。
整个过程执行完毕。如下:
- void *test_thread( void* threadid )
- {
- ...
- if ( g_qseeCommHandles[tid][0] != NULL ) {
- QSEECom_shutdown_app( &g_qseeCommHandles[tid][0] );
- }
- } while( 0 );
-
- pthread_exit( NULL );
- return NULL;
- }
注:QSEECom _XX开头的函数都在kernel中的qseecom.c里实现,scm系统调用,都在scm.c中实现。
HLOS user层把握QSEEComAPI.h文件
HLOS kernel层把握qseecom.c 和 scm.c两文件
谢谢