1. 程式人生 > >[轉]簡談高通Trustzone的實現

[轉]簡談高通Trustzone的實現

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.   /* Initialize the global/statics to zero */
  5.   memset( g_qseeCommHandles, 0, sizeof(g_qseeCommHandles) );  
  6.   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.       ret = pthread_create( &threads[j], NULL, &test_thread, (void*)j );//建立test_thread執行緒
  12.     }  

初始化一個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. staticint 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, constvoid *cmd_buf, size_t cmd_len,  
  2.         void *resp_buf, size_t resp_len)  
  3. {  
  4.     ...  
  5.     ret = scm_call_common(svc_id, cmd_id, cmd_buf, cmd_len, resp_buf,  
  6.                 resp_len, cmd, len);  
  7.     kfree(cmd);  
  8.     return ret;  
  9. }  

scm_call_common的實現如下:
  1. staticint scm_call_common(u32 svc_id, u32 cmd_id, constvoid *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.     mutex_lock(&scm_lock);  
  8.     ret = __scm_call(scm_buf);//呼叫
  9.     mutex_unlock(&scm_lock);  
  10.     if (ret)  
  11.         return ret;  
  12.     rsp = scm_command_to_response(scm_buf);  
  13.     start = (unsigned long)rsp;  
  14.     do {  
  15.         scm_inv_range(start, start + sizeof(*rsp));  
  16.     } while (!rsp->is_complete);  
  17.     end = (unsigned long)scm_get_response_buffer(rsp) + resp_len;  
  18.     scm_inv_range(start, end);  
  19.     if (resp_buf)  
  20.         memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);  
  21.     return ret;  
  22. }  
呼叫__scm_call()陷入安全世界,回來後呼叫scm_get_response_buffer()獲取安全世界返回的資訊供上面QSApp client用

__scm_call實現如下:

  1. staticint __scm_call(conststruct scm_command *cmd)  
  2. {  
  3. ...  
  4.     ret = smc(cmd_addr);  
  5. ...  
  6.     return ret;  
  7. }  

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\n"
  15. #endif
  16.             "smc    #0  @ switch to secure world\n"
  17.             : "=r" (r0)  
  18.             : "r" (r0), "r" (r1), "r" (r2)  
  19.             : "r3");  
  20.     } while (r0 == SCM_INTERRUPTED);  
  21.     return r0;  
  22. }  

是一段彙編程式,好吧,安全世界的QSApp已經執行起來了,當QSApp完成相應服務後就會返回資料。這個函式就會返回。

Starting QSApp已經完成,下面就註冊listener,這個listener用於監聽QSApp那邊的請求。因為有時QSApp也需要HLOS這邊做一些事。

實現如下:

  1. void *listener_thread( void* threadid )  
  2. {  
  3. ....  
  4.   do {  
  5. ...  
  6.     /* Register as a listener with the QSApp */
  7.     LOGD( "L%#X: Registering as listener with QSApp...", (uint32_t)threadid );  
  8.     ret = QSEECom_register_listener( &g_qseeCommHandles[parent_tid][tid], GET_LSTNR_SVC_ID(parent_tid, tid),  
  9.             sizeof(qseecom_req_res_t), 0 );  
  10. ....  
  11.     for( ;; ) {  
  12.       /* Wait for request from the QSApp */
  13.       ret = QSEECom_receive_req( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );  
  14.       if( ret ) break;  
  15.      ....  
  16.       /* Send the response to the QSApp */
  17.       ret = QSEECom_send_resp( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );  
  18.       CHECK_RETURN( ret, __LINE__ );  
  19.     }  
  20.   } while( 0 );  
  21. ...  
  22. }  

這個函式比較長,簡化一下,分步來看

首先呼叫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.   pthread_exit( NULL );  
  9.   return NULL;  
  10. }  

注:QSEECom _XX開頭的函式都在kernel中的qseecom.c裡實現,scm系統呼叫,都在scm.c中實現。

HLOS user層把握QSEEComAPI.h檔案

HLOS kernel層把握qseecom.c 和 scm.c兩檔案

謝謝