22. OP-TEE中TA与CA执行流程-------tee-supplicant(一)

2023-12-09 20:32
文章标签 流程 执行 ca 22 op supplicant ta tee

本文主要是介绍22. OP-TEE中TA与CA执行流程-------tee-supplicant(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

   

  

tee_supplicant的主要作用是使OP-TEE能够通过tee_supplicant来访问REE端文件系统中的资源,例如加载存放在文件系统中的TA镜像到TEE中,对REE端数据库的操作,对EMMC中RPMB分区的操作,提供socket通信等。 其源代码optee_client/tee-supplicant目录中。编译之后会生成一个名字为tee_supplicant的可执行文件,该可执行文件在REE启动的时候会作为一个后台程序被自动启动,而且常驻于系统中。

1. tee_supplicant编译生成和Linux中的自启动

  tee_supplicant会在编译optee-client目标的时候被编译生成一个可执行文件,具体编译过程请查看optee-client目录中的Makefile文件结合《3.OP-TEE+qemu的编译--工程编译target依赖关系》文章就能明了该可执行文件是如何一步步被编译出来的。

  tee_supplicant可执行文件在Linux启动的时候会被作为后台程序启动。启动的动作存放在build/init.d.optee文件中,其内容如下:

#!/bin/sh
#
# /etc/init.d/optee
#
# Start/stop tee-supplicant (OP-TEE normal world daemon)
#
case "$1" instart)if [ -e /bin/tee-supplicant -a -e /dev/teepriv0 ]; thenecho "Starting tee-supplicant..."tee-supplicant&	#将tee_supplicat以后台方式启动exit 0elseecho "tee-supplicant or TEE device not found"exit 1fi;;stop)killall tee-supplicant;;status)cat /dev/teepriv0 2>&1 | grep -q "Device or resource busy" || not="not "echo "tee-supplicant is ${not}active";;
esac

  在编译的时候init.d.optee文件将会被打包到根文件系统中并以"optee“名字存放在/etc/init.d目录中。而且会被链接到/etc/rc.d/S09_optee文件。这些操作是在编译生成rootfs的时候所做的,详细情况请查看build/common.mk文件中filelist-tee-common目标的内容。系统启动tee_supplicant的过程如下图所示:

2. tee_supplicant入口函数

  tee_supplicant启动后作为Linux中的一个后台程序运行,起到处理RPC请求 service的作用。通过类似于C/S的方式为OP-TEE提供对REE端文件系统的操作。该可执行文件的源代码的入口函数存放在optee_client/tee-supplicant/src/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 {
/*打开/dev/teepriv0设备,该设备为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);}if (sql_fs_init() != 0) {EMSG("sql_fs_init() failed ");exit(EXIT_FAILURE);}/* 调用process_one_request函数接收来自TEE的请求,并加以处理 */while (!arg.abort) {if (!process_one_request(&arg))arg.abort = true;}close(arg.fd);return EXIT_FAILURE;
}

3. tee_supplicant中的loop循环

  tee_supplicant启动之后最终会进入一个loop循环,调用process_one_request函数来监控,接收,处理,回复OP-TEE的请求。整个处理过程如下图所示:



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 */
/* 组合tee_supplican等待TA请求的参数 */params = (struct tee_ioctl_param *)(&request.send + 1);params->attr = TEE_IOCTL_PARAM_ATTR_META;/* 增加当前正在等待处理的tee_supplicant的数量 */num_waiters_inc(arg);/* 通过ioctl函数,将等待请求发送到tee驱动,在tee驱动中将会block住,直到有来自TA的请求才会返回 */if (!read_request(arg->fd, &request))return false;/* 解析从TA发送的请求,分离出TA需要tee_supplicant所做的事情ID和相关参数 */if (!find_params(&request, &func, &num_params, ¶ms, &num_meta))return false;/* 创建新的线程来等待接收来自TA的请求,将等待请求的数量减一 */if (num_meta && !num_waiters_dec(arg) && !spawn_thread(arg))return false;/* 根据TA请求的ID来执行具体的handle */switch (func) {case RPC_CMD_LOAD_TA:ret = load_ta(num_params, params);	//加载在文件系统的TA镜像break;case RPC_CMD_FS:ret = tee_supp_fs_process(num_params, params);	//处理操作文件系统的请求break;case RPC_CMD_SQL_FS:ret = sql_fs_process(num_params, params);	//处理操作数据库文件的请求break;case RPC_CMD_RPMB:ret = process_rpmb(num_params, params);	//处理对EMMC中rpmb分区的操作请求break;case RPC_CMD_SHM_ALLOC:ret = process_alloc(arg->fd, num_params, params);	//处理分配共享内存的请求break;case RPC_CMD_SHM_FREE:ret = process_free(num_params, params);	//释放分配的共享内存的请求break;case RPC_CMD_GPROF:ret = gprof_process(num_params, params);	//处理gprof请求break;case OPTEE_MSG_RPC_CMD_SOCKET:ret = tee_socket_process(num_params, params);	//处理网络socket请求break;default:EMSG("Cmd [0x%" PRIx32 "] not supported", func);/* Not supported. */ret = TEEC_ERROR_NOT_SUPPORTED;break;}request.send.ret = ret;
/* 回复处理后的数据给TA */return write_response(arg->fd, &request);
}

4. 接收来自TA的请求

  tee_supplicant通过read_request来接收来自TA端的请求。该函数会block在tee驱动层面。内容如下:

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);/* 将在tee_supplicant中设定的用于存放TA请求的buffer和属性的地址作为参数,然后调用ioctl函数进入到tee驱动中等待来自TA的请求 */if (ioctl(fd, TEE_IOC_SUPPL_RECV, &data)) {EMSG("TEE_IOC_SUPPL_RECV: %s", strerror(errno));return false;}return true;
}

  在OP-TEE驱动中ioctl的TEE_IOC_SUPPL_RECV操作将会block住,直到接收到来自TA的请求。关于驱动部分将在后续章节详细介绍。

5. 解析来自TA的请求

  在tee_supplicant中,使用find_params函数来解析来自TA的请求。函数内容如下:

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 */
/* 跳过属性为TEE_IOCTL_PARAM_ATTR_META的参数 */for (n = 0; n < request->recv.num_params; n++)if (!(p[n].attr & TEE_IOCTL_PARAM_ATTR_META))break;*func = request->recv.func; //记录TA请求的操作编号*num_params = request->recv.num_params - n;	//确定TA真正的参数个数*params = p + n;	//将params指向TA发送过来的参数*num_meta = n;	//定位meta的起始位置/* Make sure that no meta parameters follows a non-meta parameter */
/* 确保剩下的参数中没有属性为TEE_IOCTL_PARAM_ATTR_META的参数 */for (; n < request->recv.num_params; n++) {if (p[n].attr & TEE_IOCTL_PARAM_ATTR_META) {EMSG("Unexpected meta parameter");return false;}}return true;
}

6. 请求的处理

  当解析玩来自TA的请求参数信息之后,在process_one_request函数中会使用switch方式,根据请求的func ID来决定具体执行什么操作,这些操作包括:1. 从文件系统中读取TA的镜像保存在共享内存中。2. 对文件系统中的节点进行读/写/打开/关闭/移除等操作。3.执行针对数据库的操作。4. 执行RPMB相关操作。5. 分配共享内存。6. 释放共享内存。7处理gprof请求。8. 执行网络socket请求。

7. 回复数据给TA

  tee_supplicant执行完具体的操作请求之后,会通过write_response函数将执行结果和数据反馈给TA。该函数内容如下:

static bool write_response(int fd, union tee_rpc_invoke *request)
{struct tee_ioctl_buf_data data;/* 将需要返回给TA的数据存放在buffer中 */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;/* 调用驱动中ioctl函数的TEE_IOC_SUPPL_SEND功能,进数据发送给TA */if (ioctl(fd, TEE_IOC_SUPPL_SEND, &data)) {EMSG("TEE_IOC_SUPPL_SEND: %s", strerror(errno));return false;}return true;
}

8. tee_supplicant中使用的结构体

  在tee_supplicant中用于接收和发送请求的的数据都存放在类型为tee_rpc_invoke的结构体变量中,该结构体内容如下:

union tee_rpc_invoke {uint64_t buf[(RPC_BUF_SIZE - 1) / sizeof(uint64_t) + 1];struct tee_iocl_supp_recv_arg recv;struct tee_iocl_supp_send_arg send;
};

RPC_BUF_SIZE的定义如下:

#define RPC_BUF_SIZE	(sizeof(struct tee_iocl_supp_send_arg) + \RPC_NUM_PARAMS * sizeof(struct tee_ioctl_param))

整个结构体中成员的排列如下图所示:


  在整个结构体中等待来自TA的请求的时候,第一部分为tee_ioctl_supp_send_arg结构体,当处理完请求之后,需要将处理后的数据发送给TA时,第一部分为tee_ioctl_supp_send_arg







 

这篇关于22. OP-TEE中TA与CA执行流程-------tee-supplicant(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/474974

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

Smarty模板执行原理

为了实现程序的业务逻辑和内容表现页面的分离从而提高开发速度,php 引入了模板引擎的概念,php 模板引擎里面最流行的可以说是smarty了,smarty因其功能强大而且速度快而被广大php web开发者所认可。本文将记录一下smarty模板引擎的工作执行原理,算是加深一下理解。 其实所有的模板引擎的工作原理是差不多的,无非就是在php程序里面用正则匹配将模板里面的标签替换为php代码从而将两者

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

Java程序到CPU上执行 的步骤

相信很多的小伙伴在最初学习编程的时候会容易产生一个疑惑❓,那就是编写的Java代码究竟是怎么一步一步到CPU上去执行的呢?CPU又是如何执行的呢?今天跟随小编的脚步去化解开这个疑惑❓。 在学习这个过程之前,我们需要先讲解一些与本内容相关的知识点 指令 指令是指导CPU运行的命令,主要由操作码+被操作数组成。 其中操作码用来表示要做什么动作,被操作数是本条指令要操作的数据,可能是内存地址,也