OP-TEE Hello World 入门实战:从构建到 Host / TA 交互的完整解析

内容分享2小时前发布
0 0 0

OP-TEE Hello World 入门实战:从构建到 Host / TA 交互的完整解析

关键词:OP-TEE、Trusted Application(TA)、Client Application(CA/Host)、TEEC_InvokeCommand、TEE_Param、Secure World / Normal World


1. 为什么要写这篇文章

在学习 OP-TEE 的过程中,很多资料要么停留在“概念介绍”,要么直接跳到复杂的安全存储、密钥管理,中间缺少一个真正“能跑起来、能看清调用链”的实例


hello_world
示例恰好填补了这个空白:

它足够简单(只做整数加减)却完整覆盖了 OP-TEE 的核心机制Host 与 TA 的交互路径清晰非常适合作为第一个实战级入门案例

本文基于一次完整的实际构建与运行过程,对
hello_world
示例进行系统整理,目标是:

读完本文,你应该能明确回答:OP-TEE 的功能,究竟是“怎么通过 Host 和 TA 协同实现的”。


2. OP-TEE 的基本运行模型(先建立正确认知)

在深入代码之前,必须先建立一个非常重要的认知:

2.1 OP-TEE 不是“你直接写 Secure OS”

在 OP-TEE 体系中,你能直接编写的代码只有两类:

世界 代码类型 作用
Normal World Client Application(CA / Host) 发起请求
Secure World Trusted Application(TA) 处理安全逻辑

OP-TEE OS 本身只是一个安全运行环境,真正的“功能”永远在 TA 里。


2.2 Host / TA 的关系是什么?

可以用一句话概括:

Host 是客户端,TA 是一个运行在 Secure World 的同步 RPC 服务。

调用关系是:


Host (Linux 用户态)
  ↓ TEEC_InvokeCommand
OP-TEE Driver (Kernel)
  ↓ SMC
OP-TEE OS
  ↓
TA_InvokeCommandEntryPoint

TA 执行完后,结果会原路返回到 Host。


3. hello_world 示例整体结构


hello_world
示例由两部分组成:


hello_world/
├── host/        # Normal World 客户端程序
│   └── main.c
└── ta/          # Secure World Trusted Application
    ├── hello_world_ta.c
    ├── include/
    └── Makefile

这两个目录缺一不可,它们分别对应 OP-TEE 的两侧世界。


4. Host 端代码详解(Normal World)

OP-TEE Hello World 入门实战:从构建到 Host / TA 交互的完整解析

4.1 Host 的角色是什么?

Host 代码的职责非常明确:

初始化 OP-TEE 上下文打开一个 TA Session构造参数(TEE_Param)通过
TEEC_InvokeCommand
发起请求接收 TA 的返回结果


4.2 关键数据结构:TEEC_Operation


main.c
中,有一段非常关键的代码:


memset(&op, 0, sizeof(op));

op.paramTypes = TEEC_PARAM_TYPES(
    TEEC_VALUE_INOUT,
    TEEC_NONE,
    TEEC_NONE,
    TEEC_NONE
);

op.params[0].value.a = 42;

这里体现了 OP-TEE 参数传递的核心机制

paramTypes 是什么?

OP-TEE 固定支持 4 个参数槽每个参数的类型由
paramTypes
指定

本例中:

第 0 个参数:
VALUE_INOUT
其余 3 个参数:未使用

这意味着:

一个整数从 Normal World 传入 Secure World,并且允许 TA 修改后再传回。


4.3 发起调用:TEEC_InvokeCommand


printf("Invoking TA to increment %d
", op.params[0].value.a);

res = TEEC_InvokeCommand(
    &sess,
    TA_HELLO_WORLD_CMD_DEC_VALUE,
    &op,
    &err_origin
);

printf("TA incremented value to %d
", op.params[0].value.a);

这一行是 Host → Secure World 的唯一入口

注意几个关键点:


TA_HELLO_WORLD_CMD_DEC_VALUE
命令 ID
op
中的参数会被 OP-TEE 自动拷贝这是一个同步调用,不会立即返回


5. TA 端代码详解(Secure World)

5.1 TA 的统一入口函数

所有来自 Host 的命令,最终都会进入:


TEE_Result TA_InvokeCommandEntryPoint(
    void *sess_ctx,
    uint32_t cmd_id,
    uint32_t param_types,
    TEE_Param params[4]
)

这是 TA 的“命令分发中心”。


5.2 cmd_id 与 Host 的对应关系

在 TA 中:


switch (cmd_id) {
case TA_HELLO_WORLD_CMD_INC_VALUE:
    return inc_value(param_types, params);
case TA_HELLO_WORLD_CMD_DEC_VALUE:
    return dec_value(param_types, params);
default:
    return TEE_ERROR_BAD_PARAMETERS;
}

这里的
cmd_id
直接对应 Host 中的
TEEC_InvokeCommand
第二个参数

也就是说:

Host 决定调用哪个 TA 函数,完全由 cmd_id 决定。


5.3 真正的业务逻辑:inc_value / dec_value


dec_value
为例:


static TEE_Result dec_value(uint32_t param_types, TEE_Param params[4])
{
    uint32_t exp_param_types = TEE_PARAM_TYPES(
        TEE_PARAM_TYPE_VALUE_INOUT,
        TEE_PARAM_TYPE_NONE,
        TEE_PARAM_TYPE_NONE,
        TEE_PARAM_TYPE_NONE
    );

    if (param_types != exp_param_types)
        return TEE_ERROR_BAD_PARAMETERS;

    IMSG("Got value: %u from NW", params[0].value.a);
    params[0].value.a--;
    IMSG("Decrease value to: %u", params[0].value.a);

    return TEE_SUCCESS;
}

这里发生了三件关键事情:

校验参数类型(安全边界)读取来自 Normal World 的数据修改参数内容作为返回值


6. 参数是如何“来回传递”的?

这是很多初学者最困惑的问题。

6.1 VALUE_INOUT 的真实含义

当你写:


TEEC_VALUE_INOUT

它意味着:

OP-TEE Core 会在进入 TA 前,把值拷贝到 Secure WorldTA 修改
params[0].value.a
在 TA
return
时,OP-TEE Core 会把结果再拷回 Normal World

整个过程对开发者是透明的。


6.2 调用结束后发生了什么?

当 TA 执行:


return TEE_SUCCESS;

并不是“直接回到 Host”,而是:

返回给 OP-TEE CoreOP-TEE Core 拷贝 OUT / INOUT 参数触发 Secure → Normal World 切换Linux OP-TEE Driver 恢复用户态
TEEC_InvokeCommand()
返回

这就是为什么你能在 Host 中直接看到修改后的值。


7. 运行结果验证

在目标设备上执行 Host 程序:


./optee_example_hello_world

输出:


Invoking TA to increment 42
TA incremented value to 43

同时可以确认:


tee-supplicant
正在运行OP-TEE 驱动已初始化TA 已成功从
/lib/optee_armtz
加载


8. 这个 hello_world 示例真正教会了什么?

这个示例虽然简单,但它完整展示了:

OP-TEE 的 Host / TA 编程模型Secure World 与 Normal World 的 同步调用机制参数如何在两个世界之间安全传递cmd_id 作为“安全接口”的本质

可以说:

理解了 hello_world,就理解了 80% 的 OP-TEE 应用开发模式。


9. 关于 RPC(Remote Procedure Call)的简要说明

在理解了
hello_world
这种 同步调用模型 之后,很多读者会进一步产生一个疑问:

如果 TA 需要访问 Normal World 的资源(比如文件系统、设备、网络),该怎么办?

这就引出了 OP-TEE 中另一个非常重要、但在
hello_world
刻意没有用到的概念:RPC(Remote Procedure Call)

9.1 什么是 OP-TEE 中的 RPC

在 OP-TEE 语境下,RPC 并不是一个“网络概念”,而是:

Secure World 中的 TA,主动请求 Normal World 帮它完成某些事情的一种机制。

也就是说:

正常情况下:

调用方向是 Normal → Secure(Host 调用 TA)

使用 RPC 时:

调用方向会在执行过程中 反向一次:Secure → Normal

但需要强调的是:

RPC 并不会打破整体的同步模型。

从 Host 的视角来看:


TEEC_InvokeCommand() 仍然是一次同步调用

RPC 只是发生在 Secure World 内部执行过程中的一个“中断式回调”。


9.2 RPC 一般用来做什么?

TA 在以下场景中,通常会触发 RPC:

访问 REE 文件系统(普通文件、RPMB 除外)使用 tee-supplicant 提供的服务需要较大内存分配(非 Secure RAM)使用部分依赖 Normal World 的系统能力

在这些情况下:


TA → OP-TEE Core → RPC → tee-supplicant / Linux → 返回 TA

随后 TA 继续执行,最终再返回给 Host。


9.3 为什么 hello_world 没有 RPC?

这是一个非常好的设计选择


hello_world
示例的目标是:

让初学者只关注 Host / TA 的最小闭环

不引入 tee-supplicant、文件系统等复杂因素

把注意力集中在:

cmd_id参数传递Secure / Normal 世界切换

因此,在
hello_world
中:

TA 是一个“纯计算逻辑”,不依赖 Normal World 资源,自然也就不需要 RPC。

这也是为什么它非常适合作为第一个实战示例。


10. hello_world 示例的官方参考链接

为了便于读者对照源码、跟进最新实现,这里给出
hello_world
示例的官方仓库链接:

OP-TEE 官方示例仓库(hello_world):
https://github.com/linaro-swg/optee_examples/tree/master/hello_world

强烈建议:

对照本文阅读源码自己修改
cmd_id
或参数类型做实验逐步体会 Host / TA 交互模型


11. 下一步可以做什么?

在 hello_world 之后,常见的进阶方向包括:

使用
MEMREF
传递 buffer(字符串、数据块)在 TA 中使用 Secure Storage设计多命令 TA(类似安全服务)理解 TA 如何通过 RPC 使用 Normal World 资源

这些内容,都建立在本文所讲的基础之上。


10. 总结

OP-TEE 并不神秘。

它的核心思想非常简单:

用 Secure World 的 TA,去实现你真正信任的逻辑;
用 Normal World 的 Host,去驱动和使用这些逻辑。


hello_world
这个例子,正是理解这一思想的最佳起点。


如果你正在入门 OP-TEE,强烈建议你亲手把这个示例完整跑一遍。
理解一次,胜过看十篇零散的资料。

© 版权声明

相关文章

暂无评论

none
暂无评论...