/*
* Called when the instance of the TA is created. This is the first call in
* the TA.
*/TEE_ResultTA_CreateEntryPoint(void);/*
* Called when the instance of the TA is destroyed if the TA has not
* crashed or panicked. This is the last call in the TA.
*/voidTA_DestroyEntryPoint(void);/*
* Called when a new session is opened to the TA. *sess_ctx can be updated
* with a value to be able to identify this session in subsequent calls to the
* TA. In this function you will normally do the global initialization for the
* TA.
*/TEE_ResultTA_OpenSessionEntryPoint(uint32_tparam_types,TEE_Param__maybe_unusedparams[4],void__maybe_unused**sess_ctx);/*
* Called when a session is closed, sess_ctx hold the value that was
* assigned by TA_OpenSessionEntryPoint().
*/voidTA_CloseSessionEntryPoint(void__maybe_unused*sess_ctx);/*
* Called when a TA is invoked. sess_ctx hold that value that was
* assigned by TA_OpenSessionEntryPoint(). The rest of the paramters
* comes from normal world.
*/TEE_ResultTA_InvokeCommandEntryPoint(void__maybe_unused*sess_ctx,uint32_tcmd_id,uint32_tparam_types,TEE_Paramparams[4]);
intmain(void){TEEC_Resultres;TEEC_Contextctx;TEEC_Sessionsess;TEEC_Operationop;TEEC_UUIDuuid=TA_HELLO_WORLD_UUID;uint32_terr_origin;/* Initialize a context connecting us to the TEE */res=TEEC_InitializeContext(NULL,&ctx);if(res!=TEEC_SUCCESS)errx(1,"TEEC_InitializeContext failed with code 0x%x",res);/*
* Open a session to the "hello world" TA, the TA will print "hello
* world!" in the log when the session is created.
*/res=TEEC_OpenSession(&ctx,&sess,&uuid,TEEC_LOGIN_PUBLIC,NULL,NULL,&err_origin);if(res!=TEEC_SUCCESS)errx(1,"TEEC_Opensession failed with code 0x%x origin 0x%x",res,err_origin);/*
* Execute a function in the TA by invoking it, in this case
* we're incrementing a number.
*
* The value of command ID part and how the parameters are
* interpreted is part of the interface provided by the TA.
*//* Clear the TEEC_Operation struct */memset(&op,0,sizeof(op));/*
* Prepare the argument. Pass a value in the first parameter,
* the remaining three parameters are unused.
*/op.paramTypes=TEEC_PARAM_TYPES(TEEC_VALUE_INOUT,TEEC_NONE,TEEC_NONE,TEEC_NONE);op.params[0].value.a=42;/*
* TA_HELLO_WORLD_CMD_INC_VALUE is the actual function in the TA to be
* called.
*/printf("Invoking TA to increment %d\n",op.params[0].value.a);res=TEEC_InvokeCommand(&sess,TA_HELLO_WORLD_CMD_INC_VALUE,&op,&err_origin);if(res!=TEEC_SUCCESS)errx(1,"TEEC_InvokeCommand failed with code 0x%x origin 0x%x",res,err_origin);printf("TA incremented value to %d\n",op.params[0].value.a);/*
* We're done with the TA, close the session and
* destroy the context.
*
* The TA will print "Goodbye!" in the log when the
* session is closed.
*/TEEC_CloseSession(&sess);TEEC_FinalizeContext(&ctx);return0;}
注释写的很清楚了, CA 首先要 TEEC_OpenSession 与特定的 TA 建立会话, 这里需要传入 TA 的 uuid; 然后 TEEC_InvokeCommand 调用 TA 命令, cmd 指定功能, op 传递参数.
按提示用 root 登陆, 可以看到 /root 下有一个 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta, 通过查阅 文档 可以发现, 这就是 TA 应用, 那一串东西是 uuid, 用来区分 TA 的.
引用
BINARY and LIBNAME
These are exclusive, meaning that you cannot use both at the same time. If building a TA, BINARY shall provide the TA filename used to load the TA. The built and signed TA binary file will be named ${BINARY}.ta. In native OP-TEE, it is the TA UUID, used by tee-supplicant to identify TAs.
除了 /root 下有 TA, 系统其他地方也有一些. /root 里这个就是出题人写的 TA 了.
This function-like macro builds a constant containing four Parameter types for use in the paramTypes field of a TEEC_Operation structure. It accepts four parameters which MUST be taken from the constant values described in Table 4-5.
Note that the way in which the parameter types are packed in a 32-bit integer is implementation-defined and a Client MUST use this macro to build the content of the paramTypes field. However, the value 0 MUST always be equivalent to all types set to TEEC_NONE.
The Parameter is a TEEC_TempMemoryReference describing a region of memory which needs to be temporarily registered for the duration of the Operation and is tagged as input.
TEEC_MEMREF_TEMP_OUTPUT
0x00000006
Same as TEEC_MEMREF_TEMP_INPUT, but the Memory Reference is tagged as output. The Implementation may update the size field to reflect the required output size in some use cases.
还能翻到对应的结构 TEEC_TempMemoryReference:
引用
This type defines a Temporary Memory Reference. It is used as a TEEC_Operation parameter when the corresponding parameter type is one of TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_OUTPUT, or TEEC_MEMREF_TEMP_INOUT.
#include<err.h>#include<stdio.h>#include<string.h>/* OP-TEE TEE client API (built by optee_client) */#include<tee_client_api.h>#define TA_HELLO_WORLD_UUID \
{ \
0x8aaaf200, 0x2450, 0x11e4, { \
0xab, 0xe2, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b \
} \
}
intmain(void){TEEC_Resultres;TEEC_Contextctx;TEEC_Sessionsess;TEEC_Operationop;TEEC_UUIDuuid=TA_HELLO_WORLD_UUID;uint32_terr_origin;/* Initialize a context connecting us to the TEE */res=TEEC_InitializeContext(NULL,&ctx);if(res!=TEEC_SUCCESS)errx(1,"TEEC_InitializeContext failed with code 0x%x",res);/*
* Open a session to the "hello world" TA, the TA will print "hello
* world!" in the log when the session is created.
*/res=TEEC_OpenSession(&ctx,&sess,&uuid,TEEC_LOGIN_PUBLIC,NULL,NULL,&err_origin);if(res!=TEEC_SUCCESS)errx(1,"TEEC_Opensession failed with code 0x%x origin 0x%x",res,err_origin);/*
* Execute a function in the TA by invoking it, in this case
* we're incrementing a number.
*
* The value of command ID part and how the parameters are
* interpreted is part of the interface provided by the TA.
*//* Clear the TEEC_Operation struct */memset(&op,0,sizeof(op));/*
* Prepare the argument. Pass a value in the first parameter,
* the remaining three parameters are unused.
*/op.paramTypes=TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,TEEC_MEMREF_TEMP_OUTPUT,TEEC_NONE,TEEC_NONE);charbuf0[]="AAAA";intsize0=4;charbuf1[]="BBBB";intsize1=4;op.params[0].tmpref.buffer=buf0;op.params[0].tmpref.size=size0;op.params[1].tmpref.buffer=buf1;op.params[1].tmpref.size=size1;/*
* TA_HELLO_WORLD_CMD_INC_VALUE is the actual function in the TA to be
* called.
*/res=TEEC_InvokeCommand(&sess,0,&op,&err_origin);if(res!=TEEC_SUCCESS)errx(1,"TEEC_InvokeCommand failed with code 0x%x origin 0x%x",res,err_origin);/*
* We're done with the TA, close the session and
* destroy the context.
*
* The TA will print "Goodbye!" in the log when the
* session is closed.
*/TEEC_CloseSession(&sess);TEEC_FinalizeContext(&ctx);return0;}
主要就是修改 TEEC_Operation. 这样就可以成功进入 TA 的中的功能了.
调试
Normal world 和 Secure World 都有日志, qemu 启动脚本里加上这 -serial tcp:localhost:54320 -serial tcp:localhost:54321, 然后启动前用 build/soc_term.py 监听这两个端口即可. Secure World 有个程序叫 ldelf, 他负责把 TA 装载进内存. 装载的时候有部分地址随机, 在 CA 调用 TEEC_OpenSession() 后, TEEC_InvokeCommand() 前用 getchar() 停下, 日志里会有输出装载地址, 就知道在哪下断点了.