深入解析OP-TEE的libteec核心API实现

在 OP-TEE 体系中,libteec是运行在 REE 用户态的客户端库。它完整实现了 GlobalPlatform (GP) 标准的 TEE Client API。libteec 的核心文件是 src/tee_client_api.c。它的本质工作非常纯粹:将上层面向对象的 GP API 转换为对 Linux 内核 TEE 驱动(/dev/tee0)的 ioctl 级联调用
以下梳理其最核心的三个 API 的代码实现骨架、关键数据结构及参数序列化逻辑。

一、 TEEC_InitializeContext:初始化上下文与建立通道

该函数负责打开 TEE 驱动的字符设备文件,并获取当前 TEE 的基本版本信息与软硬件能力(Capabilities)。C
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)
{
	char devname[PATH_MAX] = { 0 };
	int fd = 0;
	size_t n = 0;

	if (!ctx)
		return TEEC_ERROR_BAD_PARAMETERS;

	for (n = 0; n < TEEC_MAX_DEV_SEQ; n++) {
		uint32_t gen_caps = 0;

		snprintf(devname, sizeof(devname), "/dev/tee%zu", n);
		fd = teec_open_dev(devname, name, &gen_caps);
		if (fd >= 0) {
			ctx->imp.fd = fd;
			ctx->imp.reg_mem = gen_caps & TEE_GEN_CAP_REG_MEM;
			ctx->imp.memref_null = gen_caps & TEE_GEN_CAP_MEMREF_NULL;
			return TEEC_SUCCESS;
		}
	}

	return TEEC_ERROR_ITEM_NOT_FOUND;
}

二、 TEEC_OpenSession:开启与指定 TA 的安全会话

在调用具体的 TA 业务前,需要通过 UUID 寻址来拉起目标 TA。此时,libteec 会把 GP 的 TEEC_Operation 结构参数打包转换为内核识别的 tee_ioctl_paramC
TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session,
			const TEEC_UUID *destination,
			uint32_t connection_method, const void *connection_data,
			TEEC_Operation *operation, uint32_t *ret_origin)
{
	struct tee_ioctl_open_session_arg *arg = NULL;
	struct tee_ioctl_param *params = NULL;
	TEEC_Result res = TEEC_ERROR_GENERIC;
	uint32_t eorig = 0;
	int rc = 0;
	const size_t arg_size = sizeof(struct tee_ioctl_open_session_arg) +
				TEEC_CONFIG_PAYLOAD_REF_COUNT *
					sizeof(struct tee_ioctl_param);
	union {
		struct tee_ioctl_open_session_arg arg;
		uint8_t data[arg_size];
	} buf;
	struct tee_ioctl_buf_data buf_data;
	TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];

	memset(&buf, 0, sizeof(buf));
	memset(&shm, 0, sizeof(shm));
	memset(&buf_data, 0, sizeof(buf_data));

	if (!ctx || !session) {
		eorig = TEEC_ORIGIN_API;
		res = TEEC_ERROR_BAD_PARAMETERS;
		goto out;
	}

	buf_data.buf_ptr = (uintptr_t)&buf;
	buf_data.buf_len = sizeof(buf);

	arg = &buf.arg;
	arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT;
	params = (struct tee_ioctl_param *)(arg + 1);

	uuid_to_octets(arg->uuid, destination);

	setup_client_data(arg, connection_method, connection_data);

	res = teec_pre_process_operation(ctx, operation, params, shm);
	if (res != TEEC_SUCCESS) {
		eorig = TEEC_ORIGIN_API;
		goto out_free_temp_refs;
	}

	rc = ioctl(ctx->imp.fd, TEE_IOC_OPEN_SESSION, &buf_data);
	if (rc) {
		EMSG("TEE_IOC_OPEN_SESSION failed");
		eorig = TEEC_ORIGIN_COMMS;
		res = ioctl_errno_to_res(errno);
		goto out_free_temp_refs;
	}
	res = arg->ret;
	eorig = arg->ret_origin;
	if (res == TEEC_SUCCESS) {
		session->imp.ctx = ctx;
		session->imp.session_id = arg->session;
	}
	teec_post_process_operation(operation, params, shm);

out_free_temp_refs:
	teec_free_temp_refs(operation, shm);
out:
	if (ret_origin)
		*ret_origin = eorig;
	return res;
}

三、 TEEC_InvokeCommand:核心业务指令的下发与数据交互

这是日常业务中最频繁调用的 API。由于它专注于执行指令,不需要像 OpenSession 那样搬运 UUID,所以它的逻辑更轻量,直奔 TEE_IOC_INVOKEC

TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, uint32_t cmd_id,
			TEEC_Operation *operation, uint32_t *error_origin)
{
	struct tee_ioctl_invoke_arg *arg = NULL;
	struct tee_ioctl_param *params = NULL;
	TEEC_Result res = TEEC_ERROR_GENERIC;
	uint32_t eorig = 0;
	int rc = 0;
        /* 1. 装填 Session ID 与要执行的命令号 */
	const size_t arg_size = sizeof(struct tee_ioctl_invoke_arg) +
				TEEC_CONFIG_PAYLOAD_REF_COUNT *
					sizeof(struct tee_ioctl_param);
	union {
		struct tee_ioctl_invoke_arg arg;
		uint8_t data[arg_size];
	} buf;
	struct tee_ioctl_buf_data buf_data;
	TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];

	memset(&buf, 0, sizeof(buf));
	memset(&buf_data, 0, sizeof(buf_data));
	memset(&shm, 0, sizeof(shm));

	if (!session) {
		eorig = TEEC_ORIGIN_API;
		res = TEEC_ERROR_BAD_PARAMETERS;
		goto out;
	}

	buf_data.buf_ptr = (uintptr_t)&buf;
	buf_data.buf_len = sizeof(buf);

	arg = &buf.arg;
	arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT;
	params = (struct tee_ioctl_param *)(arg + 1);

	arg->session = session->imp.session_id;
	arg->func = cmd_id;

	if (operation) {
		teec_mutex_lock(&teec_mutex);
		operation->imp.session = session;
		teec_mutex_unlock(&teec_mutex);
	}
        /* 2. 序列化参数 (TEEC_Operation -> tee_ioctl_param) */
	res = teec_pre_process_operation(session->imp.ctx, operation, params, shm);
	if (res != TEEC_SUCCESS) {
		eorig = TEEC_ORIGIN_API;
		goto out_free_temp_refs;
	}
       /* 3. 发送关键 ioctl 陷落至内核,阻塞等待 Secure 世界(TA)处理完毕返回 */
	rc = ioctl(session->imp.ctx->imp.fd, TEE_IOC_INVOKE, &buf_data);
	if (rc) {
		EMSG("TEE_IOC_INVOKE failed");
		eorig = TEEC_ORIGIN_COMMS;
		res = ioctl_errno_to_res(errno);
		goto out_free_temp_refs;
	}

	res = arg->ret;
	eorig = arg->ret_origin;
	teec_post_process_operation(operation, params, shm);
        /* 4. 反序列化与收尾:将 TA 修改后的 Value 或 Memref 刷新回用户态 CA 内存空间 */
out_free_temp_refs:
	teec_free_temp_refs(operation, shm);
out:
	if (error_origin)
		*error_origin = eorig;
	return res;
}

四、 核心桥梁:参数的预处理与内存共享机制

深入细读 libteec,最复杂的逻辑其实隐藏在 teec_pre_process_operation 内部。GP 标准规定每次调用最多传递 4 个参数(TEEC_Parameter params[4]),类型可以是 VALUE(纯数值)或 MEMREF(内存引用)。
static TEEC_Result teec_pre_process_operation(TEEC_Context *ctx,
			TEEC_Operation *operation,
			struct tee_ioctl_param *params,
			TEEC_SharedMemory *shms)
{
	TEEC_Result res = TEEC_ERROR_GENERIC;
	size_t n = 0;

	 memset(shms, 0, sizeof(TEEC_SharedMemory) *
			TEEC_CONFIG_PAYLOAD_REF_COUNT);

	 for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++)
		shms[n].imp.id = -1;

	 if (!operation) {
		memset(params, 0, sizeof(struct tee_ioctl_param) *
				  TEEC_CONFIG_PAYLOAD_REF_COUNT);
		return TEEC_SUCCESS;
	 }

	 for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) {
		uint32_t param_type = 0;

		param_type = TEEC_PARAM_TYPE_GET(operation->paramTypes, n);
		switch (param_type) {
		case TEEC_NONE:
			params[n].attr = param_type;
			break;
		case TEEC_VALUE_INPUT:
		case TEEC_VALUE_OUTPUT:
		case TEEC_VALUE_INOUT:
			params[n].attr = param_type;
			params[n].a = operation->params[n].value.a;
			params[n].b = operation->params[n].value.b;
			break;
		case TEEC_MEMREF_TEMP_INPUT:
		case TEEC_MEMREF_TEMP_OUTPUT:
		case TEEC_MEMREF_TEMP_INOUT:
			res = teec_pre_process_tmpref(ctx, param_type,
				&operation->params[n].tmpref, params + n,
				shms + n);
			if (res != TEEC_SUCCESS)
				return res;
			break;
		case TEEC_MEMREF_WHOLE:
			res = teec_pre_process_whole(
					&operation->params[n].memref,
					params + n);
			if (res != TEEC_SUCCESS)
				return res;
			break;
		case TEEC_MEMREF_PARTIAL_INPUT:
		case TEEC_MEMREF_PARTIAL_OUTPUT:
		case TEEC_MEMREF_PARTIAL_INOUT:
			res = teec_pre_process_partial(param_type,
				&operation->params[n].memref, params + n);
			if (res != TEEC_SUCCESS)
				return res;
			break;
		default:
			return TEEC_ERROR_BAD_PARAMETERS;
		}
 	}

	    return TEEC_SUCCESS;
}

libteec 中,它们会映射到内核驱动的 struct tee_ioctl_param,底层转换逻辑如下:

1. 值的映射 (TEEC_VALUE_INPUT / OUTPUT / INOUT)

最直观,不涉及复杂的内存映射,直接做拷贝。
  • params[n].a = operation->params[n].value.a;
  • params[n].b = operation->params[n].value.b;

2. 内存引用的映射 (TEEC_MEMREF_TEMP_* / TEEC_REGISTERED_MEM_*)

这是性能与安全的焦点。如果 CA 传入的是一块普通的 Buffer 内存(TEEC_MEMREF_TEMP_INPUT),由于非安全世界用户态的虚拟地址(VA)安全世界根本无法直接访问,libteec 会执行以下高开销操作:
  1. 申请共享内存:通过 ioctl(fd, TEE_IOC_SHM_ALLOC, &shm_alloc_arg) 向内核 TEE 驱动申请一段专用的、连通非安全与安全世界的物理连续/非连续共享内存(Shared Memory)。
  2. 内容拷贝:如果涉及输入(Input),使用 memcpy 将用户态临时 Buffer 的内容拷贝到这段共享内存中。
  3. 记录标识:将分配到的 shm_num(共享内存 ID)和 offset 填入 tee_ioctl_param.ctee_ioctl_param.a 中。
  4. 回写与销毁:在 teec_post_process_operation 中,如果是 Output,再把数据从共享内存拷回给 CA 的临时 Buffer,并调用 TEE_IOC_SHM_FREE 释放共享内存。
内容概要:本文围绕可变桨叶四旋翼无人机的规范控制与点对点运动模拟展开,重点研究优化推力分配策略在翻转动作中的应用与性能比较。通过Matlab代码实现,构建了四旋翼动力学模型,并设计了多种控制算法以实现精确的姿态调整与轨迹跟踪。研究对比了不同推力分配方案在执行高机动性翻转动作时的稳定性、能耗效率与响应速度,旨在提升无人机在复杂飞行任务中的动态性能与控制精度。该仿真研究为无人机飞控系统的设计与优化提供了理论依据和技术支持。; 适合人群:具备一定自动控制理论基础和Matlab编程能力,从事无人机控制、飞行器动力学或机器人系统研究的科研人员及研究生。; 使用场景及目标:① 实现四旋翼无人机在三维空间中的精确点对点运动控制;② 对比分析不同推力分配策略在执行翻转等高难度动作时的控制效果与能耗表现,优化飞行性能;③ 为无人机自主飞行、特技飞行及复杂环境下的机动控制提供算法验证平台。; 阅读建议:此资源以Matlab仿真为核心,建议读者结合相关控制理论知识,深入理解代码实现细节,重点关注动力学建模、控制律设计与推力分配模块。在学习过程中,应动手调试参数,复现文中翻转动作的仿真结果,并尝试拓展至其他复杂飞行任务,以加深对无人机控制机理的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值