Post

Plugin of TEE-Supplicant

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里。主要包含两个函数:

  1. plugin_load_all
      该函数从文件系统指定目录中(默认路径在/usr/lib/tee-supplicant/plugins/)把所有的plugin.so(命名为UUID)都用dl load进来并在一个链表里以供查找。如果该plugin有init函数,就调用该函数做初始化。

  2. plugin_process
      当TEE-Supplicant收到由kernel转发过来的plugin RPC请求,根据UUID找到plugin,并调用invoke函数。

Implementation of Custom Command

  如上所说,plugin其实是一个动态链接库,由TEE-supplicant在启动时调用plugin_load_all函数load进来。它的开发要遵循一定的规则。

  1. 分配一个UUID给该plugin,生成的.so要以UUID命名
  2. 申明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);
    };
    
  3. 实现init函数(如果需要的话)
  4. 实现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

This post is licensed under CC BY 4.0 by the author.