Linux 5.10 内核 TEE 驱动详解

本文基于 Linux 5.10 LTS 内核(Android 12~Android 14 官方标配 GKI 通用内核版本)完整梳理 TEE 驱动架构、工作流程、libteec 服务对接、ATF 世界切换与数据交互全链路。所有 SMC 通信逻辑均集成在 optee/core.c 与 optee/optee_smc.h 中.

一、整体分层架构与源码结构

Linux 5.10 TEE 子系统采用「通用框架抽象 + 厂商驱动实现 + 架构通信封装」三层松耦合设计,向上提供统一接口,向下对接 OP-TEE 等安全操作系统,是正常世界(REE)与安全世界(TEE)交互的唯一内核桥梁。

1. 三层架构职责划分

架构层级
核心源码路径
核心职责
TEE 核心框架层
drivers/tee/tee_core.c
tee_shm.c
tee_bus.c
与具体 TEE 实现无关的通用抽象:字符设备模型、共享内存池、TEE 总线、上下文与会话管理
OP-TEE 驱动实现层
drivers/tee/optee/core.c
call.c
shm.c
OP-TEE 专属适配:固件探测、消息协议封装、中断处理、RPC 分发
架构通信层
drivers/tee/optee/optee_smc.h
arch/arm64/kernel/smccc-call.S
遵循 ARM SMCCC 标准,封装 SMC 指令触发、寄存器参数打包与返回值解析

2. 5.10 内核完整目录结构

drivers/tee/
├── tee_core.c          # 核心框架:字符设备注册、上下文管理、ioctl 分发
├── tee_shm.c           # 通用共享内存:分配、映射、缓存一致性
├── tee_bus.c           # TEE 总线:TA 设备枚举与内核态驱动匹配
├── tee_private.h       # 核心层私有数据结构与宏定义
└── optee/              # OP-TEE 驱动主体(Android 生态唯一主流实现)
    ├── core.c          # 驱动入口、probe 初始化、SMC 调用封装
    ├── call.c          # 消息协议解析、会话/命令调用核心逻辑
    ├── shm.c           # OP-TEE 专属共享内存与 DMA 适配
    ├── optee_smc.h     # 所有 SMC 功能号、SMCCC 调用宏定义
    └── optee_private.h # 驱动私有结构体、函数声明

3. 双字符设备节点设计

TEE 子系统对外暴露两类设备节点,严格权限隔离,对应不同使用场景:
设备节点
访问主体
权限等级
核心用途
/dev/tee0
第三方应用 CA、普通用户态进程
普通权限
常规业务调用:加密、签名、安全存储等
/dev/teepriv0
tee-supplicant 守护进程
特权权限
处理安全世界反向 RPC 请求:TA 加载、文件系统代理、RPMB 代理

二、核心数据结构

5.10 内核通过一组层级化结构体管理全链路资源,从属关系为:tee_device → tee_context → tee_session,共享内存 tee_shm 挂靠在上下文下。

1. struct tee_device —— TEE 设备根实例

每个物理 TEE 硬件对应两个实例(普通设备 + 特权设备),是驱动的根对象:
  • 存储设备描述符、操作函数集、共享内存池根节点
  • 绑定字符设备 cdev、设备号、内核设备模型
  • 维护引用计数,保证多进程并发访问安全

2. struct tee_context —— 进程级上下文

用户态进程每打开一次 /dev/tee0,内核创建一个独立上下文,与文件描述符生命周期绑定:
  • 维护当前进程的所有会话链表、共享内存链表,实现进程间资源隔离
  • 区分普通客户端上下文与 supplicant 特权上下文,权限分级管控
  • 随 close() 系统调用自动销毁,强制清理残留会话与内存

3. struct tee_session —— TA 会话实例

对应一次与指定 UUID TA 的连接,由 TEEC_OpenSession 创建:
  • 包含会话 ID、TA UUID、会话状态
  • 一个上下文可同时存在多个 TA 会话,所有命令调用必须基于会话执行
  • 会话 ID 由安全世界分配,内核仅做透传与映射

4. struct tee_shm —— 共享内存块

管理 REE 与 TEE 均可访问的非安全共享内存:
  • 记录物理地址、内核虚拟地址、大小、所属上下文
  • 支持两种模式:内核分配、用户态内存注册
  • 维护引用计数,支持多会话共享同一块内存

5. struct tee_param —— 调用参数

统一描述 4 个操作参数,完全对齐 GlobalPlatform 标准:
  • 支持值类型(value)、内存引用类型(memref)两种格式
  • 内核层负责用户态参数合法性校验、虚拟地址到物理地址转换

三、驱动初始化与设备注册流程

OP-TEE 驱动通过设备树节点匹配硬件,入口为 optee_probe() 函数(定义于 drivers/tee/optee/core.c),完整执行流程如下:

1. 硬件信息解析

  1. 解析设备树 firmware/optee 节点,获取安全中断号、共享内存预留区域等硬件参数
  2. 调用 get_invoke_func() 根据 CPU 架构匹配 SMC 调用函数指针,返回 optee_smccc_smc 作为统一调用入口

2. 安全固件探测与能力协商

通过一系列 SMC 快速调用与安全世界握手,验证 TEE 有效性并约定交互规则:
  1. 发送 OPTEE_SMC_FUNCID_CALLS_UID 调用,读取 TEE 标准 UID,确认是 OP-TEE 兼容固件
  2. 调用 OPTEE_SMC_CALL_GET_OS_UUID 与版本查询接口,获取 OP-TEE OS 版本、支持的 API 能力集
  3. 调用 OPTEE_SMC_GET_SHM_CONFIG,与安全世界约定共享内存的物理地址范围、页大小与属性
以上所有 SMC 调用均通过 optee_smccc_smc() 函数触发,功能号定义于 optee_smc.h

3. 核心资源初始化

  1. 共享内存池创建:基于协商好的内存范围,调用 tee_shm_pool_alloc_res_mem() 创建全局共享内存池,划分管理块与数据块
  2. 中断注册:注册安全世界通知中断(通常为 FIQ),绑定中断处理函数,用于异步事件与 RPC 回调分发

4. 设备注册上线

  1. 调用 tee_device_alloc() 分配两个 tee_device 实例,分别对应普通业务设备与特权 RPC 设备
  2. 填充设备描述符与操作函数集,绑定共享内存池
  3. 调用 tee_device_register() 完成注册,内核自动创建 /dev/tee0 与 /dev/teepriv0 字符设备节点
  4. 枚举可用 TA 设备,注册到 TEE 总线,等待内核态客户端驱动匹配挂载

四、面向 libteec 的核心业务工作流程

用户态 libteec 库所有 GlobalPlatform 标准 API,最终均通过 ioctl 系统调用下沉到内核 TEE 驱动执行。

1. ioctl 命令与 libteec API 对应关系

libteec 标准 API
内核 ioctl 命令
执行核心逻辑
TEEC_InitializeContext
open("/dev/tee0")
创建进程级上下文,绑定文件描述符
TEEC_OpenSession
TEE_IOC_OPEN_SESSION
与指定 UUID 的 TA 建立会话
TEEC_InvokeCommand
TEE_IOC_INVOKE
向 TA 发送业务命令并传递参数
TEEC_AllocateSharedMemory
TEE_IOC_ALLOC_SHM
内核分配共享内存并映射到用户态
TEEC_RegisterSharedMemory
TEE_IOC_REGISTER_SHM
将用户态已有内存注册为共享内存
TEEC_CloseSession
TEE_IOC_CLOSE_SESSION
销毁 TA 会话,释放安全世界资源

2. 核心调用:TEEC_InvokeCommand 全链路

这是最核心的业务交互流程,完整路径分为 4 个阶段:
阶段1:用户态参数准备
  1. CA 填充命令 ID、4 个操作参数(value/memref),调用 libteec 的 TEEC_InvokeCommand
  2. libteec 将参数封装为 tee_ioctl_invoke_arg 结构体,发起 TEE_IOC_INVOKE ioctl 系统调用
阶段2:内核参数处理与消息封装
  1. 内核从文件私有数据取出 tee_context,校验会话 ID 有效性
  2. 遍历所有参数,对内存引用类型做地址合法性校验;普通用户内存临时映射到共享内存池,已注册共享内存直接转换物理地址
  3. optee/call.c 中按照 OP-TEE 消息协议,将命令 ID、参数、会话 ID 封装为 optee_msg_arg 结构体,写入共享内存缓冲区
  4. 对共享内存执行 flush_cache_range 缓存刷写,确保数据写入物理内存,安全世界可读取到最新内容
阶段3:SMC 触发与安全世界执行
  1. 调用 optee_smccc_smc(),将共享内存物理地址、消息大小等参数按 SMCCC 规范写入通用寄存器
  2. 底层执行 SMC #0 指令,CPU 提升异常级别至 EL3,进入 ATF 固件
  3. ATF 校验 SMC 功能号,路由到 OP-TEE SPD,切换安全状态,跳转到 OP-TEE OS 入口
  4. OP-TEE OS 解析共享内存消息,找到对应会话与 TA,执行指定命令,将结果写回共享内存
  5. 安全世界执行 SMC 返回指令,回到 EL3;ATF 恢复非安全上下文,返回 Linux 内核
阶段4:结果回传
  1. 内核返回后对共享内存执行 invalidate_cache_range 缓存失效,确保读取到安全世界写入的最新数据
  2. 解析 optee_msg_arg 中的返回状态与输出参数,更新内核态参数结构
  3. 将输出数据拷贝到用户态缓冲区,释放临时映射的内存资源
  4. 向用户态返回执行结果与错误码

3. RPC 反向调用流程

安全世界无法直接访问正常世界资源,需通过 RPC 反向请求协助,典型场景为动态 TA 加载、安全存储后端代理:
  1. 安全世界执行中需要 REE 服务,构造 RPC 请求写入共享内存,执行 SMC 返回
  2. Linux 内核从 SMC 返回值识别 RPC 请求,解析命令类型
  3. 内核可直接处理的请求(如内存分配)直接处理后再次 SMC 返回结果
  4. 需要用户态处理的请求,转发到 /dev/teepriv0,唤醒 tee-supplicant 进程
  5. tee-supplicant 执行对应操作(如读取 TA 镜像文件),将结果写回共享内存,通过 ioctl 通知内核
  6. 内核再次触发 SMC 调用,将结果传回安全世界,安全世界恢复执行

五、SMC 调用与 ATF 世界切换全链路

5.10 内核中 SMC 调用采用「驱动封装 → 架构通用实现 → 硬件触发 → ATF 分发」的四层链路,是 TrustZone 隔离的唯一合法入口。

1. 内核 SMC 封装实现(5.10 专属)

定义位置:drivers/tee/optee/core.c
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); 
}
  • 该函数是 OP-TEE 驱动的 SMC 统一出口,直接封装内核通用 arm_smccc_smc() 接口
  • 所有 SMC 功能号宏定义于 drivers/tee/optee/optee_smc.h,遵循 ARM SMCCC 1.1 规范
  • 底层最终由 arch/arm64/kernel/smccc-call.S 中的汇编代码执行 SMC #0 指令

2. 硬件触发与异常级别提升

Linux 内核运行在非安全 EL1 特权级,执行 SMC #0 指令时,CPU 硬件自动完成:
  1. 保存当前程序状态寄存器 PSTATE 到 SPSR_EL3,保存返回地址到 ELR_EL3
  2. 异常级别提升至 EL3,CPU 进入安全监控模式
  3. 跳转到 VBAR_EL3 指向的异常向量表,即 ATF 预先注册的异常入口
硬件强制保证:非安全世界无法直接跳转到安全世界代码,所有切换必须经过 EL3 的 ATF 校验分发,这是 TrustZone 隔离的硬件根基。

3. ATF 异常捕获与 SMC 分发

ATF BL31 运行在 EL3,runtime_exceptions.S 定义完整异常向量表,SMC 异常进入 smc_handler64 处理函数:

1.保存非安全上下文:将非安全世界的通用寄存器、系统寄存器保存到 ATF 内部上下文栈

2.解析 SMC 功能号:从 x0 读取 Function ID,按 SMCCC 规范判断调用类型

  • 标准快速调用(PSCI 电源管理等):ATF 内置服务直接处理
  • 可信操作系统调用:功能号属于 TEE 服务范围,转发给对应 SPD

3.OP-TEE SPD 路由:Android 平台注册 opteed 作为 OP-TEE 分发器,负责 TEE 请求转发与生命周期管理

4. 安全世界进入与返回

opteed_smc_handler 执行世界切换的核心步骤:
  1. 校验请求合法性,检查功能号范围、参数边界
  2. 修改 SCR_EL3.NS 位为 0(安全态),配置 SPSR_EL3 目标异常级别为 S-EL1
  3. 从 ATF 上下文栈中恢复安全世界的寄存器状态
  4. 设置 ELR_EL3 为 OP-TEE OS 的 SMC 处理入口,执行 eret 退出 EL3,进入 S-EL1 的 OP-TEE 内核
安全世界处理完成后,反向执行 SMC 指令触发返回,ATF 恢复非安全上下文,切换回非安全态,最终回到 Linux 内核的 SMC 调用处继续执行。

六、从 ATF / 安全世界读取数据的机制

根据数据量与服务类型,分为三类数据读取模式,所有交互均符合 SMCCC 规范。

1. 寄存器级短数据读取

适用于状态码、版本号、小尺寸参数等少量数据,直接通过通用寄存器传递:
  • 调用时:x0 存放功能号,x1~x7 存放输入参数
  • 返回时:x0 存放返回状态码,x1~x3 存放返回数据
  • 典型场景:OP-TEE 固件版本查询、会话创建返回码、ATF 原生服务状态
  • 限制:单轮 SMC 最多返回 4 个寄存器数据,仅适合控制信令

2. 共享内存大数据读取

业务级数据(加密结果、证书、密钥密文等)均通过非安全共享内存传输,完整读取流程:
  1. 内核 TEE 驱动从共享内存池分配缓冲区,获得物理地址
  2. 将物理地址、长度通过寄存器参数传递给 ATF,转发给 OP-TEE OS
  3. OP-TEE 校验内存地址属于合法非安全区域后,将处理结果写入物理内存
  4. SMC 返回后,内核执行缓存失效操作,将数据拷贝到用户态缓冲区
安全设计:共享内存仅配置为非安全属性,不会泄露安全内存数据;OP-TEE 内部会做地址范围校验,防止攻击者构造安全内存地址窃取数据。

3. ATF 原生服务数据读取

若数据由 ATF 固件直接提供(无需经过 OP-TEE),由 EL3 代码直接处理返回:
  • 典型服务:SOC 版本读取、芯片唯一 ID、安全 EFUSE 状态、电源管理状态
  • 调用方式:内核直接发送对应功能号的 SMC 调用,ATF 读取硬件寄存器后通过寄存器返回结果
  • 特点:链路最短,不经过安全世界,仅用于芯片级底层信息查询

4. 异步数据通知机制

耗时较长的安全操作采用「SMC 发起请求 + 中断通知结果」的异步模式:
  1. 内核发送 SMC 提交异步任务后立即返回,不阻塞等待
  2. 安全世界处理完成后触发 FIQ 中断,经 ATF 路由到非安全世界
  3. Linux TEE 驱动中断处理函数被触发,从共享内存读取处理结果,通过回调通知上层

七、安全设计要点

  1. 资源隔离:每个进程拥有独立上下文,会话、共享内存按上下文隔离,进程间无法互相访问 TEE 资源
  2. 参数强校验:内核层对所有用户态传入的地址、长度、参数类型做严格校验,防止非法参数触发内存漏洞
  3. 单向不可逆切换:SMC 切换由硬件与 ATF 双重管控,非安全世界无法直接访问安全内存
  4. 特权分级:普通设备与特权设备分离,普通应用无法访问 RPC 特权接口
  5. 内存边界防护:安全世界侧二次校验共享内存地址范围,防止越权访问安全内存
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐