Brief Talk about the Realization of Qualcomm Trustzone

FROM: http://blog.csdn.net/hovan/article/details/42674055


from My Opinion on trust zone You know, trustzone-enabled chips run in two worlds.

Ordinary world, security world, corresponding to Qualcomm here is HLOS,QSEE.

The following picture:



Here are the HLOS and QSEE software Framework chart




HLOS is divided into kernel layer and user layer. The user layer starts the app on trustzone through the API provided by qseecom.

In addition to providing API s, qseecom driver also calls scm functions for world switching.

When the scm driver receives a call from qseecom, it puts HLOS-related data (including instruction parameters) into its buffer, and then executes the scm call.

qsapp accepts requests from HLOS through the api provided by qsee and returns the execution results to HLOS.

In addition to providing API s, qsee also passes data from HLOS to qsapp from monitor, and then returns data from qsapp to HLOS.

The monitor doesn't need to say that it handles shared buffer s as well as switching the world.


It's a rough sketch. The details are rather complicated and there is no open source.


The whole invocation process is illustrated by a simple qseecom_security_test code.

The following picture:




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) );  

Initialize the global variable g_qseeCommHandles first

  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 );//Initialize a semaphore  
  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 );//Create test_thread thread thread  
  13.     }  

Initialize a barrier signal variable for synchronization at thread creation

Then call the pthread_create() function to create the test_thread thread thread, which will start 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",//Start QSApp called security test  
  8.             "securitytest"sizeof(qseecom_req_res_t)*2 );  
  9.     LOGD( "T%#X: Started QSApp...", (uint32_t)threadid );  
  10.     CHECK_RETURN( ret, __LINE__ );  

Follow to the test_thread thread thread

Call the QSEECom_start_app() function to start QSApp.

This function is implemented in kernel as follows:


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, for communication with the security world

Call scm_call() to get into the safe world.

scm_call() is implemented as follows:

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

The implementation of scm_call_common is as follows:

  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);//call  
  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. }  


Call _scm_call() into the safe world and call scm_get_response_buffer() to get the information returned from the safe world for the above QSApp client to use

_ scm_call is implemented as follows:

  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 is implemented as follows:

  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 is an assembler. Well, QSApp in the safe world is running. When QSApp completes the corresponding service, it will return the data. This function will return.

Starting QSApp has been completed. Now register listener, which is used to listen for requests from QSApp. Because sometimes QSApp also needs HLOS to do something.

The realization is as follows:


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

This function is longer, simplify it, step by step.

First, call the QSEECom_register_listener() function to register the listener and tell QSApp that I can receive your application.

If you see the for loop again, it's waiting for the message from QSApp, but if there's a message from QSEECom_reveive_req, it's returned, and then it's processed here.

Then call qSEECom_send_resp() to send the response to QSApp.


Whether starting QSApp or registering listener is executed in threads, once all threads exit, the QSEECom_shutdown_app() function is called to stop QSApp.

The whole process is completed. As follows:

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

Note: The functions at the beginning of QSEECom_XX are implemented in qseecom.c of kernel, and the system calls of SCM are implemented in scm.c.

HLOS user layer grasps QSEEComAPI.h file

HLOS kernel layer grasps qseecom.c and scm.c files

Thank you

Posted by gtomescu on Sun, 21 Apr 2019 20:33:35 -0700