tee-supplicant 是运行在user space,主要是optee_linuxdriver通过ioctl告诉上层,让其访问rich os文件系统中的资源。从其makefile中可以看到最终build出的可执行档就是tee-supplicant
包换的源文件:tee_supplicant.c/teec_ta_load.c/tee_supp_fs.c/rpmb.c/handle.c
all: tee-supplicant
################################################################################
# Teec configuration
################################################################################
PACKAGE_NAME := tee-supplicant
TEES_SRCS := tee_supplicant.c \
teec_ta_load.c \
tee_supp_fs.c \
rpmb.c \
handle.c
其入口函数在tee_supplicant.c 中
int main(int argc, char *argv[])
{
struct thread_arg arg = { .fd = -1 };
int e;
e = pthread_mutex_init(&arg.mutex, NULL);
if (e) {
EMSG("pthread_mutex_init: %s", strerror(e));
EMSG("terminating...");
exit(EXIT_FAILURE);
}
if (argc > 2)
return usage();
if (argc == 2) {
arg.fd = open_dev(argv[1]);
if (arg.fd < 0) {
EMSG("failed to open \"%s\"", argv[1]);
exit(EXIT_FAILURE);
}
} else {
//打开tee 驱动对应的字符设备
arg.fd = get_dev_fd();
if (arg.fd < 0) {
EMSG("failed to find an OP-TEE supplicant device");
exit(EXIT_FAILURE);
}
}
if (tee_supp_fs_init() != 0) {
EMSG("error tee_supp_fs_init");
exit(EXIT_FAILURE);
}
//创建一个thread出来TA的请求
while (!arg.abort) {
if (!process_one_request(&arg))
arg.abort = true;
}
close(arg.fd);
return EXIT_FAILURE;
}
在main函数中最终要的就是process_one_request
static bool process_one_request(struct thread_arg *arg)
{
union tee_rpc_invoke request;
size_t num_params;
size_t num_meta;
struct tee_ioctl_param *params;
uint32_t func;
uint32_t ret;
DMSG("looping");
memset(&request, 0, sizeof(request));
request.recv.num_params = RPC_NUM_PARAMS;
/* Let it be known that we can deal with meta parameters */
params = (struct tee_ioctl_param *)(&request.send + 1);
params->attr = TEE_IOCTL_PARAM_ATTR_META;
num_waiters_inc(arg);
//分析TA 发送过来的请求
if (!read_request(arg->fd, &request))
return false;
//解析TA的请求
if (!find_params(&request, &func, &num_params, ¶ms, &num_meta))
return false;
//调用spawn_thread 创建一个新的thread,这个thread的处理函数thread_main。
if (num_meta && !num_waiters_dec(arg) && !spawn_thread(arg))
return false;
根据解析TA的命令执行不同的操作。假如是OPTEE_MSG_RPC_CMD_LOAD_TA 命令的话,则执行load_ta
switch (func) {
case OPTEE_MSG_RPC_CMD_LOAD_TA:
ret = load_ta(num_params, params);
break;
}
request.send.ret = ret;
//回复处理后的数据给TA
return write_response(arg->fd, &request);
}
有几个重点函数read_request/find_params/spawn_thread/load_ta/write_response
先看read_request
static bool read_request(int fd, union tee_rpc_invoke *request)
{
struct tee_ioctl_buf_data data;
data.buf_ptr = (uintptr_t)request;
data.buf_len = sizeof(*request);
if (ioctl(fd, TEE_IOC_SUPPL_RECV, &data)) {
EMSG("TEE_IOC_SUPPL_RECV: %s", strerror(errno));
return false;
}
return true;
}
原来就是通过ioctl 从optee_linuxdriver 得到需求
static bool find_params(union tee_rpc_invoke *request, uint32_t *func,
size_t *num_params, struct tee_ioctl_param **params,
size_t *num_meta)
{
struct tee_ioctl_param *p;
size_t n;
p = (struct tee_ioctl_param *)(&request->recv + 1);
/* Skip meta parameters in the front */
for (n = 0; n < request->recv.num_params; n++)
if (!(p[n].attr & TEE_IOCTL_PARAM_ATTR_META))
break;
//最重要的参数,tee_supplicant.c 中的process_one_request会根据func的值,执行不同的命令.
*func = request->recv.func;
*num_params = request->recv.num_params - n;
*params = p + n;
*num_meta = n;
/* Make sure that no meta parameters follows a non-meta parameter */
for (; n < request->recv.num_params; n++) {
if (p[n].attr & TEE_IOCTL_PARAM_ATTR_META) {
EMSG("Unexpected meta parameter");
return false;
}
}
return true;
}
在find_params 中会解析通过ioctl TEE_IOC_SUPPL_RECV得到的命令
static bool spawn_thread(struct thread_arg *arg)
{
pthread_t tid;
int e;
DMSG("Spawning a new thread");
/*
* Increase number of waiters now to avoid starting another thread
* before this thread has been scheduled.
*/
num_waiters_inc(arg);
e = pthread_create(&tid, NULL, thread_main, arg);
if (e) {
EMSG("pthread_create: %s", strerror(e));
num_waiters_dec(arg);
return false;
}
e = pthread_detach(tid);
if (e)
EMSG("pthread_detach: %s", strerror(e));
return true;
}
spawn_thread 调用pthread_create 新建一个thread,其处理函数是thread_main
static void *thread_main(void *a)
{
struct thread_arg *arg = a;
/*
* Now that this thread has been scheduled, compensate for the
* initial increase in spawn_thread() before.
*/
num_waiters_dec(arg);
while (!arg->abort) {
if (!process_one_request(arg))
arg->abort = true;
}
return NULL;
}
在thread_main 中通过是调用process_one_request ,这个函数也就是我们正在分析的
static uint32_t load_ta(size_t num_params, struct tee_ioctl_param *params)
{
int ta_found = 0;
size_t size = 0;
TEEC_UUID uuid;
struct tee_ioctl_param_value *val_cmd;
TEEC_SharedMemory shm_ta;
memset(&shm_ta, 0, sizeof(shm_ta));
if (num_params != 2 || get_value(num_params, params, 0, &val_cmd) ||
get_param(num_params, params, 1, &shm_ta))
return TEEC_ERROR_BAD_PARAMETERS;
uuid_from_octets(&uuid, (void *)val_cmd);
size = shm_ta.size;
ta_found = TEECI_LoadSecureModule(ta_dir, &uuid, shm_ta.buffer, &size);
if (ta_found != TA_BINARY_FOUND) {
EMSG(" TA not found");
return TEEC_ERROR_ITEM_NOT_FOUND;
}
params[1].u.memref.size = size;
return TEEC_SUCCESS;
}
在load_ta 中通过TEECI_LoadSecureModule 开加载ta的binary
{
#ifdef TEEC_TEST_LOAD_PATH
int res;
res = try_load_secure_module(TEEC_TEST_LOAD_PATH,
dev_path, destination, ta, ta_size);
if (res != TA_BINARY_NOT_FOUND)
return res;
#endif
return try_load_secure_module(TEEC_LOAD_PATH,
dev_path, destination, ta, ta_size);
}
正式版本不会定义TEEC_TEST_LOAD_PATH,因此调用try_load_secure_module
static int try_load_secure_module(const char* prefix,
const char* dev_path,
const TEEC_UUID *destination, void *ta,
size_t *ta_size)
{
char fname[PATH_MAX];
FILE *file = NULL;
bool first_try = true;
size_t s;
int n;
if (!ta_size || !destination) {
printf("wrong inparameter to TEECI_LoadSecureModule\n");
return TA_BINARY_NOT_FOUND;
}
/*
* We expect the TA binary to be named after the UUID as per RFC4122,
* that is: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.ta
* If the file cannot be open, try the deprecated format:
* xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.ta
*/
again:
n = snprintf(fname, PATH_MAX,
"%s/%s/%08x-%04x-%04x-%02x%02x%s%02x%02x%02x%02x%02x%02x.ta",
prefix, dev_path,
destination->timeLow,
destination->timeMid,
destination->timeHiAndVersion,
destination->clockSeqAndNode[0],
destination->clockSeqAndNode[1],
first_try ? "-" : "",
destination->clockSeqAndNode[2],
destination->clockSeqAndNode[3],
destination->clockSeqAndNode[4],
destination->clockSeqAndNode[5],
destination->clockSeqAndNode[6],
destination->clockSeqAndNode[7]);
DMSG("Attempt to load %s", fname);
if ((n < 0) || (n >= PATH_MAX)) {
EMSG("wrong TA path [%s]", fname);
return TA_BINARY_NOT_FOUND;
}
file = fopen(fname, "r");
if (file == NULL) {
DMSG("failed to open the ta %s TA-file", fname);
if (first_try) {
first_try = false;
goto again;
}
return TA_BINARY_NOT_FOUND;
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return TA_BINARY_NOT_FOUND;
}
s = ftell(file);
if (s > *ta_size || !ta) {
/*
* Buffer isn't large enough, return the required size to
* let the caller increase the size of the buffer and try
* again.
*/
goto out;
}
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return TA_BINARY_NOT_FOUND;
}
if (s != fread(ta, 1, s, file)) {
printf("error fread TA file\n");
fclose(file);
return TA_BINARY_NOT_FOUND;
}
out:
*ta_size = s;
fclose(file);
return TA_BINARY_FOUND;
}
try_load_secure_module 也就是直接调用fread将binary读到内存中.
最后通过TEE_IOC_SUPPL_SEND 告诉optee_linuxdriver处理的结果
static bool write_response(int fd, union tee_rpc_invoke *request)
{
struct tee_ioctl_buf_data data;
data.buf_ptr = (uintptr_t)&request->send;
data.buf_len = sizeof(struct tee_iocl_supp_send_arg) +
sizeof(struct tee_ioctl_param) *
request->send.num_params;
if (ioctl(fd, TEE_IOC_SUPPL_SEND, &data)) {
EMSG("TEE_IOC_SUPPL_SEND: %s", strerror(errno));
return false;
}
return true;
}
包换的源文件:tee_supplicant.c/teec_ta_load.c/tee_supp_fs.c/rpmb.c/handle.c
all: tee-supplicant
################################################################################
# Teec configuration
################################################################################
PACKAGE_NAME := tee-supplicant
TEES_SRCS := tee_supplicant.c \
teec_ta_load.c \
tee_supp_fs.c \
rpmb.c \
handle.c
其入口函数在tee_supplicant.c 中
int main(int argc, char *argv[])
{
struct thread_arg arg = { .fd = -1 };
int e;
e = pthread_mutex_init(&arg.mutex, NULL);
if (e) {
EMSG("pthread_mutex_init: %s", strerror(e));
EMSG("terminating...");
exit(EXIT_FAILURE);
}
if (argc > 2)
return usage();
if (argc == 2) {
arg.fd = open_dev(argv[1]);
if (arg.fd < 0) {
EMSG("failed to open \"%s\"", argv[1]);
exit(EXIT_FAILURE);
}
} else {
//打开tee 驱动对应的字符设备
arg.fd = get_dev_fd();
if (arg.fd < 0) {
EMSG("failed to find an OP-TEE supplicant device");
exit(EXIT_FAILURE);
}
}
if (tee_supp_fs_init() != 0) {
EMSG("error tee_supp_fs_init");
exit(EXIT_FAILURE);
}
//创建一个thread出来TA的请求
while (!arg.abort) {
if (!process_one_request(&arg))
arg.abort = true;
}
close(arg.fd);
return EXIT_FAILURE;
}
在main函数中最终要的就是process_one_request
static bool process_one_request(struct thread_arg *arg)
{
union tee_rpc_invoke request;
size_t num_params;
size_t num_meta;
struct tee_ioctl_param *params;
uint32_t func;
uint32_t ret;
DMSG("looping");
memset(&request, 0, sizeof(request));
request.recv.num_params = RPC_NUM_PARAMS;
/* Let it be known that we can deal with meta parameters */
params = (struct tee_ioctl_param *)(&request.send + 1);
params->attr = TEE_IOCTL_PARAM_ATTR_META;
num_waiters_inc(arg);
//分析TA 发送过来的请求
if (!read_request(arg->fd, &request))
return false;
//解析TA的请求
if (!find_params(&request, &func, &num_params, ¶ms, &num_meta))
return false;
//调用spawn_thread 创建一个新的thread,这个thread的处理函数thread_main。
if (num_meta && !num_waiters_dec(arg) && !spawn_thread(arg))
return false;
根据解析TA的命令执行不同的操作。假如是OPTEE_MSG_RPC_CMD_LOAD_TA 命令的话,则执行load_ta
switch (func) {
case OPTEE_MSG_RPC_CMD_LOAD_TA:
ret = load_ta(num_params, params);
break;
}
request.send.ret = ret;
//回复处理后的数据给TA
return write_response(arg->fd, &request);
}
有几个重点函数read_request/find_params/spawn_thread/load_ta/write_response
先看read_request
static bool read_request(int fd, union tee_rpc_invoke *request)
{
struct tee_ioctl_buf_data data;
data.buf_ptr = (uintptr_t)request;
data.buf_len = sizeof(*request);
if (ioctl(fd, TEE_IOC_SUPPL_RECV, &data)) {
EMSG("TEE_IOC_SUPPL_RECV: %s", strerror(errno));
return false;
}
return true;
}
原来就是通过ioctl 从optee_linuxdriver 得到需求
static bool find_params(union tee_rpc_invoke *request, uint32_t *func,
size_t *num_params, struct tee_ioctl_param **params,
size_t *num_meta)
{
struct tee_ioctl_param *p;
size_t n;
p = (struct tee_ioctl_param *)(&request->recv + 1);
/* Skip meta parameters in the front */
for (n = 0; n < request->recv.num_params; n++)
if (!(p[n].attr & TEE_IOCTL_PARAM_ATTR_META))
break;
//最重要的参数,tee_supplicant.c 中的process_one_request会根据func的值,执行不同的命令.
*func = request->recv.func;
*num_params = request->recv.num_params - n;
*params = p + n;
*num_meta = n;
/* Make sure that no meta parameters follows a non-meta parameter */
for (; n < request->recv.num_params; n++) {
if (p[n].attr & TEE_IOCTL_PARAM_ATTR_META) {
EMSG("Unexpected meta parameter");
return false;
}
}
return true;
}
在find_params 中会解析通过ioctl TEE_IOC_SUPPL_RECV得到的命令
static bool spawn_thread(struct thread_arg *arg)
{
pthread_t tid;
int e;
DMSG("Spawning a new thread");
/*
* Increase number of waiters now to avoid starting another thread
* before this thread has been scheduled.
*/
num_waiters_inc(arg);
e = pthread_create(&tid, NULL, thread_main, arg);
if (e) {
EMSG("pthread_create: %s", strerror(e));
num_waiters_dec(arg);
return false;
}
e = pthread_detach(tid);
if (e)
EMSG("pthread_detach: %s", strerror(e));
return true;
}
spawn_thread 调用pthread_create 新建一个thread,其处理函数是thread_main
static void *thread_main(void *a)
{
struct thread_arg *arg = a;
/*
* Now that this thread has been scheduled, compensate for the
* initial increase in spawn_thread() before.
*/
num_waiters_dec(arg);
while (!arg->abort) {
if (!process_one_request(arg))
arg->abort = true;
}
return NULL;
}
在thread_main 中通过是调用process_one_request ,这个函数也就是我们正在分析的
static uint32_t load_ta(size_t num_params, struct tee_ioctl_param *params)
{
int ta_found = 0;
size_t size = 0;
TEEC_UUID uuid;
struct tee_ioctl_param_value *val_cmd;
TEEC_SharedMemory shm_ta;
memset(&shm_ta, 0, sizeof(shm_ta));
if (num_params != 2 || get_value(num_params, params, 0, &val_cmd) ||
get_param(num_params, params, 1, &shm_ta))
return TEEC_ERROR_BAD_PARAMETERS;
uuid_from_octets(&uuid, (void *)val_cmd);
size = shm_ta.size;
ta_found = TEECI_LoadSecureModule(ta_dir, &uuid, shm_ta.buffer, &size);
if (ta_found != TA_BINARY_FOUND) {
EMSG(" TA not found");
return TEEC_ERROR_ITEM_NOT_FOUND;
}
params[1].u.memref.size = size;
return TEEC_SUCCESS;
}
在load_ta 中通过TEECI_LoadSecureModule 开加载ta的binary
{
#ifdef TEEC_TEST_LOAD_PATH
int res;
res = try_load_secure_module(TEEC_TEST_LOAD_PATH,
dev_path, destination, ta, ta_size);
if (res != TA_BINARY_NOT_FOUND)
return res;
#endif
return try_load_secure_module(TEEC_LOAD_PATH,
dev_path, destination, ta, ta_size);
}
正式版本不会定义TEEC_TEST_LOAD_PATH,因此调用try_load_secure_module
static int try_load_secure_module(const char* prefix,
const char* dev_path,
const TEEC_UUID *destination, void *ta,
size_t *ta_size)
{
char fname[PATH_MAX];
FILE *file = NULL;
bool first_try = true;
size_t s;
int n;
if (!ta_size || !destination) {
printf("wrong inparameter to TEECI_LoadSecureModule\n");
return TA_BINARY_NOT_FOUND;
}
/*
* We expect the TA binary to be named after the UUID as per RFC4122,
* that is: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.ta
* If the file cannot be open, try the deprecated format:
* xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.ta
*/
again:
n = snprintf(fname, PATH_MAX,
"%s/%s/%08x-%04x-%04x-%02x%02x%s%02x%02x%02x%02x%02x%02x.ta",
prefix, dev_path,
destination->timeLow,
destination->timeMid,
destination->timeHiAndVersion,
destination->clockSeqAndNode[0],
destination->clockSeqAndNode[1],
first_try ? "-" : "",
destination->clockSeqAndNode[2],
destination->clockSeqAndNode[3],
destination->clockSeqAndNode[4],
destination->clockSeqAndNode[5],
destination->clockSeqAndNode[6],
destination->clockSeqAndNode[7]);
DMSG("Attempt to load %s", fname);
if ((n < 0) || (n >= PATH_MAX)) {
EMSG("wrong TA path [%s]", fname);
return TA_BINARY_NOT_FOUND;
}
file = fopen(fname, "r");
if (file == NULL) {
DMSG("failed to open the ta %s TA-file", fname);
if (first_try) {
first_try = false;
goto again;
}
return TA_BINARY_NOT_FOUND;
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return TA_BINARY_NOT_FOUND;
}
s = ftell(file);
if (s > *ta_size || !ta) {
/*
* Buffer isn't large enough, return the required size to
* let the caller increase the size of the buffer and try
* again.
*/
goto out;
}
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return TA_BINARY_NOT_FOUND;
}
if (s != fread(ta, 1, s, file)) {
printf("error fread TA file\n");
fclose(file);
return TA_BINARY_NOT_FOUND;
}
out:
*ta_size = s;
fclose(file);
return TA_BINARY_FOUND;
}
try_load_secure_module 也就是直接调用fread将binary读到内存中.
最后通过TEE_IOC_SUPPL_SEND 告诉optee_linuxdriver处理的结果
static bool write_response(int fd, union tee_rpc_invoke *request)
{
struct tee_ioctl_buf_data data;
data.buf_ptr = (uintptr_t)&request->send;
data.buf_len = sizeof(struct tee_iocl_supp_send_arg) +
sizeof(struct tee_ioctl_param) *
request->send.num_params;
if (ioctl(fd, TEE_IOC_SUPPL_SEND, &data)) {
EMSG("TEE_IOC_SUPPL_SEND: %s", strerror(errno));
return false;
}
return true;
}