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.  
  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\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.  
  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