Linux5.10内核TEE驱动全链路源码深度解析

Linux 5.10 内核 TEE 驱动全链路源码解释

本文基于 Linux 5.10 LTS 内核源码,从内核启动时的驱动注册与设备初始化,到运行时应用通过 libteec 调用 TEE 服务的完整路径,逐函数拆解关键代码与调用流程。

一、内核启动阶段:驱动注册与设备初始化

OP-TEE 驱动以平台驱动(platform_driver)形式注册,通过设备树匹配硬件,经 probe 流程完成固件探测、资源初始化与字符设备注册,最终向用户空间暴露 /dev/tee0 等服务入口。

1. 驱动入口:平台驱动注册

文件路径:drivers/tee/optee/core.c
OP-TEE 驱动通过内核标准 module_platform_driver 宏注册为平台驱动,绑定设备树匹配表与 probe 入口函数。
// 设备树匹配表:匹配 DTS 中的 linaro,optee-tz 节点
static const struct of_device_id optee_dt_match[] = {
	{ .compatible = "linaro,optee-tz" },
	{},
};
MODULE_DEVICE_TABLE(of, optee_dt_match);

// 平台驱动结构体
static struct platform_driver optee_driver = {
	.probe = optee_probe,  // 核心probe入口
	.driver = {
		.name = "optee",
		.of_match_table = optee_dt_match,
	},
};

// 注册驱动到内核平台总线
module_platform_driver(optee_driver);

内核启动时,若设备树中存在 firmware/optee 节点且 compatible 属性匹配,内核自动调用 optee_probe 执行驱动初始化。

2. 核心 Probe 流程:固件探测与资源初始化

文件路径:drivers/tee/optee/core.c
optee_probe 是驱动初始化的总入口,按顺序完成硬件能力确认、资源分配与设备注册,核心步骤如下:
步骤1:获取 SMC 调用函数指针
首先根据硬件架构获取底层 SMC 调用入口,为后续与安全世界通信做准备。
static int optee_probe(struct platform_device *pdev)
{
	struct optee *optee;
	int ret;

	// 分配驱动私有结构体
	optee = devm_kzalloc(&pdev->dev, sizeof(*optee), GFP_KERNEL);
	if (!optee)
		return -ENOMEM;

	// 获取架构对应的SMC调用函数指针(ARM64下为optee_smccc_smc)
	optee->invoke_fn = get_invoke_func(&pdev->dev);
	if (!optee->invoke_fn)
		return -ENODEV;
    ...
}

get_invoke_func 匹配设备树属性,返回对应的 SMC 封装函数,ARM TrustZone 架构下固定返回 optee_smccc_smc:

static optee_invoke_fn *get_invoke_func(struct device *dev)
{
	const struct of_device_id *match;
	match = of_match_device(optee_dt_match, dev);
	if (match)
		return optee_smccc_smc;
	return NULL;
}
步骤2:安全固件探测与能力协商
通过一系列 SMC 快速调用与安全世界握手,验证 OP-TEE 固件存在并获取核心配置:
	// 1. 验证OP-TEE固件UID,确认固件合法性
	ret = optee_detect(optee);
	if (ret)
		return ret;

	// 2. 获取OP-TEE OS版本与API能力
	ret = optee_get_version(optee);
	if (ret)
		return ret;

	// 3. 协商共享内存物理地址范围与属性
	ret = optee_get_shm_config(optee);
	if (ret)
		return ret;

以 optee_detect 为例,通过发送标准 SMC 命令读取固件 UID:

static int optee_detect(struct optee *optee)
{
	struct arm_smccc_res res;

	// 发送OPTEE_SMC_FUNCID_CALLS_UID快速调用
	optee->invoke_fn(OPTEE_SMC_FUNCID_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);

	// 校验返回的UID是否为OP-TEE标准UID
	if (res.a0 != OPTEE_UID_0 || res.a1 != OPTEE_UID_1 ||
	    res.a2 != OPTEE_UID_2 || res.a3 != OPTEE_UID_3)
		return -ENODEV;
	return 0;
}

步骤3:共享内存池初始化
文件路径:drivers/tee/optee/shm.c
基于协商好的共享内存范围,初始化全局共享内存池,用于后续大数据传输:
	// 初始化共享内存池
	ret = optee_shm_init(optee);
	if (ret)
		return ret;

步骤4:注册两级 TEE 字符设备
创建普通业务设备与特权 RPC 设备两个实例,向用户空间暴露服务入口:
	// 1. 注册普通业务设备:对应/dev/tee0,供普通应用使用
	optee->teedev = tee_device_alloc(&optee->desc, &pdev->dev,
					 optee->pool, optee);
	ret = tee_device_register(optee->teedev);
	if (ret)
		goto err_shm_remove;

	// 2. 注册特权RPC设备:对应/dev/teepriv0,供tee-supplicant使用
	optee->supp_teedev = tee_device_alloc(&optee->supp_desc, &pdev->dev,
					      optee->pool, optee);
	ret = tee_device_register(optee->supp_teedev);
	if (ret)
		goto err_teedev_unregister;

步骤5:中断注册
注册安全世界异步通知中断,用于处理 RPC 回调与异步事件:
	// 注册安全中断处理函数
	ret = optee_supp_init(optee);
	if (ret)
		goto err_supp_teedev_unregister;

3. TEE 核心层:字符设备注册实现

文件路径:drivers/tee/tee_core.c
tee_device_alloc 与 tee_device_register 是 TEE 通用框架的设备注册核心,负责创建字符设备、绑定文件操作集。
tee_device_alloc:初始化设备结构
struct tee_device *tee_device_alloc(const struct tee_desc *desc,
				    struct device *dev,
				    struct tee_shm_pool *pool, void *data)
{
	struct tee_device *teedev;
    ...
	// 初始化字符设备cdev,绑定文件操作集tee_fops
	cdev_init(&teedev->cdev, &tee_fops);
	teedev->cdev.owner = desc->owner;
    ...
	teedev->desc = desc;   // 底层驱动的操作函数集
	teedev->pool = pool;   // 共享内存池
	teedev->data = data;   // 驱动私有数据
    ...
}

字符设备文件操作集 tee_fops
这是用户态应用与内核驱动交互的总入口,所有系统调用都通过该结构体分发:
static const struct file_operations tee_fops = {
	.owner = THIS_MODULE,
	.open = tee_open,            // 打开设备,创建上下文
	.release = tee_release,      // 关闭设备,销毁上下文
	.unlocked_ioctl = tee_ioctl, // 命令分发核心
	.compat_ioctl = compat_ptr_ioctl,
	.mmap = tee_mmap,            // 共享内存映射
	.llseek = noop_llseek,
};

tee_device_register:注册字符设备节点
int tee_device_register(struct tee_device *teedev)
{
	int rc;
	// 分配设备号
	rc = alloc_chrdev_region(&teedev->devt, 0, 1, "tee");
    ...
	// 添加字符设备到内核
	rc = cdev_add(&teedev->cdev, teedev->devt, 1);
    ...
	// 创建设备节点,用户空间可见/dev/tee0
	teedev->dev.devt = teedev->devt;
	device_add(&teedev->dev);
    ...
}

执行完成后,用户空间出现 /dev/tee0 与 /dev/teepriv0 两个设备节点,应用可通过标准文件操作访问 TEE 服务。

二、运行时阶段:为应用提供服务的完整调用链路

用户态应用通过 GlobalPlatform 标准库 libteec 访问 TEE 服务,所有 API 最终通过 open + ioctl 系统调用下沉到内核驱动,经 SMC 切换到安全世界执行。以下拆解最核心的 3 个服务流程。

1. 上下文建立:TEEC_InitializeContext

用户态调用 TEEC_InitializeContext(NULL, &ctx) 时,libteec 内部执行 open("/dev/tee0", O_RDWR),触发内核 tee_open 回调,创建进程级 TEE 上下文。
文件路径:drivers/tee/tee_core.c
static int tee_open(struct inode *inode, struct file *file)
{
	struct tee_device *teedev = container_of(inode->i_cdev,
						 struct tee_device, cdev);
	struct tee_context *ctx;
	int rc;

	// 分配进程级上下文结构体
	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return -ENOMEM;

	// 初始化上下文:引用计数、会话链表、共享内存链表
	kref_init(&ctx->refcount);
	ctx->teedev = teedev;
	INIT_LIST_HEAD(&ctx->sessions);
	INIT_LIST_HEAD(&ctx->shm_list);
	mutex_init(&ctx->mutex);

	// 调用底层驱动的open回调(optee_open,执行驱动私有初始化)
	if (teedev->desc->ops->open) {
		rc = teedev->desc->ops->open(ctx);
		if (rc)
			goto err_free_ctx;
	}

	// 上下文指针存入文件私有数据,后续ioctl直接取用
	file->private_data = ctx;
	return 0;
    ...
}

关键作用:每个 open 对应一个独立 tee_context,实现进程间资源隔离;后续所有会话、共享内存都挂靠在该上下文下。

2. 会话创建:TEEC_OpenSession

用户态调用 TEEC_OpenSession 与指定 UUID 的 TA 建立会话,对应 ioctl 命令 TEE_IOC_OPEN_SESSION。
步骤1:核心层 ioctl 分发
文件路径:drivers/tee/tee_core.c
static long tee_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct tee_context *ctx = file->private_data;
	long rc;

	switch (cmd) {
	case TEE_IOC_OPEN_SESSION:
		rc = tee_ioctl_open_session(ctx, (void __user *)arg);
		break;
	case TEE_IOC_INVOKE:
		rc = tee_ioctl_invoke(ctx, (void __user *)arg);
		break;
    ...
	}
	return rc;
}

步骤2:参数校验与底层转发
tee_ioctl_open_session 拷贝用户态参数,校验合法性后转发给底层 OP-TEE 驱动:
static int tee_ioctl_open_session(struct tee_context *ctx, void __user *uarg)
{
	struct tee_ioctl_open_session_arg arg;
	struct tee_param *param = NULL;
	int rc;

	// 从用户空间拷贝参数
	if (copy_from_user(&arg, uarg, sizeof(arg)))
		return -EFAULT;

	// 解析参数属性,分配内核态参数结构
	num_params = tee_param_attr_to_num(arg.clnt_login & TEE_IOCTL_PARAM_ATTR_MASK);
	if (num_params) {
		param = kcalloc(num_params, sizeof(*param), GFP_KERNEL);
		tee_ioctl_param_get(num_params, arg.params, param);
	}

	// 调用底层驱动的open_session回调
	rc = ctx->teedev->desc->ops->open_session(ctx, arg.uuid, arg.clnt_login,
						  arg.cancel_id, &arg.session,
						  param, num_params);

	// 结果回写到用户空间
	if (copy_to_user(uarg, &arg, sizeof(arg)))
		rc = -EFAULT;
    ...
}

步骤3:OP-TEE 驱动消息封装与 SMC 调用
文件路径:drivers/tee/optee/call.c
底层 optee_open_session 封装 OP-TEE 消息协议,触发 SMC 调用:
int optee_open_session(struct tee_context *ctx, struct tee_ioctl_buf_data *buf,
		       u32 *session_id)
{
	struct optee_msg_arg *msg_arg;
	struct tee_shm *shm;
	phys_addr_t parg;
	int rc;

	// 分配共享内存,用于存放消息结构体
	shm = tee_shm_alloc(ctx->teedev, sizeof(*msg_arg), TEE_SHM_MAPPED);
	msg_arg = tee_shm_get_va(shm);

	// 填充OP-TEE消息头:命令类型、参数个数、TA UUID等
	msg_arg->cmd = OPTEE_MSG_CMD_OPEN_SESSION;
	msg_arg->num_params = num_params;
	memcpy(msg_arg->uuid, uuid, sizeof(msg_arg->uuid));
	// 填充参数...

	// 执行SMC调用,传入共享内存物理地址
	parg = tee_shm_get_pa(shm);
	rc = optee_do_call_with_arg(ctx, parg);

	// 解析返回结果,获取会话ID
	*session_id = msg_arg->session;
	tee_shm_free(shm);
	return rc;
}

步骤4:SMC 统一调用入口
文件路径:drivers/tee/optee/core.c
optee_do_call_with_arg 最终调用 invoke_fn 触发硬件 SMC 切换:
int optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
{
	struct arm_smccc_res res;
	struct optee *optee = tee_get_drvdata(ctx->teedev);

	// 执行标准SMC调用:功能号+共享内存物理地址
	optee->invoke_fn(OPTEE_SMC_CALL_WITH_ARG, 0, parg, 0, 0, 0, 0, 0, &res);

	// 解析返回状态
	return optee_smc_return_value(res.a0);
}

3. 命令调用:TEEC_InvokeCommand

这是业务逻辑的核心路径,应用向 TA 发送具体命令,整体流程与会话创建高度相似,核心差异为消息命令字。
调用链路总览
用户态 TEEC_InvokeCommand
    ↓
libteec 封装参数,发起 TEE_IOC_INVOKE ioctl
    ↓
内核 tee_ioctl_invoke 参数校验与转换
    ↓
optee_invoke_func 封装 OPTEE_MSG_CMD_INVOKE 消息
    ↓
optee_do_call_with_arg 触发 SMC 调用
    ↓
ATF 路由到 OP-TEE OS,TA 执行命令
    ↓
结果沿原路径返回用户态

文件路径:drivers/tee/optee/call.c
核心消息封装逻辑:
int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
		      struct tee_param *param)
{
    ...
    // 消息命令字设置为调用命令
	msg_arg->cmd = OPTEE_MSG_CMD_INVOKE;
	msg_arg->session = arg->session;  // 指定会话ID
	msg_arg->func = arg->func;       // 指定命令ID
    ...
    // 同样通过optee_do_call_with_arg触发SMC
	return optee_do_call_with_arg(ctx, parg);
}

4. 底层 SMC 硬件触发

文件路径:arch/arm64/kernel/smccc-call.S
OP-TEE 驱动封装的 optee_smccc_smc 最终调用内核通用 arm_smccc_smc,底层由汇编指令完成硬件级世界切换:
static void optee_smccc_smc(unsigned long a0, unsigned long a1,
			    unsigned long a2, unsigned long a3,
			    unsigned long a4, unsigned long a5,
			    unsigned long a6, unsigned long a7,
			    struct arm_smccc_res *res)
{
	arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}

汇编层最终执行 SMC #0 指令,CPU 异常级别提升至 EL3,进入 ATF 固件完成安全世界路由。

三、关键调用链总览

1. 驱动注册调用链

module_platform_driver(optee_driver)
    ↓
内核平台总线匹配设备树
    ↓
optee_probe()
    ├─ get_invoke_func() → 获取SMC入口
    ├─ optee_detect() → 固件UID校验
    ├─ optee_get_shm_config() → 共享内存协商
    ├─ optee_shm_init() → 内存池初始化
    ├─ tee_device_alloc() → 分配设备结构
    └─ tee_device_register() → 注册字符设备节点

2. 应用服务调用链

应用 TEEC_InitializeContext
    ↓
open("/dev/tee0") → tee_open() → 创建tee_context
    ↓
TEEC_OpenSession
    ↓
ioctl(TEE_IOC_OPEN_SESSION) → tee_ioctl_open_session
    ↓
optee_open_session() → 封装消息
    ↓
optee_do_call_with_arg() → SMC调用
    ↓
ATF → OP-TEE OS → TA执行
    ↓
结果原路返回

四、设计要点

  1. 上下文隔离:每个进程 open 对应独立上下文,会话、共享内存完全隔离,进程间无法越权访问。
  2. 参数强校验:核心层与驱动层双层校验用户态参数,防止非法地址、越界长度触发内核漏洞。
  3. 特权分级:普通设备与特权设备分离,普通应用无法访问 RPC 特权接口。
  4. 单向不可逆:SMC 切换由硬件与 ATF 双重管控,非安全世界无法直接访问安全内存。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值