Plugin of TEE-Supplicant
Preface
OPTEE(TEE OS)在某种程度上可以认为是REE OS在TEE环境下的投影,REE OS来统筹安排non-secure resources,而OPTEE来管理secure-resources。当然TEE OS有权限去access non-secure的resources,但两个OS之间如果没有一些同步机制(hardware or software),都去访问同一个资源,必然会产生问题。另外,秉持TEE只提供security相关的service,代码越少,提供的服务越少,则安全性越好,如果可以在REE world做的,那就应该在REE端做,在硬件设计的时候也要呼应这一需求,毕竟软件是run在硬件基础上的。为了调用REE端的service,OPTEE提供了rpc机制从TEE回到REE side,然后由Linux Kernel或者TEE-supplicant来完成相应的回调需求。TA通过调用GPD internal APIs,然后rpc回到REE完成回调。Kernel和TEE-supplicant提供了一些common的RPC Command,如果TEE端需要的功能不在列表里,应该怎么办呢?这里就要用到plugin了。
Main Implementation
plugin功能实现比较简单,主要有以下几个点。
System PTA
与TA调用GPD internal API(实际上是syscall)不同的是,TA调用plugin function要通过system pta的PTA_SYSTEM_SUPP_PLUGIN_INVOKE command。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static TEE_Result system_supp_plugin_invoke(uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_MEMREF_INOUT,
TEE_PARAM_TYPE_VALUE_OUTPUT);
TEE_Result res = TEE_ERROR_GENERIC;
size_t outlen = 0;
TEE_UUID uuid = { };
if (exp_pt != param_types)
return TEE_ERROR_BAD_PARAMETERS;
if (!params[0].memref.buffer || params[0].memref.size != sizeof(uuid))
return TEE_ERROR_BAD_PARAMETERS;
res = copy_from_user(&uuid, params[0].memref.buffer, sizeof(uuid));
if (res)
return res;
res = tee_invoke_supp_plugin_rpc(&uuid,
params[1].value.a, /* cmd */
params[1].value.b, /* sub_cmd */
NULL,
params[2].memref.buffer, /* data */
params[2].memref.size, /* in len */
&outlen);
params[3].value.a = (uint32_t)outlen;
return res;
}
system PTA则调用tee_invoke_supp_plugin_rpc返回REE。实际上最终还是OPTEE的RPC机制,使用的RPC command为OPTEE_RPC_CMD_SUPP_PLUGIN。最终转发给TEE-Supplicant的Plugin模块处理。
另外system PTA由宏CFG_SYSTEM_PTA控制是否enable,一般是默认打开的。
Plugin Module of TEE-supplicant
Plugin Module也是由一个宏CFG_TEE_SUPP_PLUGINS控制,默认也是打开的。主要实现在plugin.c里。主要包含两个函数:
plugin_load_all
该函数从文件系统指定目录中(默认路径在/usr/lib/tee-supplicant/plugins/)把所有的plugin.so(命名为UUID)都用dl load进来并在一个链表里以供查找。如果该plugin有init函数,就调用该函数做初始化。plugin_process
当TEE-Supplicant收到由kernel转发过来的plugin RPC请求,根据UUID找到plugin,并调用invoke函数。
Implementation of Custom Command
如上所说,plugin其实是一个动态链接库,由TEE-supplicant在启动时调用plugin_load_all函数load进来。它的开发要遵循一定的规则。
- 分配一个UUID给该plugin,生成的.so要以UUID命名
- 申明struct plugin_method。该结构定义如下:
1 2 3 4 5 6 7
struct plugin_method { const char *name; /* short friendly name of the plugin */ TEEC_UUID uuid; TEEC_Result (*init)(void); TEEC_Result (*invoke)(unsigned int cmd, unsigned int sub_cmd, void *data, size_t in_len, size_t *out_len); };
- 实现init函数(如果需要的话)
实现invoke函数(必须实现)
invoke函数解析cmd和sub_cmd,并实现相应的处理函数。
How to Use Plugin
Plugin主要用于TA,OPTEE为此在tee_internal_api_extensions.h实现了以下函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*
* tee_invoke_supp_plugin() - invoke a tee-supplicant's plugin
* @uuid: uuid of the plugin
* @cmd: command for the plugin
* @sub_cmd: subcommand for the plugin
* @buf: data [for/from] the plugin [in/out]
* @len: length of the input buf
* @outlen: pointer to length of the output data (if they will be used)
*
* Return TEE_SUCCESS on success or TEE_ERRROR_* on failure.
*/
TEE_Result tee_invoke_supp_plugin(const TEE_UUID *uuid, uint32_t cmd,
uint32_t sub_cmd, void *buf, size_t len,
size_t *outlen);
该函数被link进libutee中,TA可以直接调用。如前所述,这里并没有用syscall的方式,而是通过TEE_OpenTAsession和TEE_InvokeTACommand调用system PTA来完成。具体参看lib/libutee/tee_system_pta.c的实现。
Reference
Loadable plugins framework
OPTEE Example - Plugin
Loadable plugins in tee-supplicant