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执行
↓
结果原路返回
四、设计要点
- 上下文隔离:每个进程 open 对应独立上下文,会话、共享内存完全隔离,进程间无法越权访问。
- 参数强校验:核心层与驱动层双层校验用户态参数,防止非法地址、越界长度触发内核漏洞。
- 特权分级:普通设备与特权设备分离,普通应用无法访问 RPC 特权接口。
- 单向不可逆:SMC 切换由硬件与 ATF 双重管控,非安全世界无法直接访问安全内存。

312

被折叠的 条评论
为什么被折叠?



