zoukankan      html  css  js  c++  java
  • 简谈高通Trustzone的实现【转】

    本文转载自:https://blog.csdn.net/hovan/article/details/42520879

    trust zone之我见知道,支持trustzone的芯片会跑在两个世界。

    普通世界、安全世界,对应高通这边是HLOS,QSEE。

    如下图:

    如下是HLOS与QSEE的软件架构图

    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代码来说明整个调用流程。

    如下图:

    qseecom_security_test.c

     
    1. int main( int argc, char *argv[] )

    2. {

    3. ....

    4.  
    5. /* Initialize the global/statics to zero */

    6. memset( g_qseeCommHandles, 0, sizeof(g_qseeCommHandles) );

    7. memset( g_xors, 0, sizeof(g_xors) );


    先初始化全局变量g_qseeCommHandles

     
    1. for( j = 0; j < NUM_CLIENTS; j++ ) {

    2. /* Initialize the barriers to ensure that commands aren't sent before the listeners

    3. * have been started. Otherwise, errors will be generated.

    4. */

    5. ret = sem_init( &barrier[j], 0, 0 );//初始化一个信号量

    6. if( ret ) {

    7. LOGD( "barrier init failed %i, %i", ret, errno );

    8. g_err = -1;

    9. break;

    10. }

    11.  
    12. ret = pthread_create( &threads[j], NULL, &test_thread, (void*)j );//创建test_thread线程

    13. }


    初始化一个barrier信号变量,用于线程创建时的同步

    然后调用pthread_create()函数创建test_thread线程,该线程将会起动QSApp。

     
    1. void *test_thread( void* threadid )

    2. {

    3. ...

    4. do {

    5. .....

    6. LOGD( "T%#X: Starting QSApp...", (uint32_t)threadid );

    7. ret = QSEECom_start_app( &g_qseeCommHandles[tid][0], "/firmware/image",//起动名为securitytest的QSApp

    8. "securitytest", sizeof(qseecom_req_res_t)*2 );

    9. LOGD( "T%#X: Started QSApp...", (uint32_t)threadid );

    10. CHECK_RETURN( ret, __LINE__ );


    跟着来到test_thread线程

    调用QSEECom_start_app()函数起动QSApp。

    这个函数在kernel实现 如下:

    qseecom.c

     
    1. static int qseecom_load_app(struct qseecom_dev_handle *data, void __user *argp)

    2. {

    3. ...

    4. /* Get the handle of the shared fd */

    5. ihandle = ion_import_dma_buf(qseecom.ion_clnt,

    6. load_img_req.ifd_data_fd);

    7. ...

    8. /* SCM_CALL to load the app and get the app_id back */

    9. ret = scm_call(SCM_SVC_TZSCHEDULER, 1, &load_req,

    10. sizeof(struct qseecom_load_app_ireq),

    11. &resp, sizeof(resp));


    Get shared buf fd,用于与安全世界通信

    调用scm_call()来陷入安全世界。

    scm_call()实现如下:

    arch/arm/mach-msm/scm.c

     
    1. int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,

    2. void *resp_buf, size_t resp_len)

    3. {

    4. ...

    5.  
    6. ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,

    7. resp_len, cmd, len);

    8. kfree(cmd);

    9. return ret;

    10. }


    scm_call_common的实现如下:

     
    1. static int scm_call_common(u32 svc_id, u32 cmd_id, const void *cmd_buf,

    2. size_t cmd_len, void *resp_buf, size_t resp_len,

    3. struct scm_command *scm_buf,

    4. size_t scm_buf_length)

    5. {

    6. ....

    7.  
    8. mutex_lock(&scm_lock);

    9. ret = __scm_call(scm_buf);//调用

    10. mutex_unlock(&scm_lock);

    11. if (ret)

    12. return ret;

    13.  
    14. rsp = scm_command_to_response(scm_buf);

    15. start = (unsigned long)rsp;

    16.  
    17. do {

    18. scm_inv_range(start, start + sizeof(*rsp));

    19. } while (!rsp->is_complete);

    20.  
    21. end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;

    22. scm_inv_range(start, end);

    23.  
    24. if (resp_buf)

    25. memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);

    26.  
    27. return ret;

    28. }

    调用__scm_call()陷入安全世界,回来后调用scm_get_response_buffer()获取安全世界返回的信息供上面QSApp client用

    __scm_call实现如下:

     
    1. static int __scm_call(const struct scm_command *cmd)

    2. {

    3. ...

    4.  
    5. ret = smc(cmd_addr);

    6. ...

    7.  
    8. return ret;

    9. }


    smc实现如下:

     
    1. static u32 smc(u32 cmd_addr)

    2. {

    3. int context_id;

    4. register u32 r0 asm("r0") = 1;

    5. register u32 r1 asm("r1") = (u32)&context_id;

    6. register u32 r2 asm("r2") = cmd_addr;

    7. do {

    8. asm volatile(

    9. __asmeq("%0", "r0")

    10. __asmeq("%1", "r0")

    11. __asmeq("%2", "r1")

    12. __asmeq("%3", "r2")

    13. #ifdef REQUIRES_SEC

    14. ".arch_extension sec "

    15. #endif

    16. "smc #0 @ switch to secure world "

    17. : "=r" (r0)

    18. : "r" (r0), "r" (r1), "r" (r2)

    19. : "r3");

    20. } while (r0 == SCM_INTERRUPTED);

    21.  
    22. return r0;

    23. }


    是一段汇编程序,好吧,安全世界的QSApp已经运行起来了,当QSApp完成相应服务后就会返回数据。这个函数就会返回。

    Starting QSApp已经完成,下面就注册listener,这个listener用于监听QSApp那边的请求。因为有时QSApp也需要HLOS这边做一些事。

    实现如下:

     
    1. void *listener_thread( void* threadid )

    2. {

    3. ....

    4.  
    5. do {

    6. ...

    7. /* Register as a listener with the QSApp */

    8. LOGD( "L%#X: Registering as listener with QSApp...", (uint32_t)threadid );

    9. ret = QSEECom_register_listener( &g_qseeCommHandles[parent_tid][tid], GET_LSTNR_SVC_ID(parent_tid, tid),

    10. sizeof(qseecom_req_res_t), 0 );

    11.  
    12. ....

    13.  
    14. for( ;; ) {

    15. /* Wait for request from the QSApp */

    16. ret = QSEECom_receive_req( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );

    17. if( ret ) break;

    18.  
    19. ....

    20.  
    21. /* Send the response to the QSApp */

    22. ret = QSEECom_send_resp( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );

    23. CHECK_RETURN( ret, __LINE__ );

    24. }

    25. } while( 0 );

    26. ...

    27. }


    这个函数比较长,简化一下,分步来看

    首先调用QSEECom_register_listener()函数来注册监听,告诉QSApp,我可以接收你的申请。

    再次看到for循环没有,这就是一直等待QSApp那边的消息,一但有消息QSEECom_reveive_req就返回,这边处理完之后。

    再调用qSEECom_send_resp()发送response给QSApp。

    无论是起动QSApp,还是注册listener都是线程中执行,一但所有线程都退出后就会调用QSEECom_shutdown_app()函数停止QSApp。

    整个过程执行完毕。如下:

     
    1. void *test_thread( void* threadid )

    2. {

    3. ...

    4. if ( g_qseeCommHandles[tid][0] != NULL ) {

    5. QSEECom_shutdown_app( &g_qseeCommHandles[tid][0] );

    6. }

    7. } while( 0 );

    8.  
    9. pthread_exit( NULL );

    10. return NULL;

    11. }


    注:QSEECom _XX开头的函数都在kernel中的qseecom.c里实现,scm系统调用,都在scm.c中实现。

    HLOS user层把握QSEEComAPI.h文件

    HLOS kernel层把握qseecom.c 和 scm.c两文件

    谢谢

    --------------------- 本文来自 hovan-邓永坚 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/hovan/article/details/42674055?utm_source=copy

    高通msm8916平台QSEE之trustzone分析

    从硬件背景上看TZ是在ARMv6的架构上产生的,在ARMv7上重新设计了,TZ的目的是提供一个软硬件都安全的环境,安全的环境是单独的运行在一个硬件空间,和非安全的进行了硬件上的隔离。

    QSEE主要负责加载APP,以及安全APP的堆栈管理,提供安全的API接口,安全的字符操作和LOG功能

    QSAPPS是高通开发的APP,运行在QSEE之上

    用户空间通过加载并调用QSEEAPP来是实现一些安全数据或敏感操作的加密

    QSEEComAPIlib是暴露给HLOS的API,HLOS客户端和监听端利用QSEEComdriver通过这些API来和QSEE进行数据的收和发。

    QSEEComclient是需要调用QSEECom_start_app来得到一个句柄指向QSEECom字符设备,并且可以用这个句柄来进行数据的首发

    QSEEComlistener需要用QSEECom_register_listener来注册,可以注册多个listener,每个listener所在的线程将会被挂起,直到接收到包含到该listener的信息。

    QSEEComdriver是一个字符型设备,通过IOCTL来和QSEECom进行通讯,这些IOCTL都应该被QSEEComAPI进行调用

    因为qsee不开源 tz部分只能看到少量符号

    所以主要从用户和开发角度观察tz

    开发需要开发相应的qsapp来实现安全的一些数据操作

    用户态中需要加载相应的qsapp,并且利用qsapplib提供的API来和qsapp进行通讯

    而qsapp如何与qsee进行通讯的呢

    qsapp则调用scm来将数据保存到安全空间

    为什么tz能实现安全呢

    从硬件上 就区分了两个世界,安全世界用一根总线,非安全世界用一根总线

    非安全世界的操作无法访问到安全世界

    必须通过qsee中的scm调用来访问

    而用户操作只能访问qsapp

    所以达到了安全保护的作用

    高通msm8916平台QSEE之trustzone分析

    转自http://www.it610.com/article/5292101.htm

  • 相关阅读:
    Generate SQL from Excel
    ASP.NET Web API系列教程目录
    进阶篇:以IL为剑,直指async/await
    30分钟?不需要,轻松读懂IL
    进程简介
    二维码详解
    通过IL分析C#中的委托、事件、Func、Action、Predicate之间的区别与联系
    我是一个线程
    ServiceLocator 简单示例(转)
    特性(C#)
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/9999794.html
Copyright © 2011-2022 走看看