本文轉載自:https://blog.csdn.net/hovan/article/details/42520879html
從trust zone之我見知道,支持trustzone的芯片會跑在兩個世界。編程
普通世界、安全世界,對應高通這邊是HLOS,QSEE。api
以下圖:安全
以下是HLOS與QSEE的軟件架構圖架構
HLOS這兩分爲kernel層,user層。user層的經過qseecom提供的API起動trustzone那邊的app。app
qseecom driver 除了提供API,還調用scm函數作世界切換。函數
scm driver 那邊接到qseecom的調用後,會把HLOS相關數據(包括指令參數)放入指它buffer,而後執行scm調用。ui
qsapp經過qsee提供的api接受來自HLOS那邊的請求,並把執行結果返回HLOS。加密
qsee除了提供API,還與從monitor把來自HLOS的數據傳給qsapp,而後把qsapp的數據返回給HLOS。.net
monitor就不用說了,切換世界用的,還處理shared buffer的內容。
是大概的架構圖,細節比較複雜,沒有開元。
下面經過一個簡單的qseecom_security_test代碼來講明整個調用流程。
以下圖:
qseecom_security_test.c
int main( int argc, char *argv[] )
{
....
/* Initialize the global/statics to zero */
memset( g_qseeCommHandles, 0, sizeof(g_qseeCommHandles) );
memset( g_xors, 0, sizeof(g_xors) );
先初始化全局變量g_qseeCommHandles
for( j = 0; j < NUM_CLIENTS; j++ ) {
/* Initialize the barriers to ensure that commands aren't sent before the listeners
* have been started. Otherwise, errors will be generated.
*/
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 );//建立test_thread線程
}
初始化一個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的QSApp
"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)
{
...
/* Get the handle of the shared fd */
ihandle = ion_import_dma_buf(qseecom.ion_clnt,
load_img_req.ifd_data_fd);
...
/* SCM_CALL to load the app and get the app_id back */
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 {
...
/* Register as a listener with the QSApp */
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( ;; ) {
/* Wait for request from the QSApp */
ret = QSEECom_receive_req( g_qseeCommHandles[parent_tid][tid], req_res, sizeof(qseecom_req_res_t) );
if( ret ) break;
....
/* Send the response to the QSApp */
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兩文件
謝謝
--------------------- 本文來自 hovan-鄧永堅 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/hovan/article/details/42674055?utm_source=copy
從硬件背景上看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
因此達到了安全保護的做用