手机安全和可信应用开发指南.pdf
http://www.100md.com
2020年1月8日
![]() |
| 第1页 |
![]() |
| 第6页 |
![]() |
| 第14页 |
![]() |
| 第24页 |
![]() |
| 第45页 |
![]() |
| 第408页 |
参见附件(18090KB,786页)。
手机安全和可信应用开发指南,这是一本技术开发教程书籍,里面的内容由基础到深入的为学者们讲解,全书一共四大篇三四十个小章节,有需要的同学可以学习。

内容提要
TrustZone技术是一种提高ARM芯片安全性的技术,OP-TEE是基于ARM的TrustZone技术搭建的可信执行环境。两者的结合可为系统软件提供硬件级别的安全保护。本书主要分为4篇,总计25章,第壹篇介绍了TrustZone技术的背景、实现原理、系统基本框架以及OP-TEE开发环境的搭建;第二篇分析了OP-TEE在REE和TEE中各组件的作用和联系,为将OP-TEE集成到基于ARMv7/ARMv8处理器的开发平台打下基础;第三篇详细介绍OP-TEE内核的中断处理、线程管理和通信等主要功能的实现原理,使读者对TEEOS的架构设计有进一步的认识;第四篇介绍基于OP-TEE在加密、解密、安全存储、在线支付等方面的实际应用,以及如何开发基于OP-TEE的可信应用程序和安全驱动软件。
部分目录
第一篇 基础技术篇
第1章 可信执行环境
1.1 系统存在的安全问题
1.2 TEE如何保护数据安全
1.3 现有TEE解决方案
1.3.1 智能手机领域的TEE
1.3.2 智能电视领域的TEE
1.3.3 IoT领域及其他领域的TEE
1.4 为什么选择OP-TEE
第2章 ARM的TrustZone技术
2.1 TrustZone技术
2.1.1 片上系统硬件框架
2.1.2 ARMv7架构的TrustZone技术
2.1.3 ARMv8架构的TrustZone技术
2.2 ARM安全扩展组件
2.2.1 AXI总线上安全状态位的扩展
2.2.2 AXI-to-APB桥的作用
2.2.3 TrustZone地址空间控制组件
2.2.4 TrustZone内存适配器组件
2.2.5 TrustZone保护控制器组件
2.2.6 TrustZone中断控制器组件
2.2.7 Cache和MMU的扩展
2.3 TrustZone技术对资源隔离的实现
2.3.1 中断源的隔离
2.3.2 片上RAM和片上ROM的隔离
2.3.3 片外DRAM的隔离
2.3.4 外围设备的隔离
2.4 小结
第3章 ARM可信固件
3.1 为什么使用ATF
3.2 ATF的主要功能
3.3 ATF与TEE的关系
3.4 小结
第4章 OP-TEE运行环境的搭建及编译
4.1 获取OP-TEE代码并搭建运行环境
4.1.1 OP-TEE开发环境的搭建
4.1.2 获取OP-TEE的源代码
4.1.3 获取编译OP-TEE的toolchain
4.1.4 编译QEMU
4.1.5 运行OP-TEE
4.1.6 运行xtest和optee_example_hello_world
4.2 运行CA和TA示例
4.2.1 示例代码的获取和集成
4.2.2 目录和文件创建
4.2.3 CA端代码的修改
4.2.4 TA端代码的修改
4.2.5 TA和CA在OP-TEE的集成
4.3 OP-TEE源代码结构
4.4 OP-TEE编译
4.4.1 编译目标的依赖关系
4.4.2 bios.bin镜像的生成过程
4.4.3 run-only目标的执行
4.5 小结
第二篇 系统集成篇
第三篇 OP-TEE内核篇
第四篇 应用开发篇
此的特色
俗话说,基础打不牢,学问攀不高。本书采取自下而上的方式从硬件的角度介绍了TrustZone技术,并结合源代码逐步剖析了基于TrustZone技术的OP-TEE实现。在技术深度上,本书从入门者的角度出发由浅入深,从最基础的开发环境的搭建到最终的OP-TEE OS的内部实现都进行了介绍;从内容易读性上来讲,本书提供了基础的示例代码和各种算法的使用示例,并给出了所有示例的源代码链接及操作的实验步骤。相信读者通过边学习边实践的方式阅读完本书后,能够掌握TrustZone技术的基础原理和使用OP-TEE进行实际的应用开发。
由于任何TEE方案的源代码都属于芯片厂商的商业机密,外界无法一览各TEE方案的实现原理,且TrustZone也是最近几年才被正式商用的,所以网上的资料较少。本书是作者基于多年的工作积累并对实际工作过程中遇见的问题进行整理后形成的。
手机安全和可信应用开发指南截图


网络空间安全技术丛书
手机安全和可信应用开发指南:TrustZone
与OP-TEE技术详解
帅峰云 黄腾 宋洋 编著
ISBN:978-7-111-60956-8
本书纸版由机械工业出版社于2018年出版,电子版
由华章分社(北京华章图文信息有限公司,北京奥
维博世图书发行有限公司)全球范围内制作与发
行。
版权所有,侵权必究
客服热线:+ 86-10-68995265
客服信箱:service@bbbvip.com
官方网址:www.hzmedia.com.cn
新浪微博 @华章数媒
微信公众号 华章电子书(微信号:hzebook)目录 推荐序
前言
致谢
第一篇 基础技术篇
第1章 可信执行环境
1.1 系统存在的安全问题
1.2 TEE如何保护数据安全
1.3 现有TEE解决方案
1.3.1 智能手机领域的TEE
1.3.2 智能电视领域的TEE
1.3.3 IoT领域及其他领域的TEE
1.4 为什么选择OP-TEE
第2章 ARM的TrustZone技术
2.1 TrustZone技术
2.1.1 片上系统硬件框架
2.1.2 ARMv7架构的TrustZone技术
2.1.3 ARMv8架构的TrustZone技术
2.2 ARM安全扩展组件
2.2.1 AXI总线上安全状态位的扩展
2.2.2 AXI-to-APB桥的作用
2.2.3 TrustZone地址空间控制组件
2.2.4 TrustZone内存适配器组件
2.2.5 TrustZone保护控制器组件
2.2.6 TrustZone中断控制器组件2.2.7 Cache和MMU的扩展
2.3 TrustZone技术对资源隔离的实现
2.3.1 中断源的隔离
2.3.2 片上RAM和片上ROM的隔离
2.3.3 片外DRAM的隔离
2.3.4 外围设备的隔离
2.4 小结
第3章 ARM可信固件
3.1 为什么使用ATF
3.2 ATF的主要功能
3.3 ATF与TEE的关系
3.4 小结
第4章 OP-TEE运行环境的搭建及编译
4.1 获取OP-TEE代码并搭建运行环境
4.1.1 OP-TEE开发环境的搭建
4.1.2 获取OP-TEE的源代码
4.1.3 获取编译OP-TEE的toolchain
4.1.4 编译QEMU
4.1.5 运行OP-TEE
4.1.6 运行xtest和
optee_example_hello_world
4.2 运行CA和TA示例
4.2.1 示例代码的获取和集成
4.2.2 目录和文件创建
4.2.3 CA端代码的修改
4.2.4 TA端代码的修改4.2.5 TA和CA在OP-TEE的集成
4.3 OP-TEE源代码结构
4.4 OP-TEE编译
4.4.1 编译目标的依赖关系
4.4.2 bios.bin镜像的生成过程
4.4.3 run-only目标的执行
4.5 小结
第二篇 系统集成篇
第5章 QEMU运行OP-TEE的启动过程
5.1 bios.bin的入口函数
5.2 OP-TEE镜像的加载和启动
5.3 Linux内核镜像的加载和启动
5.4 rootfs的挂载
5.5 OP-TEE驱动的启动
5.6 tee_supplicant的启动
5.7 小结
第6章 安全引导功能及ATF的启动过程
6.1 安全引导的作用
6.2 安全引导的原理
6.2.1 ARMv7安全引导的过程
6.2.2 ARMv8安全引导的过程
6.3 ATF的启动过程
6.3.1 ATF中bl1的启动
6.3.2 ATF中bl2的启动
6.3.3 ATF中bl31的启动
6.3.4 ATF中bl32的启动6.3.5 ATF启动过程小结
6.4 小结
第7章 OP-TEE OS的启动过程
7.1 OP-TEE镜像启动过程
7.1.1 OP-TEE OS的入口函数
7.1.2 OP-TEE的内核初始化过程
7.1.3 OP-TEE服务项的启动
7.1.4 OP-TEE驱动的挂载
7.2 ARM64位与ARM32位OP-TEE启动过程的
差异
7.3 小结
第8章 OP-TEE在REE侧的上层软件
8.1 OP-TEE的软件框架
8.2 REE侧libteec库提供的接口
8.2.1 libteec库提供的接口说明
8.2.2 CA调用libteec库中接口的流程
8.3 REE侧的守护进程——tee_supplicant
8.3.1 tee_supplicant编译生成和自启动
8.3.2 tee_supplicant入口函数
8.3.3 tee_supplicant存放RPC请求的结构体
8.3.4 tee_supplicant中的无限循环
8.3.5 tee_supplicant获取TA的RPC请求
8.3.6 TA RPC请求的解析
8.3.7 RPC请求的处理
8.3.8 回复RPC请求
8.4 各种RPC请求的处理8.4.1 加载TA镜像
8.4.2 操作REE侧的文件系统
8.4.3 操作RPMB
8.4.4 分配共享内存
8.4.5 释放共享内存
8.4.6 记录程序执行效率
8.4.7 网络套接字操作
8.5 小结
第9章 REE侧OP-TEE的驱动
9.1 OP-TEE驱动模块的编译保存
9.2 REE侧OP-TEE驱动的加载
9.2.1 设备号和class的初始化
9.2.2 optee_driver_init函数
9.2.3 挂载驱动的probe操作
9.2.4 获取切换到Monitor模式或EL3的接口
9.2.5 驱动版本和API版本校验
9.2.6 判定OP-TEE是否预留共享内存空间
9.2.7 配置驱动与OP-TEE之间的共享内存
9.2.8 分配和设置tee0和teepriv0的设备信息
结构体变量
9.2.9 tee0和teepriv0设备的注册
9.2.10 请求队列的初始化
9.2.11 使能TEE中共享内存的缓存
9.2.12 OP-TEE驱动挂载的总结
9.3 REE侧用户空间对驱动的调用过程
9.4 OP-TEE驱动中重要的结构体变量9.4.1 OP-TEE驱动的file_operation结构体变
量tee_fops
9.4.2 tee0设备的tee_driver_ops结构体变量
optee_ops
9.4.3 teepriv0设备的操作结构体变量
optee_supp_ops
9.4.4 共享驱动缓存操作变量
tee_shm_dma_buf_ops
9.5 OP-TEE驱动与OP-TEE之间共享内存的注
册和分配
9.6 libteec库中的接口在驱动中的实现
9.6.1 libteec库中的open操作
9.6.2 libteec库中的release操作
9.6.3 libteec执行get_version操作
9.6.4 libteec库中的open session操作
9.6.5 libteec库中的invoke操作
9.7 tee_supplicant接口在驱动中的实现
9.7.1 接收OP-TEE的RPC请求
9.7.2 获取OP-TEE的RPC请求
9.7.3 OP-TEE的RPC请求的返回
9.8 小结
第三篇 OP-TEE内核篇
第10章 ARM核安全态和非安全态间的切换
10.1 ARMv7基本知识
10.1.1 ARMv7运行模式扩展
10.1.2 安全状态位扩展10.1.3 重要寄存器
10.1.4 安全监控模式调用的汇编指令
10.2 Monitor模式下的处理过程
10.2.1 Monitor模式对安全监控模式调用的
处理
10.2.2 正常世界状态中触发安全监控模式
调用的处理过程
10.2.3 安全世界状态中触发安全监控模式
调用的处理过程
10.3 ARMv8基本知识
10.3.1 ARM核运行模式的新定义
10.3.2 ARMv8安全状态位扩展
10.3.3 寄存器资源
10.3.4 安全监控模式调用汇编指令
10.4 EL3的处理过程
10.4.1 ATF中EL3异常向量表的注册
10.4.2 EL3处理安全监控模式调用的流程
10.4.3 安全世界状态中触发安全监控模式
调用的处理过程
10.4.4 正常世界状态中触发安全监控模式
调用的处理过程
10.4.5 opteed_smc_handler函数
10.5 小结
第11章 OP-TEE对安全监控模式调用的处理
11.1 OP-TEE的线程向量表
11.2 ARMv7中Monitor模式对安全监控模式调用的处理
11.3 ARMv8中EL3处理安全监控模式调用的
实现
11.4 OP-TEE对快速安全监控模式调用的处理
11.5 OP-TEE对标准安全监控模式调用的处理
11.5.1 OP-TEE对RPC请求返回操作的处理
11.5.2 OP-TEE对libteec库触发的安全监控
模式调用的处理
11.6 小结
第12章 OP-TEE对中断的处理
12.1 系统的中断处理
12.2 中断控制器
12.2.1 GIC寄存器
12.2.2 ARMv7 SCR寄存器的设定
12.2.3 ARMv8 SCR寄存器的设定
12.2.4 GICv2架构
12.2.5 GICv3架构
12.3 异常向量表配置
12.3.1 ARMv7中Monitor模式的异常向量表
12.3.2 ARMv8中EL3阶段的异常向量表
12.3.3 OP-TEE异常向量的配置
12.4 OP-TEE的线程向量表
12.5 全局handle变量的初始化
12.6 ARMv7 Monitor对FIQ事件的处理
12.7 ARMv8 EL3阶段对FIQ事件的处理
12.8 OP-TEE对FIQ事件的处理12.9 OP-TEE对IRQ事件的处理
12.10 小结
第13章 OP-TEE对TA操作的各种实现
13.1 创建会话在OP-TEE中的实现
13.1.1 静态TA的创建会话操作
13.1.2 动态TA的创建会话操作
13.2 调用TA命令操作在OP-TEE中的实现
13.2.1 静态TA的调用命令操作的实现
13.2.2 动态TA的调用命令操作实现
13.3 关闭会话操作在OP-TEE中的实现
13.3.1 静态TA的关闭会话操作
13.3.2 动态TA的关闭会话操作
13.4 小结
第14章 OP-TEE的内存和缓存管理
14.1 物理内存和缓存数据的硬件安全保护
14.1.1 内存设备安全区域的隔离
14.1.2 MMU和缓存中数据的安全隔离
14.2 ARM核对内存的访问
14.2.1 ARM核获取内存数据的过程
14.2.2 获取缓存数据的过程
14.2.3 缓存和TLB中条目的一致性
14.3 OP-TEE对内存区域的管理
14.3.1 OP-TEE中内存区域的类型
14.3.2 内存区域编译设置
14.4 MMU的初始化和映射页表
14.4.1 MMU的初始化入口函数14.4.2 物理地址到虚拟地址表的建立
14.4.3 MMU转换页表的创建
14.4.4 MMU寄存器配置
14.5 OP-TEE内存安全权限检查
14.6 系统的共享内存
14.6.1 共享内存的配置
14.6.2 OP-TEE驱动与OP-TEE之间的共享
内存
14.6.3 OP-TEE内核空间与用户空间之间的
共享内存
14.7 数据是否需要写入Cache
14.8 小结
第15章 OP-TEE中的线程管理
15.1 OP-TEE中的线程
15.2 线程状态切换
15.2.1 Free态到Active态的实现
15.2.2 Active态到Suspend态的实现
15.2.3 Suspend态到Active态的实现
15.2.4 Active态到Free态的实现
15.3 线程运行时的资源
15.3.1 线程数据结构体
15.3.2 OP-TEE分配的内核栈
15.3.3 线程运行于用户空间的资源
15.3.4 tee_ta_session结构体
15.4 线程运行时资源的使用关系
15.5 OP-TEE中线程的调度15.6 线程的死锁
15.6.1 死锁的原理
15.6.2 防止死锁
15.7 小结
第16章 OP-TEE的系统调用
16.1 OP-TEE系统调用的作用
16.2 OP-TEE系统调用的实现
16.2.1 系统调用的整体流程
16.2.2 系统调用的定义
16.2.3 系统调用表tee_sv_syacall_table
16.3 小结
第17章 OP-TEE的IPC机制
17.1 IPC机制的作用
17.2 IPC机制的原理
17.3 IPC的实现
17.3.1 TA调用其他TA的实现
17.3.2 TA调用系统服务和安全驱动的实现
17.3.3 TA对密码学系统服务的调用实现
17.3.4 对SE功能模块进行操作的系统服务
17.3.5 加载TA镜像的系统服务
17.4 小结
第四篇 应用开发篇
第18章 TA镜像的签名和加载
18.1 TA镜像文件的编译和签名
18.1.1 TA镜像文件的编译
18.1.2 对TA镜像文件的签名18.2 TA镜像的加载
18.2.1 REE侧获取TA镜像文件的内容
18.2.2 加载TA镜像的RPC请求
18.2.3 RPC请求的发送
18.2.4 读取TA镜像文件内容到共享内存
18.3 TA镜像合法性的验证
18.3.1 验证TA镜像合法性使用的RSA公钥
的产生和获取
18.3.2 TA镜像文件合法性的检查
18.4 加载TA镜像到OP-TEE的用户空间
18.5 TA运行上下文的初始化
18.6 小结
第19章 OP-TEE中的密码学算法
19.1 算法使用示例
19.1.1 示例代码获取和集成
19.1.2 板级编译文件的修改
19.1.3 通用编译文件的修改
19.1.4 编译运行
19.2 OP-TEE中的SHA算法
19.2.1 TA中使用SHA算法的实现
19.2.2 SHA算法实现接口说明
19.3 OP-TEE中的AES算法
19.3.1 TA中使用AES算法的实现
19.3.2 AES算法实现接口说明
19.4 OP-TEE中的RSA算法
19.4.1 TA中使用RSA算法的实现19.4.2 RSA算法实现接口说明
19.5 小结
第20章 OP-TEE的安全存储
20.1 安全存储简介
20.2 安全存储使用示例
20.2.1 示例代码获取和集成
20.2.2 板级编译文件的修改
20.2.3 通用编译文件的修改
20.2.4 编译运行
20.3 安全存储功能使用的密钥
20.3.1 安全存储密钥
20.3.2 可信应用的存储密钥
20.3.3 文件加密密钥
20.4 安全文件、dirf.db文件的数据格式和操
作过程
20.4.1 dirf.db文件和安全文件的格式
20.4.2 安全存储功能中使用的重要结构体
20.4.3 安全存储中的文件节点组成
20.4.4 查询安全文件中的特定数据块
20.5 安全存储文件的创建
20.5.1 安全存储软件框架
20.5.2 dirf.db文件的创建
20.5.3 安全文件的创建
20.6 安全文件的打开操作
20.6.1 安全文件的打开
20.6.2 打开dirf.db文件并建立节点树20.6.3 安全文件在datatee目录下的文件编
号
20.6.4 打开特定安全文件
20.7 安全文件的读写操作
20.7.1 安全文件中数据的读取
20.7.2 安全文件中数据的写入
20.8 安全文件中数据的加解密
20.8.1 各种类型数据的组成及作用
20.8.2 元数据的加密
20.8.3 数据块区域的加密策略
20.9 小结
第21章 可信应用及客户端应用的开发
21.1 TA及CA的基本概念
21.2 GP标准
21.3 GP标准对TA属性的定义
21.4 GP标准定义的接口
21.4.1 GP定义的客户端接口
21.4.2 GP定义的内部接口
21.5 TA和CA的实现
21.5.1 建立CA和TA的目录结构
21.5.2 CA代码的实现
21.5.3 TA代码的实现
21.6 TA和CA的集成
21.6.1 CA和TA的Makefile的修改
21.6.2 OP-TEE中comm.mk和xxx.mk文件的
修改21.7 TA和CA的调试
21.8 TA和CA的使用
21.9 小结
第22章 安全驱动的开发
22.1 安全设备的硬件安全隔离
22.2 OP-TEE中安全驱动的框架
22.2.1 系统服务层
22.2.2 驱动层
22.2.3 驱动文件在源代码中的位置
22.3 安全驱动的开发过程和示例
22.3.1 示例代码获取和集成
22.3.2 驱动实现
22.3.3 添加系统服务
22.3.4 添加系统调用
22.3.5 测试使用的TA和CA
22.4 安全驱动示例的测试
22.5 小结
第23章 终端密钥在线下发系统
23.1 密钥在线下发系统的框架
23.2 密钥在线下发的数据包格式
23.3 密钥在线下发系统示例
23.3.1 示例代码获取和集成
23.3.2 板级编译文件的修改
23.3.3 通用编译文件的修改
23.3.4 编译运行
23.4 离线工具的使用23.5 小结
第24章 基于OP-TEE的在线支付系统
24.1 在线支付系统的基本框架
24.2 可信通信通道
24.3 数据交互协议
24.3.1 数据头部区域
24.3.2 数据区域
24.3.3 电子签名区域
24.3.4 交互数据包的格式
24.4 在线支付系统示例的实现
24.4.1 第一次握手请求
24.4.2 第二次握手数据的解析
24.4.3 第三次握手请求
24.4.4 支付请求
24.4.5 支付反馈
24.5 示例的集成
24.5.1 示例代码的获取和集成
24.5.2 板级编译文件的修改
24.5.3 通用编译文件的修改
24.5.4 编译运行
24.5.5 示例支持的命令说明
24.5.6 服务器端工具
24.6 组包操作嵌入内核
24.7 支付系统与生物特征的结合
24.8 小结
第25章 TEE可信应用的使用领域25.1 在线支付
25.2 数字版权保护
25.3 身份验证
25.4 其他领域
术语表推荐序
近年来随着指纹支付的盛行,如支付宝、微信
支付等,可信执行环境(Trusted Execution
Environment,TEE)被广泛应用在手机、平板电脑
等移动终端设备中。尤其是近年来谷歌对系统安全
问题越来越重视,可信执行环境已成为谷歌提升系
统安全性的重要技术之一,包含为人熟知的
keymaster、gatekeeper等,未来在Android P上还会
引入基于TUI(Trusted User Interface)衍生的
Confirmation UI,这将会为使用者提供更好的安全
体验。
可信执行环境是一个典型的软硬件协同合作的
概念,基于ARM的TrustZone技术为系统提供资源
的物理隔离,将系统执行环境区隔为安全区域和非
安全区域。开发者通过使用安全操作系统(secure
OS)提供的API开发更多的可信应用来实现特定的
安全功能。系统的安全是环环相扣的信任链,从设
备开机的安全引导到安全操作系统的安全性验证,一直到软件开发者开发的软件安全性验证,每层相
扣,而可信执行环境为可信应用提供了一个基础且
可信任的执行环境。
未来TEE的发展方向是多元的,TEE的应用也会进入更多的产业,除了目前大热的指纹识别之
外,系统也会引入更多的生物识别技术,如虹膜与
人脸识别,从摄像头获取图像到识别演算的整个过
程都会在TEE中完成。此外TUI也是重要的方向之
一,使用者如何确认所见即所支付,确认的支付金
额或转账账号不会被别人攻击或修改,都是相当重
要的安全需求。除了移动终端设备之外,车载系统
和IoT设备也都有对应的安全需求,因此在可遇见
的未来,TEE将会被广泛应用到不同领域、不同的
电子设备中。
此外,安全应用的开发者如何将安全应用广泛
部署到不同的设备中,以及如何安全升级它们也相
当重要。当发现了软件漏洞,如何第一时间更新安
全应用并避免版本回滚的攻击,是系统安全的一个
重要议题,目前商用TEE的生态、安全应用的签名
密钥都掌握在设备制造商手中,而安全应用的独立
在线下发和更新,将是未来的重要技术发展方向。
机缘巧合,我认识峰云已经有相当久的时间
了,他对TEE的了解相当深入,也相当用心地完成
了该书,遇到有疑问与不理解的地方,他会想方设
法地找出答案,他的专业与用心深受大家的肯定与
赞赏。本书涵盖了TEE的硬件和软件知识,通过
OP-TEE开源项目的协助,读者可以通过理论与实
践的结合,深入理解TEE的原理、设计与应用。期望通过本书的出版,能够让更多人了解与接触TEE
的相关知识,进而发现更多的应用场景,享受更多
的安全服务,让未来的生活在因为科技更方便的同
时,使用者的隐私与安全也能得到保护。
邱国政(Koshi)
Trustonic中国OEM经理前言
早在2014年,投资过Facebook、Skype、Twitter等的风投公司创始人安德森就说:“移动正
在吞噬这个世界”(mobile is eating the world)。这
毫不夸张,全球范围内移动设备的数量已经超过了
世界人口的总和。在如今信息化技术高速发展的时
代,人们的生活越来越离不开智能手机,越来越多
的业务从原先复杂的流程演变到现在只需要简单地
在手机上按几个按键。技术是一把双刃剑,总能给
人带来难以想象的便利,但便利总是伴随着用户隐
私的泄漏、身份认证的滥用等一系列的安全风险。
据著名安全漏洞报告机构FreeBuf 2017年度移动应
用程序安全漏洞与数据泄漏状况报告指出,多达
88%的金融类App存在内存敏感数据泄漏问题,娱
乐类移动应用程序更是安全漏洞的重灾区,社交类
App被仿冒的概率比其他类别平均高出10倍以上。
如何保障移动设备的安全,提高安全认证程序的可
靠性,一直是近几年的热门话题。
由嵌入式处理器最大的设计商ARM公司提出
的硬件虚拟化扩展技术TrustZone,发展到现在已经
有十余年的光景,如今已成为智能手机平台不可或
缺的部分。从Android 7.0开始,谷歌就明确表示,Android设备上有关生物特征(指纹、虹膜等)识别的方案一定要基于可信执行环境(Trust
Execution Environment,TEE)来实现。TEE就是基
于TrustZone技术建立的具有更高安全级别的可信执
行环境,运行在TEE环境下的应用称为可信应用程
序(Trusted Application,TA)。随着TEE可信应用
开发的API的普及,国内越来越多的手机厂商开始
集成TEE以及相关的可信应用。TEE环境的提供商
也越来越多,从先前国外的Trustonic TEE、高通
QSEE到现在国内的豆荚、华为、瓶钵等,可以说
TEE的技术开发门槛在降低,应用热度在提高。在
众多TEE产品方案中,有一个优秀的开源方案逐渐
进入人们的视野,那就是OP-TEE。OP-TEE(Open
Platform Trusted Execution Environment)由ST-
Ericsson创建,由STMicroelectronics维护,2014年
ARM的开源社区Linaro将OP-TEE方案开源。截至
目前,OP-TEE一直是Linaro社区在维护的核心安全
项目之一。目前看来,进入TEE领域最好的方式就
是学习成熟的OP-TEE方案。作者便是在学习OP-
TEE的过程中完成了本书,旨在为后继的入门者扫
除一些障碍。
本书组织结构
本书将采取由浅入深的方式介绍TrustZone技术
的原理、OP-TEE的整体架构及其主要功能模块的
原理,同时介绍如何基于OP-TEE进行可信应用、客户端应用(Client Application,CA)、安全驱动
等功能的开发。
本书主要分为四篇,总计25章,各篇的主要内
容分别如下。
·第一篇,基础技术篇(第1章~第4章),包
含TrustZone技术的背景和实现原理、系统基本框架
以及OP-TEE环境的搭建。
·第二篇,系统集成篇(第5章~第9章),分
析OP-TEE在REE和TEE中各个组件的作用和联系,对于有一定嵌入式以及LinuxAndroid开发经验的读
者,该篇实质上给将OP-TEE集成到基于
ARMv7ARMv8处理器的开发平台打下基础。
·第三篇,OP-TEE内核篇(第10章~第17
章),包含OP-TEE内核的中断处理、线程管理和
通信等主要功能的实现原理,使读者对TEE OS的
架构设计有进一步认识。
·第四篇,应用开发篇(第18章~第25章),介绍基于OP-TEE在加密、解密、安全存储等方面
的实际应用,以及如何开发基于OP-TEE的可信应
用程序。如果对OP-TEE有一定了解的读者希望通
过实践开发来了解TEE的工作原理,可以直接从应用开发篇学习。
OP-TEE的代码量远没有Linux内核大,但其涉
及的设计之复杂、模块之丰富也不是本书能完全涵
盖的。我们的初衷是希望通过本书对重要模块的代
码和流程进行分析,使读者对OP-TEE的架构有整
体的认识,之后看到其他部分也能做到举一反三。
本书的主要代码均引用自GitHub上OP-TEE开
源项目的源代码(链接:https:github.comOP-
TEEoptee_os),作者在翻译了一些代码英文注释
的基础上根据自己的理解对部分代码补充了更多的
注释。如果读者对书中代码的中文注释有疑问,可
参考上述链接中的原始代码和注释。另外,OP-
TEE也有详细的文档资料(https:github.comOP-
TEEoptee_ostreemasterdocumentation),强烈建
议英文基础好的读者结合本书和官方文档来学习。
如发现本书有纰漏和错误,或者需要改进之处,希
望读者不吝指出。
本书特色
俗话说,基础打不牢,学问攀不高。本书采取
自下而上的方式从硬件的角度介绍了TrustZone技
术,并结合源代码逐步剖析了基于TrustZone技术的
OP-TEE实现。在技术深度上,本书从入门者的角度出发由浅入深,从最基础的开发环境的搭建到最
终的OP-TEE OS的内部实现都进行了介绍;从内容
易读性上来讲,本书提供了基础的示例代码和各种
算法的使用示例,并给出了所有示例的源代码链接
及操作的实验步骤。相信读者通过边学习边实践的
方式阅读完本书后,能够掌握TrustZone技术的基础
原理和使用OP-TEE进行实际的应用开发。
由于任何TEE方案的源代码都属于芯片厂商的
商业机密,外界无法一览各TEE方案的实现原理,且TrustZone也是最近几年才被正式商用的,所以网
上的资料较少。本书是作者基于多年的工作积累并
对实际工作过程中遇见的问题进行整理后形成的。
本书读者对象
·手机、嵌入式系统和芯片开发者及技术支持
人员;
·手机和嵌入式系统安全与可信应用(支付系
统、多媒体及身份识别等)开发人员;
·相关专业安全技术研究者和大专院校学生;
·广大关心安全技术的爱好者。致谢
从萌生写本书的念头到最终完成初稿共花了一
年零三个月,一路走来历经波折,其间获得了许多
朋友和老师的帮助,感恩这一切。感谢他们曾经的
陪伴和咖啡,让我有勇气将本书写完。
感谢邱国政(Koshi)的指导以及他为本书拟
定的大纲,感谢他在写作期间提供的耐心的释疑解
惑,并在初稿完成后挤出宝贵的时间审阅全稿,提
出建设性的修改意见。
感谢段富刚师兄在写作初期的建议、支持和鼓
励,以及在后期审稿阶段给出的修改意见,因您一
语,才使我尝试将博文整理成书稿,使内容更加完
善和系统。
感谢樊鹏对本书稿件的审阅及结合自身在NXP
车载芯片上集成OP-TEE的实际工作经验对本书提
供的宝贵建议。
感恩家人在写作期间给予的理解和支持。感恩
沈雪亮和林先贤曾经的教导,感谢王佞姐平时的关
心,感谢黄诚、龚强强、徐贵友等好友在我撰写本
书期间给予的无私帮助和真切鼓励。感谢孟庆洋博士、黄冕博士、王子亮总监、尉
鲁飞师兄对本书内容的认可和推荐。感谢邓仰东老
师和朱捷编辑在审稿期间给予的非常有益的建议和
指导,让本书的内容更加丰富和严谨。
感谢张星茹在我考研时提供的帮助,感恩大学
那么一帮人,十年友谊一直未变。
感谢OP-TEE开源项目组的各位大牛,正是你
们的分享精神赋予了开发者涉足TrustZone这片神秘
领地和一探TEE具体实现原理的机会。
最后仅以此书纪念自己曾经的三十年,感恩生
命中遇见的每一个人、大学同学(于洋、郭成飞、吼哥、涛哥、胖胖、石头、王健等一帮兄弟)、研
究生同学(刘峰、孙登高、夏轩、刘智、陈耀
闯),以及所有的朋友,是你们的出现让我过去的
三十年不曾遗憾。第一篇 基础技术篇
第1章 可信执行环境
第2章 ARM的TrustZone技术
第3章 ARM可信固件
第4章 OP-TEE运行环境的搭建及编译第1章 可信执行环境
1.1 系统存在的安全问题
随着移动通信和互联网技术的飞速发展,智能
设备在各个领域扮演着越来越重要的角色。据统
计,在2017年,中国使用智能手机上网的用户数已
达6亿之多。此外,无人驾驶、物联网、网络电视
等也都与智能设备相关,或者本身就是智能设备,它们都会用到操作系统。然而由于一些黑客能够破
解智能设备的root权限,进而盗取用户数据或其他
关键信息,造成用户数据的泄露或滥用。其次,如
果用户的车载系统被黑客获取控制权限,其人身安
全将无从保障。因此手机互联网领域、电视领域、物联网领域以及车载领域的安全越来越显得重要。
再者,智能设备上各种应用不断涌现,若开发
人员在开发这些应用时没有针对安全进行加固保
护,则黑客可能会利用这些应用本身固有的安全漏
洞获取智能设备操系统的root权限,轻松截获用户
的敏感数据。鉴于此,如何保障智能设备的安全变
得越来越重要。
那么,如何消除甚至杜绝这类威胁呢?除了提
高系统被破解的难度之外,最好还要在系统中提供一个相对可信赖的运行环境,使用户的关键数据或
应用在这个相对可信赖的环境中使用和运行。这样
一来,即便系统被攻破,入侵者也无法直接获取用
户的重要信息,用户的信息安全也就实现了,这就
是可信执行环境(Trusted Execution Environment,TEE)的主要作用和理念。1.2 TEE如何保护数据安全
为了给移动设备提供一个安全的运行环境,ARM从ARMv6的架构开始引入了TrustZone技术。
TrustZone技术将中央处理器(Central Processing
Unit,CPU)的工作状态分为了正常世界状态
(Normal World Status,NWS)和安全世界状态
(Secure World Status,SWS)。支持TrustZone技
术的芯片提供了对外围硬件资源的硬件级别的保护
和安全隔离。当CPU处于正常世界状态时,任何应
用都无法访问安全硬件设备,也无法访问属于安全
世界状态下的内存、缓存(Cache)以及其他外围
安全硬件设备。
TEE基于TrustZone技术提供可信运行环境,还
为开发人员提供了应用程序编程接口(Application
Programming Interface,API),以方便他们开发实
际应用程序。
在整个系统的软件层面,一般的操作系统(如
Linux、Android、Windows等)以及应用运行在正
常世界状态中,TEE运行在安全世界状态中,正常
世界状态内的开发资源相对于安全世界状态较为丰
富,因此通常称运行在正常世界状态中的环境为丰
富执行环境(Rich Execution Environment,REE),而可信任的操作系统以及上层的可信应用
(Trusted Application,TA)运行于安全世界状
态,运行在安全世界状态中的系统就是前文提到的
TEE。
对CPU的工作状态区分之后,处于正常世界状
态中的Linux即使被root也无法访问安全世界状态中
的任何资源,包括操作安全设备、访问安全内存数
据、获取缓存数据等。这很像一个保险箱,不管保
险箱的外在环境是否安全,其内部的物件都有足够
的安全性。这是因为CPU在访问安全设备或者安全
内存地址空间时,芯片级别的安全扩展组件会去校
验CPU发送的访问请求的安全状态读写信号位
(Non-secure bit,NS bit)是0还是1,以此来判定
当前CPU发送的资源访问请求是安全请求还是非安
全请求。而处于非安全状态的CPU将访问指令发送
到系统总线上时,其访问请求的安全状态读写信号
位都会被强制设置成1,表示当前CPU的访问请求
为非安全请求。而非安全请求试图去访问安全资源
时会被安全扩展组件认为是非法访问的,于是就禁
止其访问安全资源,因此该CPU访问请求的返回结
果要么是访问失败,要么就是返回无效结果,这也
就实现了对系统资源硬件级别的安全隔离和保护。
在真实环境中,可以将用户的敏感数据保存到
TEE中,并由可信应用(Trusted Application,TA)使用重要算法和处理逻辑来完成对数据的处理。当
需要使用用户的敏感数据做身份验证时,则通过在
REE侧定义具体的请求编号(IDentity,ID)从TEE
侧获取验证结果。验证的整个过程中用户的敏感数
据始终处于TEE中,REE侧无法查看到任何TEE中
的数据。对于REE而言,TEE中的TA相当于一个黑
盒,只会接受有限且提前定义好的合法调用,而至
于这些合法调用到底是什么作用,会使用哪些数
据,做哪些操作在REE侧是无法知晓的。如果在
REE侧发送的调用请求是非法请求,TEE内的TA是
不会有任何的响应或是仅返回错误代码,并不会暴
露任何数据给REE侧。1.3 现有TEE解决方案
TEE是一套完整的安全解决方案,主要包含正
常世界状态的客户端应用(Client Application,CA)、安全世界状态的可信应用,可信硬件驱动
(Secure Driver,SD)以及可信内核系统(Trusted
Execution Environment Operation System,TEE
OS),其系统配置、内部逻辑、安全设备和安全资
源的划分是与CPU的集成电路(Integrated Circuit,IC)设计紧密挂钩的,使用ARM架构设计的不同
CPU,TEE的配置完全不一样。国内外针对不同领
域的CPU也具有不同的TEE解决方案。
国内外各种TEE解决方案一般都遵循
GP(Global Platform)规范进行开发并实现相同的
API。GP规范规定了TEE解决方案的架构以及供TA
开发使用的API原型,开发者可以使用这些规定的
API开发实际的TA并能使其正常运行于不同的TEE
解决方案中。1.3.1 智能手机领域的TEE
智能手机领域的芯片厂商众多,国外有高通
(Qualcomm)、三星(Samsung)、LG,国内有
展讯、联发科(MediaTek)、威盛电子(VIA)、华为海思(Hisilicon)等,目前手机厂商和芯片厂
商支持的TEE解决关系如图1-1所示。图1-1 TEE解决方案关系
各家TEE解决方案的内部操作系统的逻辑会不一样,但都能提供GP规范规定的API,对于二级厂
商或TA开发人员来说接口都是统一的。这些TEE解
决方案在智能手机领域主要用于实现在线支付(如
微信支付、支付宝支付)、数字版权保护(DRM、Winevine Level 1、China DRM)、用户数据安全保
护、安全存储、指纹识别、虹膜识别、人脸识别等
其他安全需求。这样可以降低用户手机在被非法
root之后带来的威胁。
Google规定在Android M之后所有的Android设
备在使用指纹数据时都需要用TEE来进行保护,否
则无法通过Google的CTS认证授权,另外Android也
建议使用硬件Keymaster和gatekeeper来强化系统安
全性。1.3.2 智能电视领域的TEE
当前的智能电视领域大多是使用Android系统
来实现的,为保护二级厂商的视频源数据以及各厂
家用户会员权益不被盗取,需要使用TEE来实现数
字版权保护、会员鉴权、用户账号信息保护等安全
功能,而TEE方案一般都是由电视芯片厂商提供
的,且所有的TEE源代码都不对外公开,即使是二
级厂商也无法获取到TEE的源代码。在我国的智能
电视领域,智能电视芯片主要有两家:星辰半导体
(Mstar)和华为海思,两家厂商使用的TEE方案都
不一样。
Mstar早期的TEE方案是在CPU的一个类似于单
片机的核上运行Nuttx系统作为TEE OS来实现TEE
方案的,但最新的Mstar芯片已经改用OP-TEE方案
来实现TEE解决方案。
华为海思的安全操作系统(Secure Operating
System,Secure OS)是按照GP规范自主研发的
TEE解决方案,其手机芯片和智能电视芯片都是使
用这个TEE方案。华为海思的TEE增加了权限校验
功能(类似于白名单机制),即在使用华为海思的
TEE方案提供的API实现特定安全功能的TA时,需
要将调用该TA对应的CA接口的进程或者服务的相关信息提前注册到TEE后方能正常使用,否则会导
致调用失败。1.3.3 IoT领域及其他领域的TEE
物联网(Internet of Thing,IoT)领域和车载
系统领域将会是未来TEE方案使用的另外一个重要
方向,大疆无人机已经使用TEE方案来保护无人机
用户的私人数据、航拍数据以及关键的飞控算法。
ARM的M系列也开始支持TrustZone技术,如何针
对资源受限的IoT设备实现TEE也是未来TEE的重要
发展方向之一。
而在车载领域NXP芯片已经集成OP-TEE作为
TEE方案,MediaTek的车载芯片也已集成了
Trustonic的TEE方案,相信在车载系统领域TEE也
将渐渐普及。1.4 为什么选择OP-TEE
本书主要是介绍OP-TEE的实现原理,OP-TEE
是由非营利的开源软件工程公司Linaro开发的,从
git上可以获取OP-TEE的所有源代码,且OP-TEE支
持的芯片也越来越多,相信未来OP-TEE将有可能
是TEE领域的Linux,并得到更加广泛的运用。
OP-TEE是按照GP规范开发的,支持QEMU、Hikey(Linaro推广的96Board系列平台之一,使用
Hisilicon处理器)以及其他通用的ARMv7ARMv8
平台,开发环境搭建方便,便于开发者开发自有的
上层可信应用,且OP-TEE提供了完整的软件开发
工具包(Software Development Kit,SDK),方便
编译TA和CA。OP-TEE遵循GP规范,支持各种加
解密和电子签名验签算法以便实现DRM、在线支
付、指纹和虹膜识别功能。OP-TEE也支持在芯片
中集成第三方的硬件加解密算法。除此之外,在
IoT和车载芯片领域也大都使用OP-TEE作为TEE解
决方案。
OP-TEE由Linaro组织负责维护,安全漏洞补丁
更新和代码迭代速度较快,系统的健壮性也越来越
好,所以利用OP-TEE来研究TrustZone技术的实现
并开发TA和CA将会是一个很好的选择。本书涉及的内核源代码使用的是OP-TEE 2.4版
本,书中所有的示例都在最新版本中测试通过。第2章 ARM的TrustZone技术
2.1 TrustZone技术
为提高系统的安全性,ARM早在ARMv6架构
中就引入了TrustZone技术[1]
,且在ARMv7和
ARMv8中得到增强,TrustZone技术能提供芯片级
别对硬件资源的保护和隔离,当前在手机芯片领域
已被广泛应用。
[1] TrustZone硬件需求文档:lcu14-
500armtrustedfirmware-140919105449-
phpapp02.pdf;TrustZone白皮书:PRD29-GENC-
009492C_TrustZone_security_whitepaper.pdf。2.1.1 片上系统硬件框架
一个完整的片上系统(System on Chip,SoC)
由ARM核、系统总线、片上RAM、片上ROM以及
其他外围设备组件构成。只有支持TrustZone技术的
ARM核配合安全扩展组件,才能为整个系统提供芯
片硬件级别的保护和隔离。如图2-1所示是一个支
持TrustZone的SoC的硬件框图。
支持TrustZone技术的ARM核在运行时将工作
状态划分为两种:安全状态和非安全状态。当处理
器核处于安全状态时只能运行TEE侧的代码,且具
有REE侧地址空间的访问权限。当处理器核处于非
安全状态时只能运行REE侧的代码,且只能通过事
先定义好的客户端接口来获取TEE侧中特定的数据
和调用特定的功能。
系统通过调用安全监控模式调用(secure
monitor call,smc)指令实现ARM核的安全状态与
非安全状态之间的切换。而ARM核对系统资源的访
问请求是否合法,则由SoC上的安全组件通过判定
ARM核发送到SoC系统总线上的访问请求中的安全
状态读写信号位(Non-secure bit,NS bit)来决
定。只有当ARM核处于安全状态(NS bit=0)时发
送到系统总线上的读写操作才会被识别为安全读写操作,对应TEE侧的数据资源才能被访问。反之,当ARM核处于非安全状态(NS bit=1)时,ARM核
发送到系统总线上的读写操作请求会被作为非安全
读写操作,安全组件会根据对资源的访问权限配置
来决定是否响应该访问请求。这也是TrustZone技术
能实现对系统资源硬件级别的保护和隔离的根本原
因。
图2-1 SoC硬件框2.1.2 ARMv7架构的TrustZone技术
ARMv7架构中使用了TrustZone技术的系统软
件层面的框图如图2-2所示。
图2-2 ARMv7系统软件框架
在ARMv7架构中CPU在运行时具有不同的特权
等级,分别是PL0(USR)、PL1(FIQIRQ、SYS、ABT、SVC、UND和MON)以及
PL2(Hyp),即ARMv7架构在原有七种模式之上
扩展出了Monitor模式和Hyp模式。Hyp模式是ARM
核用于实现虚拟化技术的一种模式。系统只有在
Monitor模式下才能实现安全状态和非安全状态的切
换。当系统在REE侧或者TEE侧运行时,系统执行
smc(安全监控模式调用)指令进入Monitor模式,通过判定系统SCR寄存器中对应的值来确定请求来
源(REETEE)以及发送目标(REETEE),相关
寄存器中的值只有当系统处于安全态时才可以更
改,关于安全状态与非安全状态之间的切换过程,在本书第10章中将进行详细介绍。2.1.3 ARMv8架构的TrustZone技术
在ARMv8架构中改用执行等级(Execution
Level,EL)EL0~EL3来定义ARM核的运行等级,其中EL0~EL2等级分为安全态和非安全态。
ARMv8架构与ARMv7架构中ARM核运行权限的对
应关系如图2-3所示。
图2-3 ARMv7v8运行权限对比
ARMv7和ARMv8架构下特权等级和工作模式
的对应关系分别如表2-1所示。
表2-1 ARMv7和ARMv8架构下各模式对应关系ARMv7架构中的PL0(USR)对应ARMv8架构
中的EL0,PL1(SVCABTIRQFIQUNDSYS)对
应ARMv8架构中的EL1,ARMv7架构中的Hyp模式
对应ARMv8架构中的EL2,而ARMv7架构中的
Mon(Monitor)则对应于ARMv8架构中的EL3。
ARMv8架构同样也是使用安全监控模式调用
指令使处理器进入EL3,在EL3中运行的代码负责
处理器安全状态和非安全状态的切换,其中关于
TEE和REE切换的处理方式与ARMv7架构中Monitor
模式下的处理方式类似,本书第10章将结合实际代
码进行详细分析。2.2 ARM安全扩展组件
TrustZone技术之所以能提高系统的安全性,是
因为对外部资源和内存资源的硬件隔离。这些硬件
隔离包括中断隔离、片上RAM和ROM的隔离、片
外RAM和ROM的隔离、外围设备的硬件隔离、外
部RAM和ROM的隔离等。实现硬件层面的各种隔
离,需要对整个系统的硬件和处理器核做出相应的
扩展。这些扩展包括:
·对处理器核的虚拟化,也就是将AMR处理器
的运行状态分为安全态和非安全态。
·对总线的扩展,增加安全位读写信号线。
·对内存管理单元(Memory Management
Unit,MMU)的扩展,增加页表的安全位。
·对缓存(Cache)的扩展,增加安全位。
·对其他外围组件进行了相应的扩展,提供安
全操作权限控制和安全操作信号。2.2.1 AXI总线上安全状态位的扩展
为了支持TrustZone技术,控制处理器在不同状
态下对硬件资源访问的权限,ARM对先进可扩展接
口(Advanced eXtensible Interface,AXI)系统总线
进行了扩展。在原有AXI总线基础上对每一个读写
信道增加了一个额外的控制信号位,用来表示当前
的读写操作是安全操作还是非安全操作,该信号位
称为安全状态位(NS bit)或者非安全状态位
(Non-Secure bit)。
·AWPROT[1]:总线写事务——低位表示安全
写事务操作,高位表示非安全写事务操作。
·ARPROT[1]:总线读事务——低位表示安全
读事务操作,高位表示非安全读事务操作。
当主设备通过总线发起读写操作时,从设备或
者外围资源同时也需要将对应的PROT控制信号发
送到总线上。总线或者从设备的解码逻辑必须能够
解析该PROT控制信号,以便保证安全设备在非安
全态下不被非法访问。所有的非安全主设备必须将
安全状态位置成高位,这样就能够保证非安全主设
备无法访问到安全从设备。如果一个非安全主设备
试图访问一个安全从设备,将会在总线或者从设备上触发一个错误操作,至于该错误如何处理就依赖
于从设备的处理逻辑和总线的配置。通常这种非法
操作最终将产生一个SLVERR(slave error)或者
DECERR(decode error)。2.2.2 AXI-to-APB桥的作用
TrustZone同样能够保护外围设备的安全,例如
中断控制、时钟、IO设备,因此Trust-Zone架构还
能用来解决更加广泛的安全问题。比如一个安全中
断控制器和安全时钟允许一个非中断的安全任务来
监控系统,能够为DRM提供可靠的时钟,能够为用
户提供一个安全的输入设备从而保证用户密码数据
不会被恶意软件窃取。
AMBA3规范包含了一个低门数、低带宽的外
设总线,被称作外设总线(Advanced Peripheral
Bus,APB),APB通过AXI-to-APB桥连接到系统
总线上。而APB总线并不具有安全状态位,为实现
APB外设与TrustZone技术相兼容,APB-to-AXI桥将
负责管理APB总线上设备的安全。APB-to-AXI桥会
拒绝不匹配的安全事务设置,并且不会将该事务请
求发送给外设。2.2.3 TrustZone地址空间控制组件
TrustZone地址空间控制组件(TrustZone
Address Space Controller,TZASC)[1]
是AXI总线上
的一个主设备,TZASC能够将从设备全部的地址空
间分割成一系列的不同地址范围。在安全状态下,通过编程TZASC能够将这一系列分割后的地址区域
设定成安全空间或者是非安全空间。被配置成安全
属性的区域将会拒绝非安全的访问请求。
使用TZASC主要是将一个AXI从设备分割成几
个安全设备,例如off-Soc、DRAM等。ARM的动态
内存控制器(Dynamic Memory Controller,DMC)
并不支持安全和非安全分区的功能。如果将DMC接
到TZASC上,就能实现DRAM支持安全区域和非安
全区域访问的功能。需要注意的是,TZASC组件只
支持存储映射设备对安全和非安全区域的划分与扩
展,但不支持对块设备(如EMMC、NAND flash
等)的安全和非安全区域的划分与扩展。图2-4所
示为使用TZASC组件的例子。图2-4 TZASC组件示意
[1] TZASC文档:
DDI0431C_tzasc_tzc380_r0p1_trm.pdf。2.2.4 TrustZone内存适配器组件
TrustZone内存适配器组件(TrustZone Memory
Adapter,TZMA)[1]
允许对片上静态内存(on-SoC
Static Memory)或者片上ROM进行安全区域和非安
全区域的划分。TZMA支持最大2MB空间的片上静
态RAM的划分,可以将2MB空间划分成两个部
分,高地址部分为非安全区域,低地址部分为安全
区域,两个区域必须按照4KB进行对齐。分区的具
体大小通过TZMA的输入信号R0SIZE来控制,该信
号来自TZPC的输出信号TZPCR0SIZE。即通过编程
TZPC可以动态地配置片上静态RAM或者ROM的大
小。使用TZMA组件的链接框图如图2-5所示。
图2-5 使用TZMA组件的链接示意
[1] TZMA文档:
cycle_models_BP141_TZMA_User_Guide_v9_1_0_DUI1083A_en.pdf。2.2.5 TrustZone保护控制器组件
TrustZone保护控制器组件(TrustZone
Protection Controller,TZPC)[1]
是用来设定
TZPCDECPORT信号和TZPCR0SIZE等相关控制信
号的。这些信号用来告知APB-to-AXI对应的外设是
安全设备还是非安全设备,而TZPCR0SIZE信号用
来控制TZMA对片上RAM或片上ROM安全区域大
小的划分。TZPC包含三组通用寄存器
TZPCDECPROT[2:0],每组通用寄存器可以产生8
种TZPCDECPROT信号,也就是TZPC最多可以将
24个外设设定成安全外设。TZPC组件还包含一个
TZPCROSIZE寄存器,该寄存器用来为TZMA提供
分区大小信息。TZPC组件的接口示意如图2-6所
示。
当上电初始化时,TZPC的TZPCDECROT寄存
器中的位会被清零,同时TZPCR0SIZE寄存器会被
设置成0x200,表示接入到TZMA上的片上RAM或
者ROM的安全区域大小为2MB。通过修改TZPC的
寄存器配置的值可实现用户对资源的特定配置。
TZPC的使用例子如图2-7所示。图2-6 TZPC组件接口示意
图2-7 TZPC使用示例
[1] TZPC文档(BP147):
DTO0015_primecell_infrastructure_amba3_tzpc_bp147_to.pdf。2.2.6 TrustZone中断控制器组件
在支持TrustZone的SoC上,ARM添加了
TrustZone中断控制器(TrustZone Interrupt
Controller,TZIC)[1]。TZIC的作用是让处理器处
于非安全态时无法捕获到安全中断。TZIC是第一级
中断控制器,所有的中断源都需要接到TZIC上。
TZIC根据配置来判定产生的中断类型,然后决定是
将该中断信号先发送到非安全的向量中断控制器
(Vector Interrupt Controller,VIC)后以nIRQ信号
发送到处理器,还是以nTZICFIQ信号直接发送到
处理器。图2-8所示为TZIC在SoC中的使用示意。
图2-8 TZIC在SoC中的使用示意
通过对TZIC的相关寄存器进行编程,可对
TZIC进行配置并设定每个接入到TZIC的中断源的中断类型。TZIC具有众多寄存器,细节说明可以参
考相关ARM的文档。在TZIC中用来设置中断源类
型的寄存器为TZICIntSelect,如果TZICIntSelect中
的某一位被设置成1,则该相应的中断源请求会被
设置成快速中断请求(Fast Interrupt Request,FIQ)。如果某一位被设置成0,则该中断源的中断
请求会被交给VIC进行处理。如果VIC的IntSelect将
获取到的中断源设置成FIQ,那么该中断源会被再
次反馈给TZIC进行处理。
[1] TZIC文档:
DTO0013B_tzic_sp890_r0p0_to.pdf。2.2.7 Cache和MMU的扩展
在支持TrustZone的SoC上,会对MMU进行虚
拟化,使得寄存器TTBR0、TTBR1、TTBCR在安
全状态和非安全状态下是相互隔离的,因此两种状
态下的虚拟地址转换表是独立的。
存放在MMU中的每一条页表描述符都会包含
一个安全状态位,用以表示被映射的内存是属于安
全内存还是非安全内存。虚拟化的MMU共享转换
监测缓冲区(Translation Lookaside Buffer,TLB),同样TLB中的每一项也会打上安全状态位
标记,只不过该标记是用来表示该条转换是正常世
界状态转化的还是安全世界状态转化的。
Cache也同样进行了扩展,Cache中的每一项都
会按照安全状态和非安全状态打上对应的标签,在
不同的状态下,处理器只能使用对应状态下的
Cache。2.3 TrustZone技术对资源隔离的实现
ARM处理器核的虚拟化和资源隔离是
TrustZone实现安全需求的根本。支持TrustZone的
处理器核具有虚拟化,也即将一个物理核分成安全
状态和非安全状态。当处理器处于非安全状态时,只能访问属于非安全的外设和内存,而不能访问安
全的资源;当处理器处于安全态时,处理器既可以
访问安全资源,也可以访问非安全的资源,只有当
处理器核为安全世界状态时才可能发出PROT的安
全访问信号。2.3.1 中断源的隔离
在原来的ARM芯片中,使用VIC来对外部中断
源进行控制和管理,支持TrustZone后,ARM提出
了TZIC组件,在芯片设计时,该组件作为一级中断
源控制器,控制所有的外部中断源,通过编程TZIC
组件的相关寄存器来设定哪个中断源为安全中断源
FIQ,而未被设定的中断源将会被传递给VIC进行
处理。一般情况下VIC会将接收到的中断源设定成
普通中断请求(Interrupt Request,IRQ),如果在
VIC中将接收到的中断源设定成FIQ,则该中断源
会被反馈给TZIC组件,TZIC组件会将安全中断源
送到安全世界状态中进行处理。2.3.2 片上RAM和片上ROM的隔离
芯片内部存在小容量的RAM或者ROM,以供
芯片上电时运行芯片ROM或者存放芯片自身相关的
数据。TrustZone架构对该部分也进行了隔离操作。
隔离操作通过使用TZMA和TZPC组件来实现。
TZMA用来将片上RAM或者ROM划分成安全
区域和非安全区域,安全区域的大小则由接入的
TZPCR0SIZE信号来决定。而TZPCR0SIZE的值可
以通过编程TZPC组件中的TZPCR0SIZE寄存器来实
现。
当处理器核访问片上RAM或者ROM时,TZMA会判定访问请求的PROT信号是安全操作还
是非安全操作,如果处理器发出的请求为非安全请
求而该请求又尝试去访问安全区域时,TZMA就会
认为该请求为非法请求。这样就能实现片上RAM和
ROM的隔离,达到非安全态的处理器核无法访问片
上安全区域的RAM和ROM。2.3.3 片外DRAM的隔离
一个完整的系统必然会有片外RAM,对片外
RAM的隔离是通过TZASC组件实现的,ARM本身
的DMC可以将DRAM分割成不同的区域,这些区域
是没有安全和非安全分类。将DMC与TZASC相连
后再挂到总线上,通过对TZASC组件进行编程可以
将DRAM划分成安全区域和非安全区域。当主设备
访问DRAM时,除需要提供物理地址之外,还会发
送PROT信号。TZASC组件首先会判定主设备需要
访问的DARM地址是属于安全区域还是非安全区
域,然后再结合接收到的PROT信号来判定该次访
问是否有效。如果PROT信号为非安全访问操作,且访问的DRAM地址属于安全区域,则TZASC就不
会响应这次访问操作,这样就能实现DRAM中安全
区域和非安全区域的隔离。2.3.4 外围设备的隔离
其他外围设备都会挂载到APB总线上,然后通
过AXI-to-APB桥连接到AXI总线上,AXI-to-APB结
合TZPC组件的TZPCDECROT的值及访问请求的
PROT信号来判定该访问是否有效。当处理器需要
访问外围设备时,会将地址和PROT信号发送到
AXI总线上。
AXI-to-APB桥会对接收到的请求进行解析,获
取需要访问的所需外围设备,然后通过查询
TZPCDECROT的值来判断外设的安全类型,再根
据PROT信号就能判定该请求的安全类型。如果该
请求是非安全请求,但需要访问的外围设备属于安
全设备,则AXI-to-APB会判定该访问无效。
通过对TZPC中的TZPCDECROT寄存器进行编
程能够设置外设的安全类型,从而做到外设在硬件
层面的隔离。2.4 小结
本章介绍了TrustZone的原理以及在ARMv7和
ARMv8架构下TrustZone技术实现的差异。
TrustZone对系统实现了硬件隔离,将系统资源划分
成安全和非安全两种类型,同时在系统总线上增加
安全读写信号位,通过读取安全读写信号位电平来
确定当前处理器的工作状态,从而判断是否具有该
资源的访问权限。因此,TrustZone从硬件级别实现
了对系统资源的保护。第3章 ARM可信固件
3.1 为什么使用ATF
ARM可信任固件(ARM Trusted Firmware,ATF)是由ARM官方提供的底层固件,该固件统一
了ARM底层接口标准,如电源状态控制接口
(Power Status Control Interface,PSCI)、安全启
动需求(Trusted Board Boot Requirements,TBBR)、安全世界状态(SWS)与正常世界状态
(NWS)切换的安全监控模式调用(secure monitor
call,smc)操作等。ATF旨在将ARM底层的操作统
一使代码能够重用和便于移植。3.2 ATF的主要功能
ATF的源代码共分为bl1、bl2、bl31、bl32、bl33部分,其中bl1、bl2、bl31部分属于固定的固
件,bl32和bl33分别用于加载TEE OS和REE侧的镜
像。整个加载过程可配置成安全启动的方式,每一
个镜像文件在被加载之前都会验证镜像文件的电子
签名是否合法。
ATF主要完成的功能如下:
·初始化安全世界状态运行环境、异常向量、控制寄存器、中断控制器、配置平台的中断。
·初始化ARM通用中断控制器(General
Interrupt Controller,GIC)2.0版本和3.0版本的驱动
初始化。
·执行ARM系统IP的标准初始化操作以及安全
扩展组件的基本配置。
·安全监控模式调用(Secure Monitor Call,smc)请求的逻辑处理代码(Monitor模式EL3)。
·实现可信板级引导功能,对引导过程中加载的镜像文件进行电子签名检查。
·支持自有固件的引导,开发者可根据具体需
求将自有固件添加到ATF的引导流程中。3.3 ATF与TEE的关系
为规范和简化TrustZone OS的集成,在ARMv8
架构中,ARM引入ATF作为底层固件并开放了源
码,用于完成系统中BootLoader、Linux内核、TEE
OS的加载和启动以及正常世界状态和安全世界状态
的切换。ATF将整个启动过程划分成不同的启动阶
段,由BLx来表示。例如,TEE OS的加载是由ATF
中的bl32来完成的,安全世界状态和正常世界状态
之间的切换是由bl31来完成的。在加载完TEE OS之
后,TEE OS需要返回一个处理函数的接口结构体
变量给bl31。当在REE侧触发安全监控模式调用指
令时,bl31通过查询该结构体变量就可知需要将安
全监控模式调用指令请求发送给TEE中的那个接口
并完成正常世界状态到安全世界状态的切换。3.4 小结
在ARMv8架构中,如果系统需要支持TEE,则
几乎都必须使用由ARM提供的ATF作为底层固件。
关于ATF如何管理BootLoader、TEE OS、Linux内
核以及各个阶段镜像的加载过程和跳转过程,本书
第6章将结合实际代码详细分析。第4章 OP-TEE运行环境的搭建及编
译
OP-TEE是开源的TEE解决方案,任何人都可
从github库中获取OP-TEE的源代码,本章主要包括
如何从github中获取OP-TEE的源代码、如何搭建运
行环境以及整个OP-TEE工程的编译过程。本书以
QEMU作为运行平台,Hikey或者其他平台的编译
和使用方式与QEMU平台类似。4.1 获取OP-TEE代码并搭建运行环境
OP-TEE的开发环境推荐使用Linux进行搭建,可在Windows系统中使用虚拟机创建一个Ubuntu系
统或者将计算机系统换成Ubuntu系统。4.1.1 OP-TEE开发环境的搭建
OP-TEE的开发环境依赖于各种基本库,在
Ubuntu系统中直接运行如下指令就可安装OP-TEE
开发环境需要使用的各种依赖库。
sudo apt-get install android-tools-adb android-tools-fastboot autoconf automake bc bison build-essential cscope curl device-tree-compiler expect flex ftp-upload gdisk iasl libattr1-dev libc6:i386 libcap-dev libfdt-dev libftdi-dev libglib2.0-dev libhidapi-dev libncurses5-dev libpixman-1-dev libssl-dev libstdc++6:i386 libtool libz1:i386 make mtools netcat python-crypto python-serial python-wand unzip uuid-dev xdg-utils xterm xz-utils zlib1g-dev4.1.2 获取OP-TEE的源代码
在系统中创建用于存放OP-TEE的目录“open-
tee”,读者可以根据自己的喜好替换目录的名字,创建完目录后就需要建立OP-TEE的repo(关于repo
或者git的使用,请读者自行查找资料了解),初始
化完repo后,使用repo sync指令就可从Github上获
取到OP-TEE的源代码,操作如下:
mkdir open-tee 创建目录
cd open-tee 切换到创建的目录
repo init -u https: github.comOP-TEEmanifest.git -m default.xml --repo-url=git:codeaurora.orgtoolsrepo.git -b 2.6.0 初始化repo
repo sync 开始获取OP-TEE源代码
如果在执行repo sync时出现“remote:
Repository not found”的报错提示,则需要修改open-
tee.repo目录下的manifest.xml文件,将该文件中所
有project域中的“.git”删除,也可通过如下指令进行
修改:
sed -i s\.gitg .repomanifest.xml
修改完成之后,重新执行repo sync来获取OP-
TEE的代码。manifest.xml文件中包含的就是整个工
程所需的单独git仓库的链接[1]。待代码同步完成后,为方便后续章节中各示例
代码的集成,需要使用如下指令将相关的代码回滚
到标签为3.0.0的版本。
cd optee_client
git checkout 3.0.0
cd optee_test
git checkout 3.0.0
cd optee_benchmark
git checkout 3.0.0
cd optee_examples
git checkout 3.0.0
cd optee_os
git checkout 3.0.0
[1] OP-TEE工程源代码链接:https:github.comOP-
TEE;OP-TEE内核代码链接:
https:github.comOP-TEEoptee_os;OP-TEE client
端代码链接:https:github.comOP-
TEEoptee_client;OP-TEE test代码链接:
https:github.comOP-TEEoptee_test;OP-TEE工程
使用的Linux Kernel代码链接:
https:github.comlinaroswglinux;OP-TEE 中使用
的QEMU 软件源代码链接:
https:github.comlinaro-swgqemu。4.1.3 获取编译OP-TEE的toolchain
OP-TEE工程的源代码下载完成后,下一步就
需要获取编译OP-TEE时使用的toolchain,切换到源
代码的build目录,执行如下指令:
cd build 切换到build目录
make -f toolchain.mk toolchains 下载toolchain
查看toolchain.mk文件可知,执行make指令之
后,系统会去下载toolchains的tar包,包括32位和64
位的编译链接工具,下载完成后会进行解压操作。
执行完make后,可发现OP-TEE源代码的根目录下
会多出一个toolchains的目录,该目录中存放的就是
编译OP-TEE工程时使用的所有编译链接工具。4.1.4 编译QEMU
OP-TEE源代码的build目录是用于编译整个工
程的编译目录,该目录包含各种平台的编译配置文
件。在QEMU平台运行时需选择qemu.mk文件进行
编译,具体操作如下:
cd build 切换到build目录
make -f qemu.mk all 编译工程
当然,读者也可将qemu.mk文件链接成
Makefile,然后在build目录下直接执行make all就能
编译QEMU平台的工程。
编译完成后将会在OP-TEE的根目录下生成一
个out目录,该目录中存放的就是使用QEMU方式运
行OP-TEE时需要的镜像和其他相关文件。
如果在编译的过程中出现“ImportError:No
Module named wand.image”的报错提示,说明系统
没有安装Python的Wand包,此时在shell中运行如下
指令即可:
pip install: Wand4.1.5 运行OP-TEE
工程编译完成之后,如果要运行OP-TEE,则
需要进入build目录中执行make run-only语句,具体
操作如下:
cd build 切换到build目录
make -f qemu.mk run-only 启动QEMU并运行OP-TEE
如果读者已将qemu.mk文件链接成了
Makefile,则直接在build目录中执行make run-only
即可。qemu.mk文件中的run-only目标首先会启动两
个分别属于安全世界状态和正常世界状态的
terminal,用于显示OP-TEE和Linux内核的日志数
据,然后加载OP-TEE镜像与Linux的镜像及其文件
系统。4.1.6 运行xtest和optee_example_hello_world
通过使用make run-only启动OP-TEE后,可在
启动的正常世界状态对应的terminal中执行
Optee_example_hello_world或者xtest指令来检查OP-
TEE是否正常运行。
Optee_example_hello_world是一个简单的CA编
译而成的二进制可执行文件,执行该可执行文件后
会调用OP-TEE中对应的TA,并执行一些简单的打
印操作,输出的日志信息可在安全世界状态的
terminal中查看。
xtest是OP-TEE自带的一个测试使用的CA可执
行文件。该CA执行后将会调用TA中的各种功能,包括检查基本算法接口、安全存储接口等。4.2 运行CA和TA示例
OP-TEE中自带的TA和CA都保存在
optee_examples目录中,那么如何添加自己开发的
TA和CA程序到OP-TEE中并运行呢?本节将对此进
行介绍。为减少对编译方面的理解,本节将结合实
际的TA和CA示例介绍详细的操作步骤。4.2.1 示例代码的获取和集成
本节所用示例的所有源代码可从gitHub上获
取,读者可使用如下指令获取到源代码,示例包中
有对应的补丁,读者直接合入补丁就可将该示例集
成到OP-TEE中,该示例的gitHub链接如下:
git clone https:github.comshuaifengyunoptee_my_test.git
获取到示例代码之后,切换到如下build目录
下,然后使用git apply命令合入补丁文件后就可将
该示例集成到OP-TEE,合入补丁的操作步骤如
下:
1)将示例代码中的
optee_mytest_common_3.0.0.patch文件和
optee_mytest_qemu_3.0.0.patch文件复制到build目录
中。
2)切换到build目录,使用如下命令合入补
丁:
git apply optee_mytest_common_3.0.0.patch
git apply optee_mytest_qemu_3.0.0.patch将补丁合入之后就可使用make-f qemu.mk all编
译整个工程,然后使用make-f qemu.mk run-only来
启动OP-TEE,在启动的正常世界状态的终端执行
my_test命令就能实现该示例的CA对TA的调用。示
例代码的运行效果如图4-1所示。
图4-1 optee_my_test示例运行4.2.2 目录和文件创建
从gitHub上获取到本章使用的示例代码后,host存放的是CA的代码,ta目录存放的是TA部分的
代码。本章提供的示例代码的目录结构如下:
├── Android.mk
├── build_ta_mytest_qemu.sh
├── doc
│ ├── close_session_and_finalize_context.msc
│ ├── invoke_command.msc
│ ├── Makefile
│ └── open_session.msc
├── host
│ ├── main.c
│ ├── Makefile
│ └── my_test_ca.h
├── Makefile
├── optee_mytest_common_3.0.0.patch
├── optee_mytest_qemu_3.0.0.patch
├── README.md
└── ta
├── Android.mk
├── include
│ ├── my_test_handle.h
│ └── my_test_ta.h
├── Makefile
├── my_test.c
├── my_test_handle.c
├── sub.mk
└── user_ta_header_defines.h
目录中文件的作用说明如下:·Android.mk文件:Android系统中编译整个TA
和CA时使用;
·build_ta_mytest_qemu.sh文件:单独编译TA和
CA使用的脚本文件;
·hostmain.c文件:CA的源代码;
·hostMakefile文件:编译CA时使用的makefile
文件;
·hostmy_test_ca.h文件:UUID、command ID
的宏定义;
·taMakefile文件:编译TA时使用的makefile文
件;
·tamy_test.c文件:主要是存放TA部分代码的
入口处理函数,CA的command请求最终会被
TA_InvokeCommandEntryPoint函数处理;
·tamy_test_handle.c文件:存放相应CA的
command请求的功能函数;
·tasub.mk文件:定义该TA中需要被编译的
source code;·tauser_ta_header_defines.h文件:定义UUID等
相关宏;
·taincludemy_test_handle.h文件:定义了该TA
需要使用的类型;
·taincludemy_test_ta.h文件:定义了UUID的宏
以及与CA对应的command ID宏;
·optee_mytest_common_3.0.0.patch文件:将该
TA和CA集成到OP-TEE时buildcommon.mk文件使
用的补丁文件;
·optee_mytest_qemu_3.0.0.patch文件:将该TA
和CA集成到OP-TEE时builldqemu.mk文件使用的补
丁文件。4.2.3 CA端代码的修改
若读者需添加新的功能,可按照GP规范调用
REE侧的相关接口,编辑完CA端的代码后就需要修
改host目录下的Makefile文件,将需要编译进CA的
文件添加到Makefile中,主要是修改hostMakefile文
件中的OBJS变量和BINARY变量,其中OBJS变量
存放的是需要编译到CA的目标文件或者库文件,BINARY是编译完成后的可执行文件的名字。注
意,在CA的头文件中需要定义UUID和command ID
的宏,且定义的内容需要与TA中的UUID和
command ID一致,否则执行CA后将会导致调用失
败,关于UUID的值并没有特殊的要求,只需按照
其格式定义一个唯一的字符串即可。4.2.4 TA端代码的修改
ta目录中存放的是该TA的源代码、makefile文
件和头文件,其中ta目录中必须存在一个
user_ta_header.h文件,该文件在编译TA镜像或者是
整个工程时会被使用到。在该文件中会定义UUID
的宏、该TA运行的堆栈空间的大小以及版本信
息。在TA的头文件中需要定义UUID的宏和
command ID,且必须与CA中定义的一样,否则CA
端将无法调用该TA中对应的操作。修改taMakefile
文件,将该文件中BINARY变量的值修改成与CA中
相同的UUID值。
修改完成后运行build_ta_mytest_qemu.sh脚本
就能单独编译CA和TA,如果出现错误,则根据提
示进行修改,编译成功后会在ta目录中生成与UUID
值一样的elf文件,在host目录中将会生成与
hostMakefile文件中BINARY变量的值一样的文
件。4.2.5 TA和CA在OP-TEE的集成
单独编译TA和CA成功后,就需要将该TA和
CA集成到OP-TEE的工程中去,需要修改OP-TEE源
代码中build目录下的qemu.mk文件和common.mk文
件。
在buildqemu.mk文件中增加该TA的目标和依
赖关系,以本示例为例,对buildqemu.mk文件的修
改如下。
1)增加optee_my_test的编译目标内容:
optee_my_test
Optee_mytest: optee_mytest-common
Optee_mytest-clean: optee_mytest-clean-common
2)将optee_mytest目标和optee_mytest-clean-
common目标添加到all中:
all: bios-qemu qemu soc-term optee-examples optee_my_test
clean: bios-qemu-clean busybox-clean linux-clean optee-os-clean optee-client-clean qemu-clean soc-term-clean check-clean optee_my_test-clean optee-examples-clean在buildcommon.mk文件中需要增加编译该TA
和CA的路径变量等信息,添加的内容如下。
1)增加TA和CA的代码路径:
OPTEE_MYTEST_PATH ?= (ROOT)optee_my_test
2)增加TA和CA的common目标:
optee_my_test
OPTEE_MYTEST_COMMON_FLAGS ?= HOST_CROSS_COMPILE=(CROSS_COMPILE_NS_USER) TA_CROSS_COMPILE=(CROSS_COMPILE_S_USER) TA_DEV_KIT_DIR=(OPTEE_OS_TA_DEV_KIT_DIR) TEEC_EXPORT=(OPTEE_CLIENT_EXPORT)
.PHONY: optee_my_test-common
optee_my_test-common: optee-os optee-client
(MAKE) -C (OPTEE_MYTEST_PATH) (OPTEE_MYTEST_COMMON_FLAGS)
OPTEE_MYTEST_CLEAN_COMMON_FLAGS ?= TA_DEV_KIT_DIR=(OPTEE_OS_TA_DEV_KIT_DIR)
.PHONY: optee_my_test-clean-common
optee_my_test-clean-common:
(MAKE) -C (OPTEE_MYTEST_PATH) (OPTEE_MYTEST_CLEAN_COMMON_FLAGS) clean
3)将该TA和CA添加到filelist-tee-common目标
的依赖关系中:
filelist-tee-common: optee-client xtest optee-examples optee_my_test
4)添加clean操作的依赖关系:optee-os-clean-common: xtest-clean optee-examples-clean optee_my_test-clean
5)在filelist-tee-common中添加TA和CA镜像需
要被打包到文件系统中的操作:
@echo optee_mytest >> (fl)
@if [ -e (OPTEE_MYTEST_PATH)hostmy_test ]; then echo file binmy_test (OPTEE_MYTEST_PATH)hostmy_test 755 0 0 >> (fl); echo file liboptee_armtz9269fadd-99d5-4afb-a1dc-ee3e9c61b04c.ta (OPTEE_MYTEST_PATH)ta9269fadd-99d5-4afb-a1dc-ee3e9c61b04c.ta 444 0 0 >> (fl); fi4.3 OP-TEE源代码结构
OP-TEE的源代码包含运行OP-TEE时需要的所
有软件源代码,OP-TEE工程编译完成后,整个源
代码的目录结构如下:
├── basicAlg_use
├── bios_qemu_tz_arm
├── build
├── busybox
├── gen_rootfs
├── linux
├── optee_benchmark
├── optee_client
├── optee_examples
├── optee_my_test
├── optee_os
├── optee_test
├── out
├── qemu
├── secStor_test
├── soc_term
└── toolchains
OP-TEE各子目录中的代码功能说明如下。
(1)bios_qemu_tz_arm目录
在QEMU平台中运行tz_arm的BIOS代码,启动
的最初阶段会被使用到,用来加载Linux内核镜
像、OP-TEE OS镜像、rootfs并启动Linux内核和OP-TEE OS。
(2)build目录
OP-TEE工程的编译目录,包含各种mk文件和
相关配置文件,其中common.mk文件是工程的通用
mk文件,不同的CPU架构有不同的mk与之相对
应,编译工程时可使用make-f的方式指定编译哪个
板级的OP-TEE。
(3)busybox目录
busybox的源代码,编译生成制作rootfs所需要
的文件和目录。
(4)gen_rootfs目录
存放制作rootfs时使用的相关脚本和配置文
件。
(5)linux目录
Linux内核代码,在drivertee目录下存放的是
OP-TEE在REE侧的驱动,任何在Linux用户空间调
用CA的接口都会经过OP-TEE的REE侧驱动处理之
后再转发到TEE侧。(6)optee_benchmark目录
OP-TEE运行的性能测试工具,只保存CA端的
代码,TA部分的代码保存在OP-TEE OS中,作为
静态TA集成到OP-TEE OS中。
(7)optee_client目录
包含CA程序调用的用户空间的接口库
(libteec)的源代码。其中tee_supplicant目录中的
代码会被编译成一个可执行文件,该可执行文件在
Linux启动时作为守护进程常驻在系统中,该守护
进程的主要作用是响应和处理来自TEE侧的RPC请
求,这些RPC请求包括:加载TA镜像、对文件系统
的操作、对SQL的操作、对EMMC RPMB的操作、网络通信的socket操作等。
(8)optee_examples目录
示例代码,目录下包含OP-TEE提供的各种示
例的TA和CA的所有代码,启动后,在REE对应的
terminal中执行optee_example_hello_world命令后,会调用optee_example_hello_world的TA的逻辑,TA
根据接收到的command ID在OP-TEE中执行对应的
操作。
(9)optee_os目录存放OP-TEE OS的源代码和相关文档,编译完
成之后,该目录将会生成OP-TEE的镜像文件。
(10)optee_test目录
opentee的测试程序xtest的源代码,主要用来测
试OP-TEE中提供的各种算法的逻辑并提供其他测
试功能。
(11)out目录
编译结果的输出目录(该目录编译完成之后才
会生成)。
(12)qemu目录
QEMU源代码,如果编译的是qemu.mk,编译
时将会使用到该目录。
(13)soc_term目录
在使用QEMU运行OP-TEE时,gnome-terminal
命令会启动终端,用于建立启动的两个terminal的
端口监听,方便OP-TEE OS的log和Linux的log分别
输出到对应的terminal中。
(14)toolchains目录编译时需要使用的编译工具链,在build目录下
执行make–f toolchai.mk toolchains后将会生成该目
录。4.4 OP-TEE编译
整个OP-TEE工程的编译是一个庞大的过程,牵扯到目标的依赖关系,本节以qemu.mk板级为
例,分析使用QEMU方式运行OP-TEE时的全部编
译过程和目标依赖关系。4.4.1 编译目标的依赖关系
编译OP-TEE工程时各主要目标的依赖关系如
下:
all
├── bios-qemu
│ ├── optee-os
│ │ └── optee-os-common
│ └── update_rootfs
│ └── update_rootfs-common
│ ├── busybox
│ │ └── busybox-common
│ │ └── linux
│ │ └── linux-common
│ │ └── linux-defconfig
│ └── filelist-tee
│ └── filelist-tee-common
│ ├── f1
│ │ └── filelist-tee.txt
│ ├── optee-client
│ │ ├── common
│ │ └── optee-client
│ └── xtest
│ │ ├── optee-client
│ │ └── xtest-common
│ │ └── optee-os
│ │ └──optee-os-common
│ └── optee-examples
│ └── build_socterm
│ └── helloworld-common
│ ├──optee-client
│ └──optee-os
├── benchmark-app
├── qemu
│ └── build_qemu
└── soc-term
│ └── build_socterm └── optee-examples
│ └── build_socterm
│ └── helloworld-common
│ ├──optee-client
│ └──optee-os
QEMU目标会切换到QEMU目录,并获取
QEMU的配置文件,然后执行make命令来编译
QEMU目标。
soc-term目标会编译soc-term目录,生成一个
soc-term的可执行文件,用于启动两个terminal。
bios-qemu目标依赖于update_rootfs和optee-os,update_rootfs和optee-os编译完成之后会调用
biosqemu-comm宏定义的指令,该宏会编译
bios_qemu_tz_arm目录,该目录编译完成之后,会
生成启动时需要的bios镜像。
optee-os-common目标将编译optee_os目录,该
目录编译完成后将会生成tee.bin及其他的lib库文
件。
busybox目标将编译linux目录和busybox目录,生成Linux内核镜像文件和制作rootfs需要的相关文
件。
filelist-tee目标将生成tee功能相关的文件和需要被挂载到rootfs中的映射图,然后与系统的其他文
件的挂载映射关系一起保存到filelist-final.txt文件
中,用于生成filesystem.cpio.gz文件。
update_rootfs-common目标依赖于busybox和
filelist-tee目标,上述两个目标编译完成之后,将会
切换到gen_rootfs目录中,调用gen_init_cpio命令生
成在启动时需要使用的filesystem.cpio.gz文件。
Optee_examples目标包含OP-TEE提供的各种
TA部分和CA部分,编译完成之后,会生成对应的
TA镜像文件和CA的可执行文件。
optee-client目标将对optee_client目录进行编
译,生成一系列的库文件和可执行文件,库文件提
供了OP-TEE在Linux端的接口,将被所有CA调用。
tee-supplicant目标将会编译生成一个tee_supplicant
的可执行文件,该可执行文件提供了optee_os访问
文件系统的RPC接口以及加载具体的TA镜像的功
能。
xtest目标将会编译optee_test目录,生成在xtest
集合中会使用的TA镜像文件和xtest可执行文件。4.4.2 bios.bin镜像的生成过程
bios.bin镜像是启动时会被使用到的主要镜像文
件,在执行make run-only指令使用QEMU方式启动
OP-TEE时,会借助qemu-system-arm命令来启动OP-
TEE和Linux kern,并挂载Linux的rootfs。在运行
qemu-system-arm命令时,其中有一个参数为“-
bios”,该参数就是告诉QEMU使用该参数之后所带
的bios.bin来启动整个系统。
bios.bin中会包含Linux kernel的镜像、OP-TEE
OS的镜像以及rootfs。该镜像文件是在bios-qemu的
目标中编译出来的。
当bios-qemu目标的依赖目标都编译完成后,会
使用bios-qemu-common函数将Linux内核、OP-TEE
镜像、rootfs打包成bios.bin镜像文件。bios-qemu-
common函数定义在buildqemu.mk文件中,内容如
下:
define bios-qemu-common
+(MAKE) -C (BIOS_QEMU_PATH) CROSS_COMPILE=(CROSS_COMPILE_NS_USER) O=(ROOT)outbios-qemu BIOS_NSEC_BLOB=(LINUX_PATH)archarmbootzImage BIOS_NSEC_ROOTFS=(GEN_ROOTFS_PATH)filesystem.cpio.gz BIOS_SECURE_BLOB=(OPTEE_OS_BIN) PLATFORM_FLAVOR=virtEndef
执行该函数时会带入相关的编译参数,编译由
变量BIOS_QEMU_PATH定义的目录
(bios_qemu_tz_arm目录),参数说明如下:
·CROSS_COMPILE:编译时使用的编译参
数,包括编译器、cflag等;
·O:编译结果的输出目录;
·BIO_NSEC_BLOB:定义该变量,指定Linux
内核镜像的名称和路径;
·BIOS_NSEC_ROOTFS:定义该变量,指定生
成的rootfs存在的目录和cpio格式文件名;
·BIOS_SECURE_BLOB:定义该变量,指定
OP-TEE OS镜像文件名;
·PLATFORM_FLAVOR:定义该变量,设定平
台变量。
编译bios_qemu_tz_arm目录时最终会将Linux内
核镜像、OP-TEE OS镜像、rootfs转换成.o文件,然
后再将这些转换后的.o文件与其他的.o文件一起链
接成biso.bin镜像文件。Linux内核镜像文件会被放在bios.bin中名称为nsec_blob的section里。OP-TEE
os image将会被放在bios.bin中名称为secure_blob的
section里。rootfs image将会被放在bios.bin中名称为
nsec_rootfs的section里。
上述将镜像文件转换成.o文件的操作是通过
OBJCOPY带--rename-section参数来实现的,具体的
内容可以在link.mk文件中找到。
bios_qemu_tz_armbiosentry.s文件存放的就是
在启动系统时bios.bin的入口文件。4.4.3 run-only目标的执行
qemu.mk文件中的run-only目标是用来启动使用
QEMU方式运行OP-TEE的起始目标,在qemu.mk文
件中,run-only目标的定义如下:
.PHONY: run-only
run-only:
(call check-terminal)
(call run-help)
(call launch-terminal, 54320,Normal World)
(call launch-terminal, 54321,Secure World)
(call wait-for-ports, 54320,54321)
(QEMU_PATH)arm-softmmuqemu-system-arm -nographic -serial tcp:localhost:54320 -serial tcp:localhost:54321 -s -S -machine virt -machine secure=on -cpu ARM核-a15 -m 1057 -bios (ROOT)outbios-qemubios.bin (QEMU_EXTRA_ARGS)
run-only目标的内容会调用各种函数,这些函
数会在相关的makefile文件中定义,下面是相关函
数的作用说明:
(call check-terminal):
check-terminal在QEMU的工程中不会被定义,该语句不会被执行,但是在其他工程中会定义,具
体可查看buildcommon.mk文件。(call run-help):
run-help函数定义在buildcommom.mk文件中,主要用来打印出相关的启动帮助信息。
(call launch-terminal,54320,Normal
World):
执行launch-terminal,54320,Normal
World指令,启动名字为Normal World的terminal,其中launch-terminal在buildcommon.mk文件中定
义。
(call launch-terminal,54321,Secure
World):
执行功能同上,只是在重定向时将端口换成了
54321,且启动的terminal名字为Secure World。
(call wait-for-ports,54320,54321):
调用wait-for-prots函数,该函数定义在
buildcommon.mk文件中,主要功能是检查上面启
动的两个terminal使用socket方式进行通信是否正
常。
(QEMU_PATH)arm-softmmuqemu-system-arm:
该指令就是调用qemu-system-arm指令,并设定
好QEMU启动的各种参数,然后开始启动Linux与
OP-TEE,该指令完全展开之后的内容如下:
homeicyshuaidevelopteebuild..qemuarm-softmmuqemu-system-arm -nographic -serial tcp:localhost:54320 -serial tcp:localhost:54321 -s -S -machine virt -machine secure=on -cpu ARM核-a15 -m 1057 -bios homeicyshuaidevelopteebuild..outbios-qemubios.bin
-nographic:不显示图形界面。
-serial:将串口重定向到后面的参数部分。
-S:使用C来控制启动(在QEMU的console界
面输入C之后才会正式启动系统)。
-m:设定虚拟的内存大小。
-bios:指定BIOS的文件(该image中会包含
OP-TEE、Linux、rootfs的镜像文件)。
1.launch-terminal函数
launch-terminal函数的主要功能是用来启动terminal。该函数定义在buildcommon.mk文件中,具体内容如下:
define launch-terminal
@nc -z 127.0.0.1 (1) || (gnome-terminal) -t (2) -x (SOC_TERM_PATH)soc_term (1)
endef
(gnome-terminal)的定义也在common.mk文件
中,定义如下:
gnome-terminal := (shell command -v gnome-terminal 2>devnull)
调用(call launch-terminal,54320,Normal
World)等价于:
gnome-terminal -t Normal World -x (SOC_TERM_PATH)soc_term 54320
调用该函数的作用是启动一个名字为Normal
World的terminal,并且在terminal中执行soc_term
54320,soc_term就是在soc_term目录中编译出来的
可执行文件。执行soc_term 54320命令的主要作用
是将该terminal的输入和输出通过54320端口重定向
到标准输入和输出端口。
2.soc_term可执行文件soc_term可执行文件用来实现Linux和OP-TEE
的两个terminal输入和输出重定向到标准输入输出
端口,该可执行文件的源代码存放在soc_term目录
中。soc_term.c文件中的main函数定义如下:
int main(int argc, char argv[])
{
int listen_fd;
char port;
bool have_handle_telnet_option = false;
switch (argc) {
case 2:
port = argv[1];
break;
case 3:
if (strcmp(argv[1], -t) != 0)
usage;
have_handle_telnet_option = true;
port = argv[2];
break;
default:
usage;
}
save_current_termios;获取当前terminal的信息(标准输入输出的terminal配置)
listen_fd = get_listen_fd(port);建立socket机制,并监听输入的端口号
printf(listening on port %s\n, port);
if (have_handle_telnet_option) 判定是否使用telent
printf(Handling telnet commands\n);
进入loop循环,完成端口监听和输入输出的重定向
while (true) {
int fd = accept_fd(listen_fd); 开始接收建立的监听端口的信息
handle_telnet = have_handle_telnet_option;
handle_telnet_codes(-1,NULL, NULL); 为使用telent时不起作用
warnx(accepted fd %d, fd);
复制当前terminal的信息,并配置其他参数,然后调用tcsetattr函数来设定当前启动的terminal的信息
set_tty_noncanonical;
开始处理监听收到的数据,并根据对应的revent进行重定向操作,server_fd函数的注释见后续章节
serve_fd(fd);
处理完成之后关闭该fd
if (close(fd)) err(1, close);
fd = -1;
保存当前terminos的配置
restore_termios;
}
}
server_fd函数用来接收受监控的端口的数据,并执行重定向操作,代码内容和解释如下:
static void serve_fd(int fd)
{
uint8_t buf[512];
struct pollfd pfds[2];
设定pollfd参数,用于实现重定向操作
memset(pfds, 0, sizeof(pfds));
pfds[0].fd = STDIN_FILENO;
pfds[0].events = POLLIN;
pfds[1].fd = fd;
pfds[1].events = POLLIN;
while (true) {
size_t n;
获取监听事件的pfds[0]和pfds[1]中定义的事件
if (poll(pfds, 2, -1) == -1)
err(1, poll);
如果pfds[0]中的POLLIN时间触发(在该terminal的标准输入中有输入操作),则进行读取操作
if (pfds[0].revents POLLIN) {
从该terminal的标准输入端口中读取输入的数据
n = read(STDIN_FILENO, buf, sizeof(buf));
if (n == -1)
err(1, read stdin);
if (n == 0)
errx(1, read stdin EOF);
将读取到的数据写入到重定向的port捆绑的socket
if (!write_buf(fd, buf, n)) {
warn(write_buf fd);
break;
}
}
如果pfds[1]中的POLLIN时间触发(监测到该terminal的port捆绑的socket有输入流操作),则读取监测的port对应的socket句柄中的数据 if (pfds[1].revents POLLIN) {
读取与port捆绑的socket的句柄中的数据
n = read(fd, buf, sizeof(buf));
if (n == -1) {
warn(read fd);
break;
}
if (n == 0) {
warnx(read fd EOF);
break;
}
handle_telnet_codes(fd, buf, n);
将读取到的数据写入到该terminal的标准输出
if (!write_buf(STDOUT_FILENO, buf, n))
err(1, write_buf stdout);
}
}
}4.5 小结
本章主要介绍OP-TEE开发和运行环境的搭
建,并提供了一个demo,介绍如何开发自己的TA
和CA并让其成功运行在OP-TEE中。为方便读者理
解整个OP-TEE工程的实际执行流程,知道如何生
成启动OP-TEE时使用的各种镜像文件,本章特意
介绍了OP-TEE工程的编译过程以及编译过程中各
种目标的依赖关系,同时对编译过程中的重要函数
做了进一步的介绍。第二篇 系统集成篇
第5章 QEMU运行OP-TEE的启动过程
第6章 安全引导功能及ATF的启动过程
第7章 OP-TEE OS的启动过程
第8章 OP-TEE在REE侧的上层软件
第9章 REE侧OP-TEE的驱动第5章 QEMU运行OP-TEE的启动过
程
使用QEMU的方式运行OP-TEE是通过在build
目录下执行make run-only来启动的,启动过程主要
是加载bios.bin文件,并从该镜像文件中分离出
Linux内核镜像和OP-TEE镜像以及rootfs镜像,并将
rootfs作为根文件系统挂到Linux系统中。本章将介
绍系统的启动过程,并详细介绍OP-TEE的启动流
程和相关的重要启动节点。5.1 bios.bin的入口函数
使用QEMU运行OP-TEE时首先加载的是编译
生成的bios.bin镜像文件,而bios.bin镜像文件的入
口函数是在bios_qemu_tz_armbiosentry.S文件中定
义的,该文件的入口函数为_start,该文件的主要内
容如下:
.section .text.boot
定义 _start函数,设定第一条指令跳转到reset函数执行
FUNC _start , :
b reset
b . Undef
b . Syscall
b . Prefetch abort
b . Data abort
b . Reserved
b . IRQ
b . FIQ
END_FUNC _start
reset 函数
LOCAL_FUNC reset , :
read_sctlr r0
orr r0, r0, SCTLR_A
write_sctlr r0
设置中断向量表
adr r0, _start
write_vbar r0
重新设定bios在RAM中的地址
mov r0, 0
ldr r1, =__text_start
ldr r2, =__data_end
sub r2, r2, r1
复制bios.bin文件中的__text_start到__data_end到地址为0的起始RAM中
bl copy_blob
跳转到上面重新定位的bios在RAM中的地址 ldr ip, =new_loc
bx ip
new_loc:
重新设定中断向量
adr r0, _start
write_vbar r0
清空BSS段的数据
ldr r0, =__bss_start
ldr r1, =__bss_end
sub r1, r1, r0
bl zero_mem
设定堆栈空间
ldr ip, =main_stack_top;
ldr sp, [ip]
push {r0, r1, r2}
mov r0, sp
ldr ip, =main_init_sec 获取main_init_sec函数地址
blx ip 跳转到main_init_sec函数中执行,加载OP-TEE OS的image
pop {r0, r1, r2}
mov ip, r0 OP-TEE OS的入口地址
mov r0, r1 argument (address of pagable part if != 0)
blx ip 跳转到OP-TEE OS的启动地址
设置Normal World的栈
ldr ip, =main_stack_top;
ldr sp, [ip]
ldr ip, =main_init_ns 获取main_init_ns函数的地址
bx ip 跳转到main_init_ns函数,加载Linux内核的image
END_FUNC reset
复制函数
LOCAL_FUNC copy_blob , :
ldrb r4, [r0], 1
strb r4, [r1], 1
subs r2, r2, 1
bne copy_blob
bx lr
END_FUNC copy_blob
清空内存数据的函数
LOCAL_FUNC zero_mem , :
cmp r1, 0
bxeq lr
mov r4, 0
strb r4, [r0], 1
sub r1, r1, 1
b zero_mem
END_FUNC zero_memmain_init_sec函数用来将Linux内核镜像、OP-
TEE OS镜像、rootfs镜像文件加载到RAM的对应位
置,并且解析出OP-TEE OS的入口地址、Linux内
核的加载地址、rootfs在RAM中的地址和其他相关
信息。main_init_sec函数执行完成后会返回OP-TEE
OS的入口地址以及设备树(device tree,DT)的地
址,然后在汇编代码中通过调用blx指令进入OP-
TEE OS的启动。
OP-TEE启动完成后会重新进入entry.S文件中
继续执行,最终执行main_init_ns函数来启动Linux
内核,在main_init_sec函数中会设定Linux内核的入
口函数地址、DT的相关信息,main_init_ns函数会
使用这些信息来开始Linux内核的加载。
上述两个函数都定义在
bios_qemu_tz_armbiosmain.c文件中。将各种镜像
文件复制到RAM的操作都是通过解析bios.bin镜像
的对应section来实现的,通过寻找特定的section来
确定各镜像文件在bios.bin文件中的位置。5.2 OP-TEE镜像的加载和启动
启动过程中entry.S文件通过汇编调用
main_init_sec函数将optee-os镜像、Linux镜像和
rootfs加载到RAM中,并定位DT的地址信息,以备
Linux和OP-TEE启动使用,这些操作是由
main_init_sec函数进行的,该函数定义在
bios_qemu_tz_armbiosmain.c文件中,其内容如
下:
void main_init_sec(struct sec_entry_arg arg)
{
void fdt;
int r;
定义OP-TEE OS 镜像文件存放的起始地址
const uint8_t sblob_start = __linker_secure_blob_start;
定义OP-TEE OS 镜像文件存放的末端地址
const uint8_t sblob_end = __linker_secure_blob_end;
struct optee_header hdr; 存放OP-TEE OS image头的信息
size_t pg_part_size; OP-TEE OS image除去初始化头部信息的大小
uint32_t pg_part_dst; OP-TEE OS image除去初始化头部信息后在RAM中的起始地址
msg_init; 初始化uart
加载device tree 信息。在qemu工程中,并没有将device tree信息编译到Bios.bin中,而默认存放在DTB_START地址中
fdt = open_fdt(DTB_START, __linker_nsec_dtb_start,__linker_nsec_dtb_end);
r = fdt_pack(fdt);
CHECK(r < 0);
判定OP-TEE OS image的大小是否大于image header的大小
CHECK(((intptr_t)sblob_end - (intptr_t)sblob_start) <
(ssize_t)sizeof(hdr));
将OP-TEE OS image header信息复制到hdr变量中
copy_bios_image(secure header, (uint32_t)hdr, sblob_start,sblob_start + sizeof(hdr));
校验OP-TEE OS image header中的magic和版本信息是否合法
CHECK(hdr.magic != OPTEE_MAGIC || hdr.version != OPTEE_VERSION); msg(found secure header\n);
sblob_start += sizeof(hdr); 将sblob_start的值后移到除去image header的位置
CHECK(hdr.init_load_addr_hi != 0); 检查OP-TEE OS的初始化加载地址是否为零
获取OP-TEE OS除去 image header和ini操作部分代码后的大小
pg_part_size = sblob_end - sblob_start - hdr.init_size;
确定存放OP-TEE OS除去image header和init操作部分代码后存放在RAM中的地址
pg_part_dst = (size_t)TZ_RES_MEM_START + TZ_RES_MEM_SIZE - pg_part_size;
将存放OP-TEE OS除去image header和init操作部分后的内容复制到RAM中
copy_bios_image(secure paged part,pg_part_dst, sblob_start + hdr.init_size, sblob_end);
sblob_end -= pg_part_size; 重新计算sblo_end的地址,剔除page part
将pg_part_dst赋值给arg中的paged_part以备跳转执行OP-TEE OS使用
arg->paged_part = pg_part_dst;
将hdr.init_load_addr_lo赋值给arg中的entry,该地址为op-TEE OS的入口地址
arg->entry = hdr.init_load_addr_lo;
将OP-TEE OS的实际image复制到起始地址为hdr.init_load_addr_l的RAM地址中
copy_bios_image(secure blob, hdr.init_load_addr_lo, sblob_start,sblob_end);
复制kernel image、rootfs到RAM,并复制device tree到对应地址,以备被kernel使用
copy_ns_images;
将device tree的地址赋值给arg->fdt变量,以备OP-TEE OS启动使用
arg->fdt = dtb_addr;
msg(Initializing secure world\n);
}
main_init_sec函数执行后将会返回一个
sec_entry_arg的变量,该变量包含启动OP-TEE OS
的入口地址、DT的地址以及paged_table的地址。
sec_entry_arg变量将会被entry.S文件用来启动OP-
TEE OS,entry.S会将OP-TEE OS的入口地址保存在
r0寄存器中,而paged_table部分的起始地址会被保
存在r1寄存器中,将r0赋值给ip,最终entry.S文件
通过执行blx ip指令进入OP-TEE OS的入口函数中去
执行OP-TEE OS的启动。当OP-TEE OS启动完成之
后,entry.S文件会调用main_init_ns函数来启动Linux内核。待Linux内核启动完成之后,整个系统
也就启动完成。5.3 Linux内核镜像的加载和启动
entry.S文件通过调用main_init_ns函数来完成对
Linux内核的启动,该函数会调用call_kernel函数来
完成Linux内核的启动,调用call_kernel函数时传入
的参数说明如下:
kernel_entry:Linux内核在RAM中的入口地
址,该值在main_init_sec函数中通过调用
copy_ns_images函数来进行赋值。
dtb_addr:DT存放的位置,该值在
main_init_sec函数中通过调用copy_ns_images函数来
进行赋值。
rootfs_start:复制到RAM中的rootfs的起始地
址,该值在main_init_sec函数中被赋值。
rootfs_end:复制到RAM中的rootfs的末端地
址,该值在main_init_sec函数中被赋值。
call_kernel函数定义在
bios_qemu_tz_armbiosmain.c文件中,该函数的内
容和相关注释如下:typedef void (kernel_ep_func)(uint32_t a0, uint32_t a1, uint32_t a2);
static void call_kernel(uint32_t entry, uint32_t dtb,uint32_t initrd, uint32_t initrd_end)
{
定义指向Linux内核入口地址的函数指针,并将函数指针的地址指向带入参数entry的位置
kernel_ep_func ep = (kernel_ep_func)entry;
void fdt = (void )dtb; 定义device tree的地址并赋值
const char cmdline[] = COMMAND_LINE; 定义存放command line的变量并进行赋值
int r;
const uint32_t a0 = 0;
MACH_VEXPRESS see linuxarcharmtoolsmach-types
const uint32_t a1 = 2272;
获取device tree的信息
r = fdt_open_into(fdt, fdt, DTB_MAX_SIZE);
CHECK(r < 0);
设置device tree中的相关节点、initrd的起始地址、initrd的末端地址、bootargs
setprop_cell(fdt, chosen, linux,initrd-start, initrd);
setprop_cell(fdt, chosen, linux,initrd-end, initrd_end);
setprop_string(fdt, chosen, bootargs, cmdline);
r = fdt_pack(fdt);
CHECK(r < 0);
打印相关信息
msg(kernel command line: \%s\\n, cmdline);
msg(Entering kernel at 0x%x with r0=0x%x r1=0x%x r2=0x%x\n,(uintptr_t)ep, a0, a1, dtb);
带入device tree信息和其他相关参数,调用Linux内核的入口函数,进而执行Linux内核的启动
ep(a0, a1, dtb);
}
ep的值是Linux内核的入口函数指针,所以最
终调用ep(a0,a1,dtb)函数就能开始Linux内核的启
动过程。5.4 rootfs的挂载
启动Linux系统时会加载rootfs,rootfs在启动
Linux系统之前会被拷贝到相应的内存地址中,系
统在启动Linux时会告知Linux内核rootfs在内存中的
地址,Linux内核启动时会到该地址中去获取rootfs
的内容,挂载起来作为Linux系统的根文件系统使
用。5.5 OP-TEE驱动的启动
在OP-TEE工程中,OP-TEE在REE侧的驱动会
被编译到Linux内核镜像中,Linux系统在启动的过
程中会自动挂载OP-TEE的驱动,驱动挂载过程中
会创建devtee0和devteepriv0设备,其中devtee0
设备将会被REE侧的用户空间的库(libteec)使
用,devteepriv0设备将会被系统中的常驻进程
tee_supplicant使用,并且在OP-TEE驱动的挂载过程
中会建立正常世界状态与安全世界状态之间的共享
内存,用于OP-TEE驱动与OP-TEE之间的数据共
享,同时还会创建两个链表,分别用于保存来自
OP-TEE的RPC请求和发送RPC请求的处理结果给
OP-TEE。5.6 tee_supplicant的启动
tee_supplicant是Linux系统中的常驻进程,该进
程用于接收和处理来自OP-TEE的RPC请求,并将
处理结果返回给OP-TEE。来自OP-TEE的RPC请求
主要包括socket操作、REE侧文件系统操作、加载
TA镜像文件、数据库操作、共享内存分配和注册
操作等。该进程在Linux系统启动过程中被自动创
建,在编译时,该进程的启动信息会被写入
到etcinit.d文件中,而该进程的可执行文件则被保
存在文件系统的bin目录下。该进程中会使用一个
loop循环接收来自OP-TEE的远程过程调用
(Remote Procedure Call,RPC)请求,且每次获取
到来自OP-TEE的RPC请求后都会自动创建一个线
程,用于接收OP-TEE驱动队列中来自OP-TEE的
RPC请求,之所以这么做是因为时刻需要保证在
REE侧有一个线程来接收OP-TEE的请求,实现RPC
请求的并发处理。5.7 小结
本章介绍了使用QEMU的方式运行OP-TEE的
启动过程,介绍了系统是如何启动的,在启动过程
中如何加载相应的镜像文件。即使在非QEMU方式
运行OP-TEE时,OP-TEE的驱动和tee_supplicant同
样会被挂载和启动,这属于运行OP-TEE不可或缺
的一部分。第6章 安全引导功能及ATF的启动过
程
安全引导(Secure Boot)功能是指在系统的整
个启动过程中,使用链式验证电子签名的方式来验
证系统中重要镜像文件的可靠性,然后再加载镜像
文件的引导过程。安全引导功能可以保护二级厂商
系统的独立性和完整性。在ARMv8架构中ARM提
供了ARM可信固件(ATF)。Bootloader、Linux内
核、TEE OS的启动都由ATF来加载和引导。对于
ARMv8,Bootloader、Linux内核和TEE OS镜像文
件的验签工作都是在ATF中完成的。本章将介绍安
全引导功能的原理以及ATF的启动过程。6.1 安全引导的作用
安全引导可用于保证系统的完整性,防止系统
中重要镜像文件被破坏或替换。一般情况下,安全
引导需要保护系统的BootLoader镜像文件、TEE镜
像文件、Linux内核镜像文件、Recover镜像文件以
及在ARMv8中使用的ATF镜像文件。将TEE镜像文
件的加载操作加入安全引导功能中可阻止黑客通过
替换TEE镜像文件的方式来窃取被TEE保护的重要
资料。当前使用ARM芯片的系统中大部分使能了安
全引导功能,该功能对于用户的最直接感受就是,当用户非法刷入其他厂商的ROM后手机无法正常启
动,这是因为非法刷机将导致系统中的重要镜像文
件被替换,系统在启动过程中对镜像文件的电子验
签失败,如果BootLoader验证失败,则系统在进入
BootLoader阶段之前就会挂死。6.2 安全引导的原理
安全引导功能的原理就是采用链式验签的方式
启动系统,也就是在系统启动过程中,在加载下一
个阶段的镜像之前都会对需要被加载的镜像文件进
行电子验签,只有验签操作通过后,该镜像才能被
加载到内存中,然后系统才会跳转到下一个阶段继
续执行,整个验签链中的任何一环验签失败都会导
致系统挂死,系统启动过程中的第一级验签操作是
由ChipRom来完成的。只要芯片一出厂,用户就无
法修改固化在芯片中的这部分代码,因此无法通过
修改第一级验签结果来关闭安全引导功能。而且验
签操作使用的RSA公钥或者哈希值将会被保存在
OTPefuse中,该区域中的数据一般只有ChipRom和
TEE能够读取且无法被修改。RSA公钥或者哈希值
将会在产品出厂之前被写入到OTPefuse中,而且不
同厂商使用的密钥会不一样。
在谷歌的安全引导功能白皮书中提出了安全引
导功能实现方案的设计建议。谷歌建议将镜像文件
的电子签名信息和验签使用的RSA公钥保存在电子
证书中,系统在启动的过程中首先会验证电子证书
的合法性,如果验证通过则需从电子证书中获取签
名信息和RSA公钥,然后再利用它们对镜像文件进
行验证。整个验证过程就是先验证证书,验证证书通过后再去验证镜像文件的合法性。但是在实际实
现过程中,大多数芯片厂商是将签名信息与需要被
验签的镜像文件打包在一起,而RSA公钥则会被打
包到执行验证操作的镜像文件中。
不同厂商可能会对镜像文件进行加密操作,使
保存在设备中的镜像文件都是以密文的形式存在。
在启动过程中,首先会验证密文镜像文件的合法性
然后再进行解密镜像文件的操作,这些都完成后才
会将明文的镜像文件加载到内存中然后再执行跳转
操作。6.2.1 ARMv7安全引导的过程
对于安全引导功能的实现和验证过程各家芯片
公司的方案都不一样,这是由该芯片的启动流程以
及启动所需镜像文件来决定的,但都会遵循链式验
签启动的原则。ARMv7架构并没有使用ATF,系统
的启动流程与以前一样使用BootLoader来引导Linux
内核和TEE OS。安全引导的启动流程如图6-1所
示。图6-1 安全引导的启动流程
系统启动过程使用链式验签的方式进行引导,其中任何一环验签失败都会导致系统启动失败,为
防止通过替换ramdisk来修改根文件系统中的内容,一般将ramdisk与Linux内核打包在同一个镜像文件
中,而且该镜像文件需要待验签通过后才可被使
用。签名信息一般是对镜像文件的内容进行哈希计
算获取摘要后再对该摘要使用RSA私钥进行电子签
名来获得,验证时同样会计算需要被引导的镜像文
件的摘要,然后使用该摘要、签名信息以及RSA公
钥进行RSA算法的验证。6.2.2 ARMv8安全引导的过程
ARMv8架构之后ARM提供了ATF,BootLoader、TEE镜像文件、Linux内核镜像文件、recovery镜像文件都是由ATF来进行引导和加载而
不是由ChipRom来完成的。ChipRom只会去验证
ATF中bl1的合法性,后续引导过程同样也是按照链
式验签的方式进行,符合TBBR规范。读者可使用
git命令从gitHub上获取ATF的所有源代码[1]。在
ARMv8架构中整个安全引导的流程如图6-2所示。图6-2 ARMv8的Secure Boot流程
ARMv8架构中引入了ATF,同时在ATF中提供
了安全引导的功能,BootLoader镜像、Linux内核、recovery镜像和TEE OS镜像文件的签名方式都由
ATF决定。当然开发者也可以对ATF进行定制化,修改ATF中的验签过程,但是修改后的验签方案需
要符合TBBR规范。
[1] ATF的git仓库链接可参阅ATF源代码链接:
https:github.comlinaro-swgarm-trusted-firmware。6.3 ATF的启动过程
ATF的启动过程根据ARMv8的运行模式
(AArch32AArch64)会有所不同,但基本一致。
在AArch32中是不会去加载bl31而是将EL3或者
Monitor模式的运行代码保存在bl32中执行。在
AArch64中,ATF的完整启动流程如图6-3所示。
图6-3 AArch64模式的ATF启动流程
在上述启动过程中,从一个镜像跳转到另外一
个镜像文件执行的方式各不相同,以下为镜像跳转的过程和方式说明。
1.bl1跳转到bl2执行
在bl1完成了将bl2镜像文件加载到RAM中的操
作、中断向量表的设定以及其他CPU相关设定后,bl1_main函数会解析出bl2镜像文件的描述信息,获
取入口地址,并设定下一个阶段的cpu上下文。这
些操作完成之后,调用el3_exit函数来实现bl1到bl2
的跳转,进入bl2中开始执行。
2.bl2跳转到bl31执行
在bl2中将会加载bl31、bl32、bl33的镜像文件
到对应权限的内存中,并将该三个镜像文件的描述
信息组成一个链表保存起来,以备bl31启动bl32和
bl33使用。在AArch64中,bl31为EL3的执行软件,其运行时的主要功能是对安全监控模式调用
(smc)指令和中断处理,运行在ARM的Monitor模
式中。
bl32一般为TEE OS镜像文件,本章以OP-TEE
为例进行说明。
bl33为正常世界状态的镜像文件,例如uboot、EKD2等。当前该部分为BootLoader部分的镜像文
件,再由BootLoader来启动Linux内核镜像。从bl2跳转到bl31是通过带入bl31的入口点信息
作为参数,然后调用安全监控模式调用指令,触发
在bl1中设定的安全监控模式调用请求,该请求处理
完成后会将中央处理器的执行权限交给bl31,并跳
转到bl31中去执行。
3.bl31跳转到bl32执行
在bl31中会执行runtime_service_inti函数,该函
数会调用注册到EL3中所有服务的初始化函数,其
中有一个服务项就是TEE服务,该服务项的初始化
函数会将TEE OS的初始化函数赋值给bl32_init变
量,当所有服务项执行完初始化后,在bl31中会调
用bl32_init执行的函数来跳转到TEE OS中并开始执
行TEE OS的启动。
4.bl31跳转到bl33执行
当TEE-OS镜像启动完成后会触发一个ID为
TEESMC_OPTEED_RETURN_ENTRY_DONE的安
全监控模式调用,该调用是用来告知EL3 TEE OS
镜像已经完成了初始化,然后将CPU的状态恢复到
bl31_init的位置继续执行。
bl31通过遍历在bl2中记录的所有镜像信息的链
表来找到需要执行的bl33的镜像。然后通过获取到bl33镜像的信息,设定下一个阶段的CPU上下文, ......
手机安全和可信应用开发指南:TrustZone
与OP-TEE技术详解
帅峰云 黄腾 宋洋 编著
ISBN:978-7-111-60956-8
本书纸版由机械工业出版社于2018年出版,电子版
由华章分社(北京华章图文信息有限公司,北京奥
维博世图书发行有限公司)全球范围内制作与发
行。
版权所有,侵权必究
客服热线:+ 86-10-68995265
客服信箱:service@bbbvip.com
官方网址:www.hzmedia.com.cn
新浪微博 @华章数媒
微信公众号 华章电子书(微信号:hzebook)目录 推荐序
前言
致谢
第一篇 基础技术篇
第1章 可信执行环境
1.1 系统存在的安全问题
1.2 TEE如何保护数据安全
1.3 现有TEE解决方案
1.3.1 智能手机领域的TEE
1.3.2 智能电视领域的TEE
1.3.3 IoT领域及其他领域的TEE
1.4 为什么选择OP-TEE
第2章 ARM的TrustZone技术
2.1 TrustZone技术
2.1.1 片上系统硬件框架
2.1.2 ARMv7架构的TrustZone技术
2.1.3 ARMv8架构的TrustZone技术
2.2 ARM安全扩展组件
2.2.1 AXI总线上安全状态位的扩展
2.2.2 AXI-to-APB桥的作用
2.2.3 TrustZone地址空间控制组件
2.2.4 TrustZone内存适配器组件
2.2.5 TrustZone保护控制器组件
2.2.6 TrustZone中断控制器组件2.2.7 Cache和MMU的扩展
2.3 TrustZone技术对资源隔离的实现
2.3.1 中断源的隔离
2.3.2 片上RAM和片上ROM的隔离
2.3.3 片外DRAM的隔离
2.3.4 外围设备的隔离
2.4 小结
第3章 ARM可信固件
3.1 为什么使用ATF
3.2 ATF的主要功能
3.3 ATF与TEE的关系
3.4 小结
第4章 OP-TEE运行环境的搭建及编译
4.1 获取OP-TEE代码并搭建运行环境
4.1.1 OP-TEE开发环境的搭建
4.1.2 获取OP-TEE的源代码
4.1.3 获取编译OP-TEE的toolchain
4.1.4 编译QEMU
4.1.5 运行OP-TEE
4.1.6 运行xtest和
optee_example_hello_world
4.2 运行CA和TA示例
4.2.1 示例代码的获取和集成
4.2.2 目录和文件创建
4.2.3 CA端代码的修改
4.2.4 TA端代码的修改4.2.5 TA和CA在OP-TEE的集成
4.3 OP-TEE源代码结构
4.4 OP-TEE编译
4.4.1 编译目标的依赖关系
4.4.2 bios.bin镜像的生成过程
4.4.3 run-only目标的执行
4.5 小结
第二篇 系统集成篇
第5章 QEMU运行OP-TEE的启动过程
5.1 bios.bin的入口函数
5.2 OP-TEE镜像的加载和启动
5.3 Linux内核镜像的加载和启动
5.4 rootfs的挂载
5.5 OP-TEE驱动的启动
5.6 tee_supplicant的启动
5.7 小结
第6章 安全引导功能及ATF的启动过程
6.1 安全引导的作用
6.2 安全引导的原理
6.2.1 ARMv7安全引导的过程
6.2.2 ARMv8安全引导的过程
6.3 ATF的启动过程
6.3.1 ATF中bl1的启动
6.3.2 ATF中bl2的启动
6.3.3 ATF中bl31的启动
6.3.4 ATF中bl32的启动6.3.5 ATF启动过程小结
6.4 小结
第7章 OP-TEE OS的启动过程
7.1 OP-TEE镜像启动过程
7.1.1 OP-TEE OS的入口函数
7.1.2 OP-TEE的内核初始化过程
7.1.3 OP-TEE服务项的启动
7.1.4 OP-TEE驱动的挂载
7.2 ARM64位与ARM32位OP-TEE启动过程的
差异
7.3 小结
第8章 OP-TEE在REE侧的上层软件
8.1 OP-TEE的软件框架
8.2 REE侧libteec库提供的接口
8.2.1 libteec库提供的接口说明
8.2.2 CA调用libteec库中接口的流程
8.3 REE侧的守护进程——tee_supplicant
8.3.1 tee_supplicant编译生成和自启动
8.3.2 tee_supplicant入口函数
8.3.3 tee_supplicant存放RPC请求的结构体
8.3.4 tee_supplicant中的无限循环
8.3.5 tee_supplicant获取TA的RPC请求
8.3.6 TA RPC请求的解析
8.3.7 RPC请求的处理
8.3.8 回复RPC请求
8.4 各种RPC请求的处理8.4.1 加载TA镜像
8.4.2 操作REE侧的文件系统
8.4.3 操作RPMB
8.4.4 分配共享内存
8.4.5 释放共享内存
8.4.6 记录程序执行效率
8.4.7 网络套接字操作
8.5 小结
第9章 REE侧OP-TEE的驱动
9.1 OP-TEE驱动模块的编译保存
9.2 REE侧OP-TEE驱动的加载
9.2.1 设备号和class的初始化
9.2.2 optee_driver_init函数
9.2.3 挂载驱动的probe操作
9.2.4 获取切换到Monitor模式或EL3的接口
9.2.5 驱动版本和API版本校验
9.2.6 判定OP-TEE是否预留共享内存空间
9.2.7 配置驱动与OP-TEE之间的共享内存
9.2.8 分配和设置tee0和teepriv0的设备信息
结构体变量
9.2.9 tee0和teepriv0设备的注册
9.2.10 请求队列的初始化
9.2.11 使能TEE中共享内存的缓存
9.2.12 OP-TEE驱动挂载的总结
9.3 REE侧用户空间对驱动的调用过程
9.4 OP-TEE驱动中重要的结构体变量9.4.1 OP-TEE驱动的file_operation结构体变
量tee_fops
9.4.2 tee0设备的tee_driver_ops结构体变量
optee_ops
9.4.3 teepriv0设备的操作结构体变量
optee_supp_ops
9.4.4 共享驱动缓存操作变量
tee_shm_dma_buf_ops
9.5 OP-TEE驱动与OP-TEE之间共享内存的注
册和分配
9.6 libteec库中的接口在驱动中的实现
9.6.1 libteec库中的open操作
9.6.2 libteec库中的release操作
9.6.3 libteec执行get_version操作
9.6.4 libteec库中的open session操作
9.6.5 libteec库中的invoke操作
9.7 tee_supplicant接口在驱动中的实现
9.7.1 接收OP-TEE的RPC请求
9.7.2 获取OP-TEE的RPC请求
9.7.3 OP-TEE的RPC请求的返回
9.8 小结
第三篇 OP-TEE内核篇
第10章 ARM核安全态和非安全态间的切换
10.1 ARMv7基本知识
10.1.1 ARMv7运行模式扩展
10.1.2 安全状态位扩展10.1.3 重要寄存器
10.1.4 安全监控模式调用的汇编指令
10.2 Monitor模式下的处理过程
10.2.1 Monitor模式对安全监控模式调用的
处理
10.2.2 正常世界状态中触发安全监控模式
调用的处理过程
10.2.3 安全世界状态中触发安全监控模式
调用的处理过程
10.3 ARMv8基本知识
10.3.1 ARM核运行模式的新定义
10.3.2 ARMv8安全状态位扩展
10.3.3 寄存器资源
10.3.4 安全监控模式调用汇编指令
10.4 EL3的处理过程
10.4.1 ATF中EL3异常向量表的注册
10.4.2 EL3处理安全监控模式调用的流程
10.4.3 安全世界状态中触发安全监控模式
调用的处理过程
10.4.4 正常世界状态中触发安全监控模式
调用的处理过程
10.4.5 opteed_smc_handler函数
10.5 小结
第11章 OP-TEE对安全监控模式调用的处理
11.1 OP-TEE的线程向量表
11.2 ARMv7中Monitor模式对安全监控模式调用的处理
11.3 ARMv8中EL3处理安全监控模式调用的
实现
11.4 OP-TEE对快速安全监控模式调用的处理
11.5 OP-TEE对标准安全监控模式调用的处理
11.5.1 OP-TEE对RPC请求返回操作的处理
11.5.2 OP-TEE对libteec库触发的安全监控
模式调用的处理
11.6 小结
第12章 OP-TEE对中断的处理
12.1 系统的中断处理
12.2 中断控制器
12.2.1 GIC寄存器
12.2.2 ARMv7 SCR寄存器的设定
12.2.3 ARMv8 SCR寄存器的设定
12.2.4 GICv2架构
12.2.5 GICv3架构
12.3 异常向量表配置
12.3.1 ARMv7中Monitor模式的异常向量表
12.3.2 ARMv8中EL3阶段的异常向量表
12.3.3 OP-TEE异常向量的配置
12.4 OP-TEE的线程向量表
12.5 全局handle变量的初始化
12.6 ARMv7 Monitor对FIQ事件的处理
12.7 ARMv8 EL3阶段对FIQ事件的处理
12.8 OP-TEE对FIQ事件的处理12.9 OP-TEE对IRQ事件的处理
12.10 小结
第13章 OP-TEE对TA操作的各种实现
13.1 创建会话在OP-TEE中的实现
13.1.1 静态TA的创建会话操作
13.1.2 动态TA的创建会话操作
13.2 调用TA命令操作在OP-TEE中的实现
13.2.1 静态TA的调用命令操作的实现
13.2.2 动态TA的调用命令操作实现
13.3 关闭会话操作在OP-TEE中的实现
13.3.1 静态TA的关闭会话操作
13.3.2 动态TA的关闭会话操作
13.4 小结
第14章 OP-TEE的内存和缓存管理
14.1 物理内存和缓存数据的硬件安全保护
14.1.1 内存设备安全区域的隔离
14.1.2 MMU和缓存中数据的安全隔离
14.2 ARM核对内存的访问
14.2.1 ARM核获取内存数据的过程
14.2.2 获取缓存数据的过程
14.2.3 缓存和TLB中条目的一致性
14.3 OP-TEE对内存区域的管理
14.3.1 OP-TEE中内存区域的类型
14.3.2 内存区域编译设置
14.4 MMU的初始化和映射页表
14.4.1 MMU的初始化入口函数14.4.2 物理地址到虚拟地址表的建立
14.4.3 MMU转换页表的创建
14.4.4 MMU寄存器配置
14.5 OP-TEE内存安全权限检查
14.6 系统的共享内存
14.6.1 共享内存的配置
14.6.2 OP-TEE驱动与OP-TEE之间的共享
内存
14.6.3 OP-TEE内核空间与用户空间之间的
共享内存
14.7 数据是否需要写入Cache
14.8 小结
第15章 OP-TEE中的线程管理
15.1 OP-TEE中的线程
15.2 线程状态切换
15.2.1 Free态到Active态的实现
15.2.2 Active态到Suspend态的实现
15.2.3 Suspend态到Active态的实现
15.2.4 Active态到Free态的实现
15.3 线程运行时的资源
15.3.1 线程数据结构体
15.3.2 OP-TEE分配的内核栈
15.3.3 线程运行于用户空间的资源
15.3.4 tee_ta_session结构体
15.4 线程运行时资源的使用关系
15.5 OP-TEE中线程的调度15.6 线程的死锁
15.6.1 死锁的原理
15.6.2 防止死锁
15.7 小结
第16章 OP-TEE的系统调用
16.1 OP-TEE系统调用的作用
16.2 OP-TEE系统调用的实现
16.2.1 系统调用的整体流程
16.2.2 系统调用的定义
16.2.3 系统调用表tee_sv_syacall_table
16.3 小结
第17章 OP-TEE的IPC机制
17.1 IPC机制的作用
17.2 IPC机制的原理
17.3 IPC的实现
17.3.1 TA调用其他TA的实现
17.3.2 TA调用系统服务和安全驱动的实现
17.3.3 TA对密码学系统服务的调用实现
17.3.4 对SE功能模块进行操作的系统服务
17.3.5 加载TA镜像的系统服务
17.4 小结
第四篇 应用开发篇
第18章 TA镜像的签名和加载
18.1 TA镜像文件的编译和签名
18.1.1 TA镜像文件的编译
18.1.2 对TA镜像文件的签名18.2 TA镜像的加载
18.2.1 REE侧获取TA镜像文件的内容
18.2.2 加载TA镜像的RPC请求
18.2.3 RPC请求的发送
18.2.4 读取TA镜像文件内容到共享内存
18.3 TA镜像合法性的验证
18.3.1 验证TA镜像合法性使用的RSA公钥
的产生和获取
18.3.2 TA镜像文件合法性的检查
18.4 加载TA镜像到OP-TEE的用户空间
18.5 TA运行上下文的初始化
18.6 小结
第19章 OP-TEE中的密码学算法
19.1 算法使用示例
19.1.1 示例代码获取和集成
19.1.2 板级编译文件的修改
19.1.3 通用编译文件的修改
19.1.4 编译运行
19.2 OP-TEE中的SHA算法
19.2.1 TA中使用SHA算法的实现
19.2.2 SHA算法实现接口说明
19.3 OP-TEE中的AES算法
19.3.1 TA中使用AES算法的实现
19.3.2 AES算法实现接口说明
19.4 OP-TEE中的RSA算法
19.4.1 TA中使用RSA算法的实现19.4.2 RSA算法实现接口说明
19.5 小结
第20章 OP-TEE的安全存储
20.1 安全存储简介
20.2 安全存储使用示例
20.2.1 示例代码获取和集成
20.2.2 板级编译文件的修改
20.2.3 通用编译文件的修改
20.2.4 编译运行
20.3 安全存储功能使用的密钥
20.3.1 安全存储密钥
20.3.2 可信应用的存储密钥
20.3.3 文件加密密钥
20.4 安全文件、dirf.db文件的数据格式和操
作过程
20.4.1 dirf.db文件和安全文件的格式
20.4.2 安全存储功能中使用的重要结构体
20.4.3 安全存储中的文件节点组成
20.4.4 查询安全文件中的特定数据块
20.5 安全存储文件的创建
20.5.1 安全存储软件框架
20.5.2 dirf.db文件的创建
20.5.3 安全文件的创建
20.6 安全文件的打开操作
20.6.1 安全文件的打开
20.6.2 打开dirf.db文件并建立节点树20.6.3 安全文件在datatee目录下的文件编
号
20.6.4 打开特定安全文件
20.7 安全文件的读写操作
20.7.1 安全文件中数据的读取
20.7.2 安全文件中数据的写入
20.8 安全文件中数据的加解密
20.8.1 各种类型数据的组成及作用
20.8.2 元数据的加密
20.8.3 数据块区域的加密策略
20.9 小结
第21章 可信应用及客户端应用的开发
21.1 TA及CA的基本概念
21.2 GP标准
21.3 GP标准对TA属性的定义
21.4 GP标准定义的接口
21.4.1 GP定义的客户端接口
21.4.2 GP定义的内部接口
21.5 TA和CA的实现
21.5.1 建立CA和TA的目录结构
21.5.2 CA代码的实现
21.5.3 TA代码的实现
21.6 TA和CA的集成
21.6.1 CA和TA的Makefile的修改
21.6.2 OP-TEE中comm.mk和xxx.mk文件的
修改21.7 TA和CA的调试
21.8 TA和CA的使用
21.9 小结
第22章 安全驱动的开发
22.1 安全设备的硬件安全隔离
22.2 OP-TEE中安全驱动的框架
22.2.1 系统服务层
22.2.2 驱动层
22.2.3 驱动文件在源代码中的位置
22.3 安全驱动的开发过程和示例
22.3.1 示例代码获取和集成
22.3.2 驱动实现
22.3.3 添加系统服务
22.3.4 添加系统调用
22.3.5 测试使用的TA和CA
22.4 安全驱动示例的测试
22.5 小结
第23章 终端密钥在线下发系统
23.1 密钥在线下发系统的框架
23.2 密钥在线下发的数据包格式
23.3 密钥在线下发系统示例
23.3.1 示例代码获取和集成
23.3.2 板级编译文件的修改
23.3.3 通用编译文件的修改
23.3.4 编译运行
23.4 离线工具的使用23.5 小结
第24章 基于OP-TEE的在线支付系统
24.1 在线支付系统的基本框架
24.2 可信通信通道
24.3 数据交互协议
24.3.1 数据头部区域
24.3.2 数据区域
24.3.3 电子签名区域
24.3.4 交互数据包的格式
24.4 在线支付系统示例的实现
24.4.1 第一次握手请求
24.4.2 第二次握手数据的解析
24.4.3 第三次握手请求
24.4.4 支付请求
24.4.5 支付反馈
24.5 示例的集成
24.5.1 示例代码的获取和集成
24.5.2 板级编译文件的修改
24.5.3 通用编译文件的修改
24.5.4 编译运行
24.5.5 示例支持的命令说明
24.5.6 服务器端工具
24.6 组包操作嵌入内核
24.7 支付系统与生物特征的结合
24.8 小结
第25章 TEE可信应用的使用领域25.1 在线支付
25.2 数字版权保护
25.3 身份验证
25.4 其他领域
术语表推荐序
近年来随着指纹支付的盛行,如支付宝、微信
支付等,可信执行环境(Trusted Execution
Environment,TEE)被广泛应用在手机、平板电脑
等移动终端设备中。尤其是近年来谷歌对系统安全
问题越来越重视,可信执行环境已成为谷歌提升系
统安全性的重要技术之一,包含为人熟知的
keymaster、gatekeeper等,未来在Android P上还会
引入基于TUI(Trusted User Interface)衍生的
Confirmation UI,这将会为使用者提供更好的安全
体验。
可信执行环境是一个典型的软硬件协同合作的
概念,基于ARM的TrustZone技术为系统提供资源
的物理隔离,将系统执行环境区隔为安全区域和非
安全区域。开发者通过使用安全操作系统(secure
OS)提供的API开发更多的可信应用来实现特定的
安全功能。系统的安全是环环相扣的信任链,从设
备开机的安全引导到安全操作系统的安全性验证,一直到软件开发者开发的软件安全性验证,每层相
扣,而可信执行环境为可信应用提供了一个基础且
可信任的执行环境。
未来TEE的发展方向是多元的,TEE的应用也会进入更多的产业,除了目前大热的指纹识别之
外,系统也会引入更多的生物识别技术,如虹膜与
人脸识别,从摄像头获取图像到识别演算的整个过
程都会在TEE中完成。此外TUI也是重要的方向之
一,使用者如何确认所见即所支付,确认的支付金
额或转账账号不会被别人攻击或修改,都是相当重
要的安全需求。除了移动终端设备之外,车载系统
和IoT设备也都有对应的安全需求,因此在可遇见
的未来,TEE将会被广泛应用到不同领域、不同的
电子设备中。
此外,安全应用的开发者如何将安全应用广泛
部署到不同的设备中,以及如何安全升级它们也相
当重要。当发现了软件漏洞,如何第一时间更新安
全应用并避免版本回滚的攻击,是系统安全的一个
重要议题,目前商用TEE的生态、安全应用的签名
密钥都掌握在设备制造商手中,而安全应用的独立
在线下发和更新,将是未来的重要技术发展方向。
机缘巧合,我认识峰云已经有相当久的时间
了,他对TEE的了解相当深入,也相当用心地完成
了该书,遇到有疑问与不理解的地方,他会想方设
法地找出答案,他的专业与用心深受大家的肯定与
赞赏。本书涵盖了TEE的硬件和软件知识,通过
OP-TEE开源项目的协助,读者可以通过理论与实
践的结合,深入理解TEE的原理、设计与应用。期望通过本书的出版,能够让更多人了解与接触TEE
的相关知识,进而发现更多的应用场景,享受更多
的安全服务,让未来的生活在因为科技更方便的同
时,使用者的隐私与安全也能得到保护。
邱国政(Koshi)
Trustonic中国OEM经理前言
早在2014年,投资过Facebook、Skype、Twitter等的风投公司创始人安德森就说:“移动正
在吞噬这个世界”(mobile is eating the world)。这
毫不夸张,全球范围内移动设备的数量已经超过了
世界人口的总和。在如今信息化技术高速发展的时
代,人们的生活越来越离不开智能手机,越来越多
的业务从原先复杂的流程演变到现在只需要简单地
在手机上按几个按键。技术是一把双刃剑,总能给
人带来难以想象的便利,但便利总是伴随着用户隐
私的泄漏、身份认证的滥用等一系列的安全风险。
据著名安全漏洞报告机构FreeBuf 2017年度移动应
用程序安全漏洞与数据泄漏状况报告指出,多达
88%的金融类App存在内存敏感数据泄漏问题,娱
乐类移动应用程序更是安全漏洞的重灾区,社交类
App被仿冒的概率比其他类别平均高出10倍以上。
如何保障移动设备的安全,提高安全认证程序的可
靠性,一直是近几年的热门话题。
由嵌入式处理器最大的设计商ARM公司提出
的硬件虚拟化扩展技术TrustZone,发展到现在已经
有十余年的光景,如今已成为智能手机平台不可或
缺的部分。从Android 7.0开始,谷歌就明确表示,Android设备上有关生物特征(指纹、虹膜等)识别的方案一定要基于可信执行环境(Trust
Execution Environment,TEE)来实现。TEE就是基
于TrustZone技术建立的具有更高安全级别的可信执
行环境,运行在TEE环境下的应用称为可信应用程
序(Trusted Application,TA)。随着TEE可信应用
开发的API的普及,国内越来越多的手机厂商开始
集成TEE以及相关的可信应用。TEE环境的提供商
也越来越多,从先前国外的Trustonic TEE、高通
QSEE到现在国内的豆荚、华为、瓶钵等,可以说
TEE的技术开发门槛在降低,应用热度在提高。在
众多TEE产品方案中,有一个优秀的开源方案逐渐
进入人们的视野,那就是OP-TEE。OP-TEE(Open
Platform Trusted Execution Environment)由ST-
Ericsson创建,由STMicroelectronics维护,2014年
ARM的开源社区Linaro将OP-TEE方案开源。截至
目前,OP-TEE一直是Linaro社区在维护的核心安全
项目之一。目前看来,进入TEE领域最好的方式就
是学习成熟的OP-TEE方案。作者便是在学习OP-
TEE的过程中完成了本书,旨在为后继的入门者扫
除一些障碍。
本书组织结构
本书将采取由浅入深的方式介绍TrustZone技术
的原理、OP-TEE的整体架构及其主要功能模块的
原理,同时介绍如何基于OP-TEE进行可信应用、客户端应用(Client Application,CA)、安全驱动
等功能的开发。
本书主要分为四篇,总计25章,各篇的主要内
容分别如下。
·第一篇,基础技术篇(第1章~第4章),包
含TrustZone技术的背景和实现原理、系统基本框架
以及OP-TEE环境的搭建。
·第二篇,系统集成篇(第5章~第9章),分
析OP-TEE在REE和TEE中各个组件的作用和联系,对于有一定嵌入式以及LinuxAndroid开发经验的读
者,该篇实质上给将OP-TEE集成到基于
ARMv7ARMv8处理器的开发平台打下基础。
·第三篇,OP-TEE内核篇(第10章~第17
章),包含OP-TEE内核的中断处理、线程管理和
通信等主要功能的实现原理,使读者对TEE OS的
架构设计有进一步认识。
·第四篇,应用开发篇(第18章~第25章),介绍基于OP-TEE在加密、解密、安全存储等方面
的实际应用,以及如何开发基于OP-TEE的可信应
用程序。如果对OP-TEE有一定了解的读者希望通
过实践开发来了解TEE的工作原理,可以直接从应用开发篇学习。
OP-TEE的代码量远没有Linux内核大,但其涉
及的设计之复杂、模块之丰富也不是本书能完全涵
盖的。我们的初衷是希望通过本书对重要模块的代
码和流程进行分析,使读者对OP-TEE的架构有整
体的认识,之后看到其他部分也能做到举一反三。
本书的主要代码均引用自GitHub上OP-TEE开
源项目的源代码(链接:https:github.comOP-
TEEoptee_os),作者在翻译了一些代码英文注释
的基础上根据自己的理解对部分代码补充了更多的
注释。如果读者对书中代码的中文注释有疑问,可
参考上述链接中的原始代码和注释。另外,OP-
TEE也有详细的文档资料(https:github.comOP-
TEEoptee_ostreemasterdocumentation),强烈建
议英文基础好的读者结合本书和官方文档来学习。
如发现本书有纰漏和错误,或者需要改进之处,希
望读者不吝指出。
本书特色
俗话说,基础打不牢,学问攀不高。本书采取
自下而上的方式从硬件的角度介绍了TrustZone技
术,并结合源代码逐步剖析了基于TrustZone技术的
OP-TEE实现。在技术深度上,本书从入门者的角度出发由浅入深,从最基础的开发环境的搭建到最
终的OP-TEE OS的内部实现都进行了介绍;从内容
易读性上来讲,本书提供了基础的示例代码和各种
算法的使用示例,并给出了所有示例的源代码链接
及操作的实验步骤。相信读者通过边学习边实践的
方式阅读完本书后,能够掌握TrustZone技术的基础
原理和使用OP-TEE进行实际的应用开发。
由于任何TEE方案的源代码都属于芯片厂商的
商业机密,外界无法一览各TEE方案的实现原理,且TrustZone也是最近几年才被正式商用的,所以网
上的资料较少。本书是作者基于多年的工作积累并
对实际工作过程中遇见的问题进行整理后形成的。
本书读者对象
·手机、嵌入式系统和芯片开发者及技术支持
人员;
·手机和嵌入式系统安全与可信应用(支付系
统、多媒体及身份识别等)开发人员;
·相关专业安全技术研究者和大专院校学生;
·广大关心安全技术的爱好者。致谢
从萌生写本书的念头到最终完成初稿共花了一
年零三个月,一路走来历经波折,其间获得了许多
朋友和老师的帮助,感恩这一切。感谢他们曾经的
陪伴和咖啡,让我有勇气将本书写完。
感谢邱国政(Koshi)的指导以及他为本书拟
定的大纲,感谢他在写作期间提供的耐心的释疑解
惑,并在初稿完成后挤出宝贵的时间审阅全稿,提
出建设性的修改意见。
感谢段富刚师兄在写作初期的建议、支持和鼓
励,以及在后期审稿阶段给出的修改意见,因您一
语,才使我尝试将博文整理成书稿,使内容更加完
善和系统。
感谢樊鹏对本书稿件的审阅及结合自身在NXP
车载芯片上集成OP-TEE的实际工作经验对本书提
供的宝贵建议。
感恩家人在写作期间给予的理解和支持。感恩
沈雪亮和林先贤曾经的教导,感谢王佞姐平时的关
心,感谢黄诚、龚强强、徐贵友等好友在我撰写本
书期间给予的无私帮助和真切鼓励。感谢孟庆洋博士、黄冕博士、王子亮总监、尉
鲁飞师兄对本书内容的认可和推荐。感谢邓仰东老
师和朱捷编辑在审稿期间给予的非常有益的建议和
指导,让本书的内容更加丰富和严谨。
感谢张星茹在我考研时提供的帮助,感恩大学
那么一帮人,十年友谊一直未变。
感谢OP-TEE开源项目组的各位大牛,正是你
们的分享精神赋予了开发者涉足TrustZone这片神秘
领地和一探TEE具体实现原理的机会。
最后仅以此书纪念自己曾经的三十年,感恩生
命中遇见的每一个人、大学同学(于洋、郭成飞、吼哥、涛哥、胖胖、石头、王健等一帮兄弟)、研
究生同学(刘峰、孙登高、夏轩、刘智、陈耀
闯),以及所有的朋友,是你们的出现让我过去的
三十年不曾遗憾。第一篇 基础技术篇
第1章 可信执行环境
第2章 ARM的TrustZone技术
第3章 ARM可信固件
第4章 OP-TEE运行环境的搭建及编译第1章 可信执行环境
1.1 系统存在的安全问题
随着移动通信和互联网技术的飞速发展,智能
设备在各个领域扮演着越来越重要的角色。据统
计,在2017年,中国使用智能手机上网的用户数已
达6亿之多。此外,无人驾驶、物联网、网络电视
等也都与智能设备相关,或者本身就是智能设备,它们都会用到操作系统。然而由于一些黑客能够破
解智能设备的root权限,进而盗取用户数据或其他
关键信息,造成用户数据的泄露或滥用。其次,如
果用户的车载系统被黑客获取控制权限,其人身安
全将无从保障。因此手机互联网领域、电视领域、物联网领域以及车载领域的安全越来越显得重要。
再者,智能设备上各种应用不断涌现,若开发
人员在开发这些应用时没有针对安全进行加固保
护,则黑客可能会利用这些应用本身固有的安全漏
洞获取智能设备操系统的root权限,轻松截获用户
的敏感数据。鉴于此,如何保障智能设备的安全变
得越来越重要。
那么,如何消除甚至杜绝这类威胁呢?除了提
高系统被破解的难度之外,最好还要在系统中提供一个相对可信赖的运行环境,使用户的关键数据或
应用在这个相对可信赖的环境中使用和运行。这样
一来,即便系统被攻破,入侵者也无法直接获取用
户的重要信息,用户的信息安全也就实现了,这就
是可信执行环境(Trusted Execution Environment,TEE)的主要作用和理念。1.2 TEE如何保护数据安全
为了给移动设备提供一个安全的运行环境,ARM从ARMv6的架构开始引入了TrustZone技术。
TrustZone技术将中央处理器(Central Processing
Unit,CPU)的工作状态分为了正常世界状态
(Normal World Status,NWS)和安全世界状态
(Secure World Status,SWS)。支持TrustZone技
术的芯片提供了对外围硬件资源的硬件级别的保护
和安全隔离。当CPU处于正常世界状态时,任何应
用都无法访问安全硬件设备,也无法访问属于安全
世界状态下的内存、缓存(Cache)以及其他外围
安全硬件设备。
TEE基于TrustZone技术提供可信运行环境,还
为开发人员提供了应用程序编程接口(Application
Programming Interface,API),以方便他们开发实
际应用程序。
在整个系统的软件层面,一般的操作系统(如
Linux、Android、Windows等)以及应用运行在正
常世界状态中,TEE运行在安全世界状态中,正常
世界状态内的开发资源相对于安全世界状态较为丰
富,因此通常称运行在正常世界状态中的环境为丰
富执行环境(Rich Execution Environment,REE),而可信任的操作系统以及上层的可信应用
(Trusted Application,TA)运行于安全世界状
态,运行在安全世界状态中的系统就是前文提到的
TEE。
对CPU的工作状态区分之后,处于正常世界状
态中的Linux即使被root也无法访问安全世界状态中
的任何资源,包括操作安全设备、访问安全内存数
据、获取缓存数据等。这很像一个保险箱,不管保
险箱的外在环境是否安全,其内部的物件都有足够
的安全性。这是因为CPU在访问安全设备或者安全
内存地址空间时,芯片级别的安全扩展组件会去校
验CPU发送的访问请求的安全状态读写信号位
(Non-secure bit,NS bit)是0还是1,以此来判定
当前CPU发送的资源访问请求是安全请求还是非安
全请求。而处于非安全状态的CPU将访问指令发送
到系统总线上时,其访问请求的安全状态读写信号
位都会被强制设置成1,表示当前CPU的访问请求
为非安全请求。而非安全请求试图去访问安全资源
时会被安全扩展组件认为是非法访问的,于是就禁
止其访问安全资源,因此该CPU访问请求的返回结
果要么是访问失败,要么就是返回无效结果,这也
就实现了对系统资源硬件级别的安全隔离和保护。
在真实环境中,可以将用户的敏感数据保存到
TEE中,并由可信应用(Trusted Application,TA)使用重要算法和处理逻辑来完成对数据的处理。当
需要使用用户的敏感数据做身份验证时,则通过在
REE侧定义具体的请求编号(IDentity,ID)从TEE
侧获取验证结果。验证的整个过程中用户的敏感数
据始终处于TEE中,REE侧无法查看到任何TEE中
的数据。对于REE而言,TEE中的TA相当于一个黑
盒,只会接受有限且提前定义好的合法调用,而至
于这些合法调用到底是什么作用,会使用哪些数
据,做哪些操作在REE侧是无法知晓的。如果在
REE侧发送的调用请求是非法请求,TEE内的TA是
不会有任何的响应或是仅返回错误代码,并不会暴
露任何数据给REE侧。1.3 现有TEE解决方案
TEE是一套完整的安全解决方案,主要包含正
常世界状态的客户端应用(Client Application,CA)、安全世界状态的可信应用,可信硬件驱动
(Secure Driver,SD)以及可信内核系统(Trusted
Execution Environment Operation System,TEE
OS),其系统配置、内部逻辑、安全设备和安全资
源的划分是与CPU的集成电路(Integrated Circuit,IC)设计紧密挂钩的,使用ARM架构设计的不同
CPU,TEE的配置完全不一样。国内外针对不同领
域的CPU也具有不同的TEE解决方案。
国内外各种TEE解决方案一般都遵循
GP(Global Platform)规范进行开发并实现相同的
API。GP规范规定了TEE解决方案的架构以及供TA
开发使用的API原型,开发者可以使用这些规定的
API开发实际的TA并能使其正常运行于不同的TEE
解决方案中。1.3.1 智能手机领域的TEE
智能手机领域的芯片厂商众多,国外有高通
(Qualcomm)、三星(Samsung)、LG,国内有
展讯、联发科(MediaTek)、威盛电子(VIA)、华为海思(Hisilicon)等,目前手机厂商和芯片厂
商支持的TEE解决关系如图1-1所示。图1-1 TEE解决方案关系
各家TEE解决方案的内部操作系统的逻辑会不一样,但都能提供GP规范规定的API,对于二级厂
商或TA开发人员来说接口都是统一的。这些TEE解
决方案在智能手机领域主要用于实现在线支付(如
微信支付、支付宝支付)、数字版权保护(DRM、Winevine Level 1、China DRM)、用户数据安全保
护、安全存储、指纹识别、虹膜识别、人脸识别等
其他安全需求。这样可以降低用户手机在被非法
root之后带来的威胁。
Google规定在Android M之后所有的Android设
备在使用指纹数据时都需要用TEE来进行保护,否
则无法通过Google的CTS认证授权,另外Android也
建议使用硬件Keymaster和gatekeeper来强化系统安
全性。1.3.2 智能电视领域的TEE
当前的智能电视领域大多是使用Android系统
来实现的,为保护二级厂商的视频源数据以及各厂
家用户会员权益不被盗取,需要使用TEE来实现数
字版权保护、会员鉴权、用户账号信息保护等安全
功能,而TEE方案一般都是由电视芯片厂商提供
的,且所有的TEE源代码都不对外公开,即使是二
级厂商也无法获取到TEE的源代码。在我国的智能
电视领域,智能电视芯片主要有两家:星辰半导体
(Mstar)和华为海思,两家厂商使用的TEE方案都
不一样。
Mstar早期的TEE方案是在CPU的一个类似于单
片机的核上运行Nuttx系统作为TEE OS来实现TEE
方案的,但最新的Mstar芯片已经改用OP-TEE方案
来实现TEE解决方案。
华为海思的安全操作系统(Secure Operating
System,Secure OS)是按照GP规范自主研发的
TEE解决方案,其手机芯片和智能电视芯片都是使
用这个TEE方案。华为海思的TEE增加了权限校验
功能(类似于白名单机制),即在使用华为海思的
TEE方案提供的API实现特定安全功能的TA时,需
要将调用该TA对应的CA接口的进程或者服务的相关信息提前注册到TEE后方能正常使用,否则会导
致调用失败。1.3.3 IoT领域及其他领域的TEE
物联网(Internet of Thing,IoT)领域和车载
系统领域将会是未来TEE方案使用的另外一个重要
方向,大疆无人机已经使用TEE方案来保护无人机
用户的私人数据、航拍数据以及关键的飞控算法。
ARM的M系列也开始支持TrustZone技术,如何针
对资源受限的IoT设备实现TEE也是未来TEE的重要
发展方向之一。
而在车载领域NXP芯片已经集成OP-TEE作为
TEE方案,MediaTek的车载芯片也已集成了
Trustonic的TEE方案,相信在车载系统领域TEE也
将渐渐普及。1.4 为什么选择OP-TEE
本书主要是介绍OP-TEE的实现原理,OP-TEE
是由非营利的开源软件工程公司Linaro开发的,从
git上可以获取OP-TEE的所有源代码,且OP-TEE支
持的芯片也越来越多,相信未来OP-TEE将有可能
是TEE领域的Linux,并得到更加广泛的运用。
OP-TEE是按照GP规范开发的,支持QEMU、Hikey(Linaro推广的96Board系列平台之一,使用
Hisilicon处理器)以及其他通用的ARMv7ARMv8
平台,开发环境搭建方便,便于开发者开发自有的
上层可信应用,且OP-TEE提供了完整的软件开发
工具包(Software Development Kit,SDK),方便
编译TA和CA。OP-TEE遵循GP规范,支持各种加
解密和电子签名验签算法以便实现DRM、在线支
付、指纹和虹膜识别功能。OP-TEE也支持在芯片
中集成第三方的硬件加解密算法。除此之外,在
IoT和车载芯片领域也大都使用OP-TEE作为TEE解
决方案。
OP-TEE由Linaro组织负责维护,安全漏洞补丁
更新和代码迭代速度较快,系统的健壮性也越来越
好,所以利用OP-TEE来研究TrustZone技术的实现
并开发TA和CA将会是一个很好的选择。本书涉及的内核源代码使用的是OP-TEE 2.4版
本,书中所有的示例都在最新版本中测试通过。第2章 ARM的TrustZone技术
2.1 TrustZone技术
为提高系统的安全性,ARM早在ARMv6架构
中就引入了TrustZone技术[1]
,且在ARMv7和
ARMv8中得到增强,TrustZone技术能提供芯片级
别对硬件资源的保护和隔离,当前在手机芯片领域
已被广泛应用。
[1] TrustZone硬件需求文档:lcu14-
500armtrustedfirmware-140919105449-
phpapp02.pdf;TrustZone白皮书:PRD29-GENC-
009492C_TrustZone_security_whitepaper.pdf。2.1.1 片上系统硬件框架
一个完整的片上系统(System on Chip,SoC)
由ARM核、系统总线、片上RAM、片上ROM以及
其他外围设备组件构成。只有支持TrustZone技术的
ARM核配合安全扩展组件,才能为整个系统提供芯
片硬件级别的保护和隔离。如图2-1所示是一个支
持TrustZone的SoC的硬件框图。
支持TrustZone技术的ARM核在运行时将工作
状态划分为两种:安全状态和非安全状态。当处理
器核处于安全状态时只能运行TEE侧的代码,且具
有REE侧地址空间的访问权限。当处理器核处于非
安全状态时只能运行REE侧的代码,且只能通过事
先定义好的客户端接口来获取TEE侧中特定的数据
和调用特定的功能。
系统通过调用安全监控模式调用(secure
monitor call,smc)指令实现ARM核的安全状态与
非安全状态之间的切换。而ARM核对系统资源的访
问请求是否合法,则由SoC上的安全组件通过判定
ARM核发送到SoC系统总线上的访问请求中的安全
状态读写信号位(Non-secure bit,NS bit)来决
定。只有当ARM核处于安全状态(NS bit=0)时发
送到系统总线上的读写操作才会被识别为安全读写操作,对应TEE侧的数据资源才能被访问。反之,当ARM核处于非安全状态(NS bit=1)时,ARM核
发送到系统总线上的读写操作请求会被作为非安全
读写操作,安全组件会根据对资源的访问权限配置
来决定是否响应该访问请求。这也是TrustZone技术
能实现对系统资源硬件级别的保护和隔离的根本原
因。
图2-1 SoC硬件框2.1.2 ARMv7架构的TrustZone技术
ARMv7架构中使用了TrustZone技术的系统软
件层面的框图如图2-2所示。
图2-2 ARMv7系统软件框架
在ARMv7架构中CPU在运行时具有不同的特权
等级,分别是PL0(USR)、PL1(FIQIRQ、SYS、ABT、SVC、UND和MON)以及
PL2(Hyp),即ARMv7架构在原有七种模式之上
扩展出了Monitor模式和Hyp模式。Hyp模式是ARM
核用于实现虚拟化技术的一种模式。系统只有在
Monitor模式下才能实现安全状态和非安全状态的切
换。当系统在REE侧或者TEE侧运行时,系统执行
smc(安全监控模式调用)指令进入Monitor模式,通过判定系统SCR寄存器中对应的值来确定请求来
源(REETEE)以及发送目标(REETEE),相关
寄存器中的值只有当系统处于安全态时才可以更
改,关于安全状态与非安全状态之间的切换过程,在本书第10章中将进行详细介绍。2.1.3 ARMv8架构的TrustZone技术
在ARMv8架构中改用执行等级(Execution
Level,EL)EL0~EL3来定义ARM核的运行等级,其中EL0~EL2等级分为安全态和非安全态。
ARMv8架构与ARMv7架构中ARM核运行权限的对
应关系如图2-3所示。
图2-3 ARMv7v8运行权限对比
ARMv7和ARMv8架构下特权等级和工作模式
的对应关系分别如表2-1所示。
表2-1 ARMv7和ARMv8架构下各模式对应关系ARMv7架构中的PL0(USR)对应ARMv8架构
中的EL0,PL1(SVCABTIRQFIQUNDSYS)对
应ARMv8架构中的EL1,ARMv7架构中的Hyp模式
对应ARMv8架构中的EL2,而ARMv7架构中的
Mon(Monitor)则对应于ARMv8架构中的EL3。
ARMv8架构同样也是使用安全监控模式调用
指令使处理器进入EL3,在EL3中运行的代码负责
处理器安全状态和非安全状态的切换,其中关于
TEE和REE切换的处理方式与ARMv7架构中Monitor
模式下的处理方式类似,本书第10章将结合实际代
码进行详细分析。2.2 ARM安全扩展组件
TrustZone技术之所以能提高系统的安全性,是
因为对外部资源和内存资源的硬件隔离。这些硬件
隔离包括中断隔离、片上RAM和ROM的隔离、片
外RAM和ROM的隔离、外围设备的硬件隔离、外
部RAM和ROM的隔离等。实现硬件层面的各种隔
离,需要对整个系统的硬件和处理器核做出相应的
扩展。这些扩展包括:
·对处理器核的虚拟化,也就是将AMR处理器
的运行状态分为安全态和非安全态。
·对总线的扩展,增加安全位读写信号线。
·对内存管理单元(Memory Management
Unit,MMU)的扩展,增加页表的安全位。
·对缓存(Cache)的扩展,增加安全位。
·对其他外围组件进行了相应的扩展,提供安
全操作权限控制和安全操作信号。2.2.1 AXI总线上安全状态位的扩展
为了支持TrustZone技术,控制处理器在不同状
态下对硬件资源访问的权限,ARM对先进可扩展接
口(Advanced eXtensible Interface,AXI)系统总线
进行了扩展。在原有AXI总线基础上对每一个读写
信道增加了一个额外的控制信号位,用来表示当前
的读写操作是安全操作还是非安全操作,该信号位
称为安全状态位(NS bit)或者非安全状态位
(Non-Secure bit)。
·AWPROT[1]:总线写事务——低位表示安全
写事务操作,高位表示非安全写事务操作。
·ARPROT[1]:总线读事务——低位表示安全
读事务操作,高位表示非安全读事务操作。
当主设备通过总线发起读写操作时,从设备或
者外围资源同时也需要将对应的PROT控制信号发
送到总线上。总线或者从设备的解码逻辑必须能够
解析该PROT控制信号,以便保证安全设备在非安
全态下不被非法访问。所有的非安全主设备必须将
安全状态位置成高位,这样就能够保证非安全主设
备无法访问到安全从设备。如果一个非安全主设备
试图访问一个安全从设备,将会在总线或者从设备上触发一个错误操作,至于该错误如何处理就依赖
于从设备的处理逻辑和总线的配置。通常这种非法
操作最终将产生一个SLVERR(slave error)或者
DECERR(decode error)。2.2.2 AXI-to-APB桥的作用
TrustZone同样能够保护外围设备的安全,例如
中断控制、时钟、IO设备,因此Trust-Zone架构还
能用来解决更加广泛的安全问题。比如一个安全中
断控制器和安全时钟允许一个非中断的安全任务来
监控系统,能够为DRM提供可靠的时钟,能够为用
户提供一个安全的输入设备从而保证用户密码数据
不会被恶意软件窃取。
AMBA3规范包含了一个低门数、低带宽的外
设总线,被称作外设总线(Advanced Peripheral
Bus,APB),APB通过AXI-to-APB桥连接到系统
总线上。而APB总线并不具有安全状态位,为实现
APB外设与TrustZone技术相兼容,APB-to-AXI桥将
负责管理APB总线上设备的安全。APB-to-AXI桥会
拒绝不匹配的安全事务设置,并且不会将该事务请
求发送给外设。2.2.3 TrustZone地址空间控制组件
TrustZone地址空间控制组件(TrustZone
Address Space Controller,TZASC)[1]
是AXI总线上
的一个主设备,TZASC能够将从设备全部的地址空
间分割成一系列的不同地址范围。在安全状态下,通过编程TZASC能够将这一系列分割后的地址区域
设定成安全空间或者是非安全空间。被配置成安全
属性的区域将会拒绝非安全的访问请求。
使用TZASC主要是将一个AXI从设备分割成几
个安全设备,例如off-Soc、DRAM等。ARM的动态
内存控制器(Dynamic Memory Controller,DMC)
并不支持安全和非安全分区的功能。如果将DMC接
到TZASC上,就能实现DRAM支持安全区域和非安
全区域访问的功能。需要注意的是,TZASC组件只
支持存储映射设备对安全和非安全区域的划分与扩
展,但不支持对块设备(如EMMC、NAND flash
等)的安全和非安全区域的划分与扩展。图2-4所
示为使用TZASC组件的例子。图2-4 TZASC组件示意
[1] TZASC文档:
DDI0431C_tzasc_tzc380_r0p1_trm.pdf。2.2.4 TrustZone内存适配器组件
TrustZone内存适配器组件(TrustZone Memory
Adapter,TZMA)[1]
允许对片上静态内存(on-SoC
Static Memory)或者片上ROM进行安全区域和非安
全区域的划分。TZMA支持最大2MB空间的片上静
态RAM的划分,可以将2MB空间划分成两个部
分,高地址部分为非安全区域,低地址部分为安全
区域,两个区域必须按照4KB进行对齐。分区的具
体大小通过TZMA的输入信号R0SIZE来控制,该信
号来自TZPC的输出信号TZPCR0SIZE。即通过编程
TZPC可以动态地配置片上静态RAM或者ROM的大
小。使用TZMA组件的链接框图如图2-5所示。
图2-5 使用TZMA组件的链接示意
[1] TZMA文档:
cycle_models_BP141_TZMA_User_Guide_v9_1_0_DUI1083A_en.pdf。2.2.5 TrustZone保护控制器组件
TrustZone保护控制器组件(TrustZone
Protection Controller,TZPC)[1]
是用来设定
TZPCDECPORT信号和TZPCR0SIZE等相关控制信
号的。这些信号用来告知APB-to-AXI对应的外设是
安全设备还是非安全设备,而TZPCR0SIZE信号用
来控制TZMA对片上RAM或片上ROM安全区域大
小的划分。TZPC包含三组通用寄存器
TZPCDECPROT[2:0],每组通用寄存器可以产生8
种TZPCDECPROT信号,也就是TZPC最多可以将
24个外设设定成安全外设。TZPC组件还包含一个
TZPCROSIZE寄存器,该寄存器用来为TZMA提供
分区大小信息。TZPC组件的接口示意如图2-6所
示。
当上电初始化时,TZPC的TZPCDECROT寄存
器中的位会被清零,同时TZPCR0SIZE寄存器会被
设置成0x200,表示接入到TZMA上的片上RAM或
者ROM的安全区域大小为2MB。通过修改TZPC的
寄存器配置的值可实现用户对资源的特定配置。
TZPC的使用例子如图2-7所示。图2-6 TZPC组件接口示意
图2-7 TZPC使用示例
[1] TZPC文档(BP147):
DTO0015_primecell_infrastructure_amba3_tzpc_bp147_to.pdf。2.2.6 TrustZone中断控制器组件
在支持TrustZone的SoC上,ARM添加了
TrustZone中断控制器(TrustZone Interrupt
Controller,TZIC)[1]。TZIC的作用是让处理器处
于非安全态时无法捕获到安全中断。TZIC是第一级
中断控制器,所有的中断源都需要接到TZIC上。
TZIC根据配置来判定产生的中断类型,然后决定是
将该中断信号先发送到非安全的向量中断控制器
(Vector Interrupt Controller,VIC)后以nIRQ信号
发送到处理器,还是以nTZICFIQ信号直接发送到
处理器。图2-8所示为TZIC在SoC中的使用示意。
图2-8 TZIC在SoC中的使用示意
通过对TZIC的相关寄存器进行编程,可对
TZIC进行配置并设定每个接入到TZIC的中断源的中断类型。TZIC具有众多寄存器,细节说明可以参
考相关ARM的文档。在TZIC中用来设置中断源类
型的寄存器为TZICIntSelect,如果TZICIntSelect中
的某一位被设置成1,则该相应的中断源请求会被
设置成快速中断请求(Fast Interrupt Request,FIQ)。如果某一位被设置成0,则该中断源的中断
请求会被交给VIC进行处理。如果VIC的IntSelect将
获取到的中断源设置成FIQ,那么该中断源会被再
次反馈给TZIC进行处理。
[1] TZIC文档:
DTO0013B_tzic_sp890_r0p0_to.pdf。2.2.7 Cache和MMU的扩展
在支持TrustZone的SoC上,会对MMU进行虚
拟化,使得寄存器TTBR0、TTBR1、TTBCR在安
全状态和非安全状态下是相互隔离的,因此两种状
态下的虚拟地址转换表是独立的。
存放在MMU中的每一条页表描述符都会包含
一个安全状态位,用以表示被映射的内存是属于安
全内存还是非安全内存。虚拟化的MMU共享转换
监测缓冲区(Translation Lookaside Buffer,TLB),同样TLB中的每一项也会打上安全状态位
标记,只不过该标记是用来表示该条转换是正常世
界状态转化的还是安全世界状态转化的。
Cache也同样进行了扩展,Cache中的每一项都
会按照安全状态和非安全状态打上对应的标签,在
不同的状态下,处理器只能使用对应状态下的
Cache。2.3 TrustZone技术对资源隔离的实现
ARM处理器核的虚拟化和资源隔离是
TrustZone实现安全需求的根本。支持TrustZone的
处理器核具有虚拟化,也即将一个物理核分成安全
状态和非安全状态。当处理器处于非安全状态时,只能访问属于非安全的外设和内存,而不能访问安
全的资源;当处理器处于安全态时,处理器既可以
访问安全资源,也可以访问非安全的资源,只有当
处理器核为安全世界状态时才可能发出PROT的安
全访问信号。2.3.1 中断源的隔离
在原来的ARM芯片中,使用VIC来对外部中断
源进行控制和管理,支持TrustZone后,ARM提出
了TZIC组件,在芯片设计时,该组件作为一级中断
源控制器,控制所有的外部中断源,通过编程TZIC
组件的相关寄存器来设定哪个中断源为安全中断源
FIQ,而未被设定的中断源将会被传递给VIC进行
处理。一般情况下VIC会将接收到的中断源设定成
普通中断请求(Interrupt Request,IRQ),如果在
VIC中将接收到的中断源设定成FIQ,则该中断源
会被反馈给TZIC组件,TZIC组件会将安全中断源
送到安全世界状态中进行处理。2.3.2 片上RAM和片上ROM的隔离
芯片内部存在小容量的RAM或者ROM,以供
芯片上电时运行芯片ROM或者存放芯片自身相关的
数据。TrustZone架构对该部分也进行了隔离操作。
隔离操作通过使用TZMA和TZPC组件来实现。
TZMA用来将片上RAM或者ROM划分成安全
区域和非安全区域,安全区域的大小则由接入的
TZPCR0SIZE信号来决定。而TZPCR0SIZE的值可
以通过编程TZPC组件中的TZPCR0SIZE寄存器来实
现。
当处理器核访问片上RAM或者ROM时,TZMA会判定访问请求的PROT信号是安全操作还
是非安全操作,如果处理器发出的请求为非安全请
求而该请求又尝试去访问安全区域时,TZMA就会
认为该请求为非法请求。这样就能实现片上RAM和
ROM的隔离,达到非安全态的处理器核无法访问片
上安全区域的RAM和ROM。2.3.3 片外DRAM的隔离
一个完整的系统必然会有片外RAM,对片外
RAM的隔离是通过TZASC组件实现的,ARM本身
的DMC可以将DRAM分割成不同的区域,这些区域
是没有安全和非安全分类。将DMC与TZASC相连
后再挂到总线上,通过对TZASC组件进行编程可以
将DRAM划分成安全区域和非安全区域。当主设备
访问DRAM时,除需要提供物理地址之外,还会发
送PROT信号。TZASC组件首先会判定主设备需要
访问的DARM地址是属于安全区域还是非安全区
域,然后再结合接收到的PROT信号来判定该次访
问是否有效。如果PROT信号为非安全访问操作,且访问的DRAM地址属于安全区域,则TZASC就不
会响应这次访问操作,这样就能实现DRAM中安全
区域和非安全区域的隔离。2.3.4 外围设备的隔离
其他外围设备都会挂载到APB总线上,然后通
过AXI-to-APB桥连接到AXI总线上,AXI-to-APB结
合TZPC组件的TZPCDECROT的值及访问请求的
PROT信号来判定该访问是否有效。当处理器需要
访问外围设备时,会将地址和PROT信号发送到
AXI总线上。
AXI-to-APB桥会对接收到的请求进行解析,获
取需要访问的所需外围设备,然后通过查询
TZPCDECROT的值来判断外设的安全类型,再根
据PROT信号就能判定该请求的安全类型。如果该
请求是非安全请求,但需要访问的外围设备属于安
全设备,则AXI-to-APB会判定该访问无效。
通过对TZPC中的TZPCDECROT寄存器进行编
程能够设置外设的安全类型,从而做到外设在硬件
层面的隔离。2.4 小结
本章介绍了TrustZone的原理以及在ARMv7和
ARMv8架构下TrustZone技术实现的差异。
TrustZone对系统实现了硬件隔离,将系统资源划分
成安全和非安全两种类型,同时在系统总线上增加
安全读写信号位,通过读取安全读写信号位电平来
确定当前处理器的工作状态,从而判断是否具有该
资源的访问权限。因此,TrustZone从硬件级别实现
了对系统资源的保护。第3章 ARM可信固件
3.1 为什么使用ATF
ARM可信任固件(ARM Trusted Firmware,ATF)是由ARM官方提供的底层固件,该固件统一
了ARM底层接口标准,如电源状态控制接口
(Power Status Control Interface,PSCI)、安全启
动需求(Trusted Board Boot Requirements,TBBR)、安全世界状态(SWS)与正常世界状态
(NWS)切换的安全监控模式调用(secure monitor
call,smc)操作等。ATF旨在将ARM底层的操作统
一使代码能够重用和便于移植。3.2 ATF的主要功能
ATF的源代码共分为bl1、bl2、bl31、bl32、bl33部分,其中bl1、bl2、bl31部分属于固定的固
件,bl32和bl33分别用于加载TEE OS和REE侧的镜
像。整个加载过程可配置成安全启动的方式,每一
个镜像文件在被加载之前都会验证镜像文件的电子
签名是否合法。
ATF主要完成的功能如下:
·初始化安全世界状态运行环境、异常向量、控制寄存器、中断控制器、配置平台的中断。
·初始化ARM通用中断控制器(General
Interrupt Controller,GIC)2.0版本和3.0版本的驱动
初始化。
·执行ARM系统IP的标准初始化操作以及安全
扩展组件的基本配置。
·安全监控模式调用(Secure Monitor Call,smc)请求的逻辑处理代码(Monitor模式EL3)。
·实现可信板级引导功能,对引导过程中加载的镜像文件进行电子签名检查。
·支持自有固件的引导,开发者可根据具体需
求将自有固件添加到ATF的引导流程中。3.3 ATF与TEE的关系
为规范和简化TrustZone OS的集成,在ARMv8
架构中,ARM引入ATF作为底层固件并开放了源
码,用于完成系统中BootLoader、Linux内核、TEE
OS的加载和启动以及正常世界状态和安全世界状态
的切换。ATF将整个启动过程划分成不同的启动阶
段,由BLx来表示。例如,TEE OS的加载是由ATF
中的bl32来完成的,安全世界状态和正常世界状态
之间的切换是由bl31来完成的。在加载完TEE OS之
后,TEE OS需要返回一个处理函数的接口结构体
变量给bl31。当在REE侧触发安全监控模式调用指
令时,bl31通过查询该结构体变量就可知需要将安
全监控模式调用指令请求发送给TEE中的那个接口
并完成正常世界状态到安全世界状态的切换。3.4 小结
在ARMv8架构中,如果系统需要支持TEE,则
几乎都必须使用由ARM提供的ATF作为底层固件。
关于ATF如何管理BootLoader、TEE OS、Linux内
核以及各个阶段镜像的加载过程和跳转过程,本书
第6章将结合实际代码详细分析。第4章 OP-TEE运行环境的搭建及编
译
OP-TEE是开源的TEE解决方案,任何人都可
从github库中获取OP-TEE的源代码,本章主要包括
如何从github中获取OP-TEE的源代码、如何搭建运
行环境以及整个OP-TEE工程的编译过程。本书以
QEMU作为运行平台,Hikey或者其他平台的编译
和使用方式与QEMU平台类似。4.1 获取OP-TEE代码并搭建运行环境
OP-TEE的开发环境推荐使用Linux进行搭建,可在Windows系统中使用虚拟机创建一个Ubuntu系
统或者将计算机系统换成Ubuntu系统。4.1.1 OP-TEE开发环境的搭建
OP-TEE的开发环境依赖于各种基本库,在
Ubuntu系统中直接运行如下指令就可安装OP-TEE
开发环境需要使用的各种依赖库。
sudo apt-get install android-tools-adb android-tools-fastboot autoconf automake bc bison build-essential cscope curl device-tree-compiler expect flex ftp-upload gdisk iasl libattr1-dev libc6:i386 libcap-dev libfdt-dev libftdi-dev libglib2.0-dev libhidapi-dev libncurses5-dev libpixman-1-dev libssl-dev libstdc++6:i386 libtool libz1:i386 make mtools netcat python-crypto python-serial python-wand unzip uuid-dev xdg-utils xterm xz-utils zlib1g-dev4.1.2 获取OP-TEE的源代码
在系统中创建用于存放OP-TEE的目录“open-
tee”,读者可以根据自己的喜好替换目录的名字,创建完目录后就需要建立OP-TEE的repo(关于repo
或者git的使用,请读者自行查找资料了解),初始
化完repo后,使用repo sync指令就可从Github上获
取到OP-TEE的源代码,操作如下:
mkdir open-tee 创建目录
cd open-tee 切换到创建的目录
repo init -u https: github.comOP-TEEmanifest.git -m default.xml --repo-url=git:codeaurora.orgtoolsrepo.git -b 2.6.0 初始化repo
repo sync 开始获取OP-TEE源代码
如果在执行repo sync时出现“remote:
Repository not found”的报错提示,则需要修改open-
tee.repo目录下的manifest.xml文件,将该文件中所
有project域中的“.git”删除,也可通过如下指令进行
修改:
sed -i s\.gitg .repomanifest.xml
修改完成之后,重新执行repo sync来获取OP-
TEE的代码。manifest.xml文件中包含的就是整个工
程所需的单独git仓库的链接[1]。待代码同步完成后,为方便后续章节中各示例
代码的集成,需要使用如下指令将相关的代码回滚
到标签为3.0.0的版本。
cd optee_client
git checkout 3.0.0
cd optee_test
git checkout 3.0.0
cd optee_benchmark
git checkout 3.0.0
cd optee_examples
git checkout 3.0.0
cd optee_os
git checkout 3.0.0
[1] OP-TEE工程源代码链接:https:github.comOP-
TEE;OP-TEE内核代码链接:
https:github.comOP-TEEoptee_os;OP-TEE client
端代码链接:https:github.comOP-
TEEoptee_client;OP-TEE test代码链接:
https:github.comOP-TEEoptee_test;OP-TEE工程
使用的Linux Kernel代码链接:
https:github.comlinaroswglinux;OP-TEE 中使用
的QEMU 软件源代码链接:
https:github.comlinaro-swgqemu。4.1.3 获取编译OP-TEE的toolchain
OP-TEE工程的源代码下载完成后,下一步就
需要获取编译OP-TEE时使用的toolchain,切换到源
代码的build目录,执行如下指令:
cd build 切换到build目录
make -f toolchain.mk toolchains 下载toolchain
查看toolchain.mk文件可知,执行make指令之
后,系统会去下载toolchains的tar包,包括32位和64
位的编译链接工具,下载完成后会进行解压操作。
执行完make后,可发现OP-TEE源代码的根目录下
会多出一个toolchains的目录,该目录中存放的就是
编译OP-TEE工程时使用的所有编译链接工具。4.1.4 编译QEMU
OP-TEE源代码的build目录是用于编译整个工
程的编译目录,该目录包含各种平台的编译配置文
件。在QEMU平台运行时需选择qemu.mk文件进行
编译,具体操作如下:
cd build 切换到build目录
make -f qemu.mk all 编译工程
当然,读者也可将qemu.mk文件链接成
Makefile,然后在build目录下直接执行make all就能
编译QEMU平台的工程。
编译完成后将会在OP-TEE的根目录下生成一
个out目录,该目录中存放的就是使用QEMU方式运
行OP-TEE时需要的镜像和其他相关文件。
如果在编译的过程中出现“ImportError:No
Module named wand.image”的报错提示,说明系统
没有安装Python的Wand包,此时在shell中运行如下
指令即可:
pip install: Wand4.1.5 运行OP-TEE
工程编译完成之后,如果要运行OP-TEE,则
需要进入build目录中执行make run-only语句,具体
操作如下:
cd build 切换到build目录
make -f qemu.mk run-only 启动QEMU并运行OP-TEE
如果读者已将qemu.mk文件链接成了
Makefile,则直接在build目录中执行make run-only
即可。qemu.mk文件中的run-only目标首先会启动两
个分别属于安全世界状态和正常世界状态的
terminal,用于显示OP-TEE和Linux内核的日志数
据,然后加载OP-TEE镜像与Linux的镜像及其文件
系统。4.1.6 运行xtest和optee_example_hello_world
通过使用make run-only启动OP-TEE后,可在
启动的正常世界状态对应的terminal中执行
Optee_example_hello_world或者xtest指令来检查OP-
TEE是否正常运行。
Optee_example_hello_world是一个简单的CA编
译而成的二进制可执行文件,执行该可执行文件后
会调用OP-TEE中对应的TA,并执行一些简单的打
印操作,输出的日志信息可在安全世界状态的
terminal中查看。
xtest是OP-TEE自带的一个测试使用的CA可执
行文件。该CA执行后将会调用TA中的各种功能,包括检查基本算法接口、安全存储接口等。4.2 运行CA和TA示例
OP-TEE中自带的TA和CA都保存在
optee_examples目录中,那么如何添加自己开发的
TA和CA程序到OP-TEE中并运行呢?本节将对此进
行介绍。为减少对编译方面的理解,本节将结合实
际的TA和CA示例介绍详细的操作步骤。4.2.1 示例代码的获取和集成
本节所用示例的所有源代码可从gitHub上获
取,读者可使用如下指令获取到源代码,示例包中
有对应的补丁,读者直接合入补丁就可将该示例集
成到OP-TEE中,该示例的gitHub链接如下:
git clone https:github.comshuaifengyunoptee_my_test.git
获取到示例代码之后,切换到如下build目录
下,然后使用git apply命令合入补丁文件后就可将
该示例集成到OP-TEE,合入补丁的操作步骤如
下:
1)将示例代码中的
optee_mytest_common_3.0.0.patch文件和
optee_mytest_qemu_3.0.0.patch文件复制到build目录
中。
2)切换到build目录,使用如下命令合入补
丁:
git apply optee_mytest_common_3.0.0.patch
git apply optee_mytest_qemu_3.0.0.patch将补丁合入之后就可使用make-f qemu.mk all编
译整个工程,然后使用make-f qemu.mk run-only来
启动OP-TEE,在启动的正常世界状态的终端执行
my_test命令就能实现该示例的CA对TA的调用。示
例代码的运行效果如图4-1所示。
图4-1 optee_my_test示例运行4.2.2 目录和文件创建
从gitHub上获取到本章使用的示例代码后,host存放的是CA的代码,ta目录存放的是TA部分的
代码。本章提供的示例代码的目录结构如下:
├── Android.mk
├── build_ta_mytest_qemu.sh
├── doc
│ ├── close_session_and_finalize_context.msc
│ ├── invoke_command.msc
│ ├── Makefile
│ └── open_session.msc
├── host
│ ├── main.c
│ ├── Makefile
│ └── my_test_ca.h
├── Makefile
├── optee_mytest_common_3.0.0.patch
├── optee_mytest_qemu_3.0.0.patch
├── README.md
└── ta
├── Android.mk
├── include
│ ├── my_test_handle.h
│ └── my_test_ta.h
├── Makefile
├── my_test.c
├── my_test_handle.c
├── sub.mk
└── user_ta_header_defines.h
目录中文件的作用说明如下:·Android.mk文件:Android系统中编译整个TA
和CA时使用;
·build_ta_mytest_qemu.sh文件:单独编译TA和
CA使用的脚本文件;
·hostmain.c文件:CA的源代码;
·hostMakefile文件:编译CA时使用的makefile
文件;
·hostmy_test_ca.h文件:UUID、command ID
的宏定义;
·taMakefile文件:编译TA时使用的makefile文
件;
·tamy_test.c文件:主要是存放TA部分代码的
入口处理函数,CA的command请求最终会被
TA_InvokeCommandEntryPoint函数处理;
·tamy_test_handle.c文件:存放相应CA的
command请求的功能函数;
·tasub.mk文件:定义该TA中需要被编译的
source code;·tauser_ta_header_defines.h文件:定义UUID等
相关宏;
·taincludemy_test_handle.h文件:定义了该TA
需要使用的类型;
·taincludemy_test_ta.h文件:定义了UUID的宏
以及与CA对应的command ID宏;
·optee_mytest_common_3.0.0.patch文件:将该
TA和CA集成到OP-TEE时buildcommon.mk文件使
用的补丁文件;
·optee_mytest_qemu_3.0.0.patch文件:将该TA
和CA集成到OP-TEE时builldqemu.mk文件使用的补
丁文件。4.2.3 CA端代码的修改
若读者需添加新的功能,可按照GP规范调用
REE侧的相关接口,编辑完CA端的代码后就需要修
改host目录下的Makefile文件,将需要编译进CA的
文件添加到Makefile中,主要是修改hostMakefile文
件中的OBJS变量和BINARY变量,其中OBJS变量
存放的是需要编译到CA的目标文件或者库文件,BINARY是编译完成后的可执行文件的名字。注
意,在CA的头文件中需要定义UUID和command ID
的宏,且定义的内容需要与TA中的UUID和
command ID一致,否则执行CA后将会导致调用失
败,关于UUID的值并没有特殊的要求,只需按照
其格式定义一个唯一的字符串即可。4.2.4 TA端代码的修改
ta目录中存放的是该TA的源代码、makefile文
件和头文件,其中ta目录中必须存在一个
user_ta_header.h文件,该文件在编译TA镜像或者是
整个工程时会被使用到。在该文件中会定义UUID
的宏、该TA运行的堆栈空间的大小以及版本信
息。在TA的头文件中需要定义UUID的宏和
command ID,且必须与CA中定义的一样,否则CA
端将无法调用该TA中对应的操作。修改taMakefile
文件,将该文件中BINARY变量的值修改成与CA中
相同的UUID值。
修改完成后运行build_ta_mytest_qemu.sh脚本
就能单独编译CA和TA,如果出现错误,则根据提
示进行修改,编译成功后会在ta目录中生成与UUID
值一样的elf文件,在host目录中将会生成与
hostMakefile文件中BINARY变量的值一样的文
件。4.2.5 TA和CA在OP-TEE的集成
单独编译TA和CA成功后,就需要将该TA和
CA集成到OP-TEE的工程中去,需要修改OP-TEE源
代码中build目录下的qemu.mk文件和common.mk文
件。
在buildqemu.mk文件中增加该TA的目标和依
赖关系,以本示例为例,对buildqemu.mk文件的修
改如下。
1)增加optee_my_test的编译目标内容:
optee_my_test
Optee_mytest: optee_mytest-common
Optee_mytest-clean: optee_mytest-clean-common
2)将optee_mytest目标和optee_mytest-clean-
common目标添加到all中:
all: bios-qemu qemu soc-term optee-examples optee_my_test
clean: bios-qemu-clean busybox-clean linux-clean optee-os-clean optee-client-clean qemu-clean soc-term-clean check-clean optee_my_test-clean optee-examples-clean在buildcommon.mk文件中需要增加编译该TA
和CA的路径变量等信息,添加的内容如下。
1)增加TA和CA的代码路径:
OPTEE_MYTEST_PATH ?= (ROOT)optee_my_test
2)增加TA和CA的common目标:
optee_my_test
OPTEE_MYTEST_COMMON_FLAGS ?= HOST_CROSS_COMPILE=(CROSS_COMPILE_NS_USER) TA_CROSS_COMPILE=(CROSS_COMPILE_S_USER) TA_DEV_KIT_DIR=(OPTEE_OS_TA_DEV_KIT_DIR) TEEC_EXPORT=(OPTEE_CLIENT_EXPORT)
.PHONY: optee_my_test-common
optee_my_test-common: optee-os optee-client
(MAKE) -C (OPTEE_MYTEST_PATH) (OPTEE_MYTEST_COMMON_FLAGS)
OPTEE_MYTEST_CLEAN_COMMON_FLAGS ?= TA_DEV_KIT_DIR=(OPTEE_OS_TA_DEV_KIT_DIR)
.PHONY: optee_my_test-clean-common
optee_my_test-clean-common:
(MAKE) -C (OPTEE_MYTEST_PATH) (OPTEE_MYTEST_CLEAN_COMMON_FLAGS) clean
3)将该TA和CA添加到filelist-tee-common目标
的依赖关系中:
filelist-tee-common: optee-client xtest optee-examples optee_my_test
4)添加clean操作的依赖关系:optee-os-clean-common: xtest-clean optee-examples-clean optee_my_test-clean
5)在filelist-tee-common中添加TA和CA镜像需
要被打包到文件系统中的操作:
@echo optee_mytest >> (fl)
@if [ -e (OPTEE_MYTEST_PATH)hostmy_test ]; then echo file binmy_test (OPTEE_MYTEST_PATH)hostmy_test 755 0 0 >> (fl); echo file liboptee_armtz9269fadd-99d5-4afb-a1dc-ee3e9c61b04c.ta (OPTEE_MYTEST_PATH)ta9269fadd-99d5-4afb-a1dc-ee3e9c61b04c.ta 444 0 0 >> (fl); fi4.3 OP-TEE源代码结构
OP-TEE的源代码包含运行OP-TEE时需要的所
有软件源代码,OP-TEE工程编译完成后,整个源
代码的目录结构如下:
├── basicAlg_use
├── bios_qemu_tz_arm
├── build
├── busybox
├── gen_rootfs
├── linux
├── optee_benchmark
├── optee_client
├── optee_examples
├── optee_my_test
├── optee_os
├── optee_test
├── out
├── qemu
├── secStor_test
├── soc_term
└── toolchains
OP-TEE各子目录中的代码功能说明如下。
(1)bios_qemu_tz_arm目录
在QEMU平台中运行tz_arm的BIOS代码,启动
的最初阶段会被使用到,用来加载Linux内核镜
像、OP-TEE OS镜像、rootfs并启动Linux内核和OP-TEE OS。
(2)build目录
OP-TEE工程的编译目录,包含各种mk文件和
相关配置文件,其中common.mk文件是工程的通用
mk文件,不同的CPU架构有不同的mk与之相对
应,编译工程时可使用make-f的方式指定编译哪个
板级的OP-TEE。
(3)busybox目录
busybox的源代码,编译生成制作rootfs所需要
的文件和目录。
(4)gen_rootfs目录
存放制作rootfs时使用的相关脚本和配置文
件。
(5)linux目录
Linux内核代码,在drivertee目录下存放的是
OP-TEE在REE侧的驱动,任何在Linux用户空间调
用CA的接口都会经过OP-TEE的REE侧驱动处理之
后再转发到TEE侧。(6)optee_benchmark目录
OP-TEE运行的性能测试工具,只保存CA端的
代码,TA部分的代码保存在OP-TEE OS中,作为
静态TA集成到OP-TEE OS中。
(7)optee_client目录
包含CA程序调用的用户空间的接口库
(libteec)的源代码。其中tee_supplicant目录中的
代码会被编译成一个可执行文件,该可执行文件在
Linux启动时作为守护进程常驻在系统中,该守护
进程的主要作用是响应和处理来自TEE侧的RPC请
求,这些RPC请求包括:加载TA镜像、对文件系统
的操作、对SQL的操作、对EMMC RPMB的操作、网络通信的socket操作等。
(8)optee_examples目录
示例代码,目录下包含OP-TEE提供的各种示
例的TA和CA的所有代码,启动后,在REE对应的
terminal中执行optee_example_hello_world命令后,会调用optee_example_hello_world的TA的逻辑,TA
根据接收到的command ID在OP-TEE中执行对应的
操作。
(9)optee_os目录存放OP-TEE OS的源代码和相关文档,编译完
成之后,该目录将会生成OP-TEE的镜像文件。
(10)optee_test目录
opentee的测试程序xtest的源代码,主要用来测
试OP-TEE中提供的各种算法的逻辑并提供其他测
试功能。
(11)out目录
编译结果的输出目录(该目录编译完成之后才
会生成)。
(12)qemu目录
QEMU源代码,如果编译的是qemu.mk,编译
时将会使用到该目录。
(13)soc_term目录
在使用QEMU运行OP-TEE时,gnome-terminal
命令会启动终端,用于建立启动的两个terminal的
端口监听,方便OP-TEE OS的log和Linux的log分别
输出到对应的terminal中。
(14)toolchains目录编译时需要使用的编译工具链,在build目录下
执行make–f toolchai.mk toolchains后将会生成该目
录。4.4 OP-TEE编译
整个OP-TEE工程的编译是一个庞大的过程,牵扯到目标的依赖关系,本节以qemu.mk板级为
例,分析使用QEMU方式运行OP-TEE时的全部编
译过程和目标依赖关系。4.4.1 编译目标的依赖关系
编译OP-TEE工程时各主要目标的依赖关系如
下:
all
├── bios-qemu
│ ├── optee-os
│ │ └── optee-os-common
│ └── update_rootfs
│ └── update_rootfs-common
│ ├── busybox
│ │ └── busybox-common
│ │ └── linux
│ │ └── linux-common
│ │ └── linux-defconfig
│ └── filelist-tee
│ └── filelist-tee-common
│ ├── f1
│ │ └── filelist-tee.txt
│ ├── optee-client
│ │ ├── common
│ │ └── optee-client
│ └── xtest
│ │ ├── optee-client
│ │ └── xtest-common
│ │ └── optee-os
│ │ └──optee-os-common
│ └── optee-examples
│ └── build_socterm
│ └── helloworld-common
│ ├──optee-client
│ └──optee-os
├── benchmark-app
├── qemu
│ └── build_qemu
└── soc-term
│ └── build_socterm └── optee-examples
│ └── build_socterm
│ └── helloworld-common
│ ├──optee-client
│ └──optee-os
QEMU目标会切换到QEMU目录,并获取
QEMU的配置文件,然后执行make命令来编译
QEMU目标。
soc-term目标会编译soc-term目录,生成一个
soc-term的可执行文件,用于启动两个terminal。
bios-qemu目标依赖于update_rootfs和optee-os,update_rootfs和optee-os编译完成之后会调用
biosqemu-comm宏定义的指令,该宏会编译
bios_qemu_tz_arm目录,该目录编译完成之后,会
生成启动时需要的bios镜像。
optee-os-common目标将编译optee_os目录,该
目录编译完成后将会生成tee.bin及其他的lib库文
件。
busybox目标将编译linux目录和busybox目录,生成Linux内核镜像文件和制作rootfs需要的相关文
件。
filelist-tee目标将生成tee功能相关的文件和需要被挂载到rootfs中的映射图,然后与系统的其他文
件的挂载映射关系一起保存到filelist-final.txt文件
中,用于生成filesystem.cpio.gz文件。
update_rootfs-common目标依赖于busybox和
filelist-tee目标,上述两个目标编译完成之后,将会
切换到gen_rootfs目录中,调用gen_init_cpio命令生
成在启动时需要使用的filesystem.cpio.gz文件。
Optee_examples目标包含OP-TEE提供的各种
TA部分和CA部分,编译完成之后,会生成对应的
TA镜像文件和CA的可执行文件。
optee-client目标将对optee_client目录进行编
译,生成一系列的库文件和可执行文件,库文件提
供了OP-TEE在Linux端的接口,将被所有CA调用。
tee-supplicant目标将会编译生成一个tee_supplicant
的可执行文件,该可执行文件提供了optee_os访问
文件系统的RPC接口以及加载具体的TA镜像的功
能。
xtest目标将会编译optee_test目录,生成在xtest
集合中会使用的TA镜像文件和xtest可执行文件。4.4.2 bios.bin镜像的生成过程
bios.bin镜像是启动时会被使用到的主要镜像文
件,在执行make run-only指令使用QEMU方式启动
OP-TEE时,会借助qemu-system-arm命令来启动OP-
TEE和Linux kern,并挂载Linux的rootfs。在运行
qemu-system-arm命令时,其中有一个参数为“-
bios”,该参数就是告诉QEMU使用该参数之后所带
的bios.bin来启动整个系统。
bios.bin中会包含Linux kernel的镜像、OP-TEE
OS的镜像以及rootfs。该镜像文件是在bios-qemu的
目标中编译出来的。
当bios-qemu目标的依赖目标都编译完成后,会
使用bios-qemu-common函数将Linux内核、OP-TEE
镜像、rootfs打包成bios.bin镜像文件。bios-qemu-
common函数定义在buildqemu.mk文件中,内容如
下:
define bios-qemu-common
+(MAKE) -C (BIOS_QEMU_PATH) CROSS_COMPILE=(CROSS_COMPILE_NS_USER) O=(ROOT)outbios-qemu BIOS_NSEC_BLOB=(LINUX_PATH)archarmbootzImage BIOS_NSEC_ROOTFS=(GEN_ROOTFS_PATH)filesystem.cpio.gz BIOS_SECURE_BLOB=(OPTEE_OS_BIN) PLATFORM_FLAVOR=virtEndef
执行该函数时会带入相关的编译参数,编译由
变量BIOS_QEMU_PATH定义的目录
(bios_qemu_tz_arm目录),参数说明如下:
·CROSS_COMPILE:编译时使用的编译参
数,包括编译器、cflag等;
·O:编译结果的输出目录;
·BIO_NSEC_BLOB:定义该变量,指定Linux
内核镜像的名称和路径;
·BIOS_NSEC_ROOTFS:定义该变量,指定生
成的rootfs存在的目录和cpio格式文件名;
·BIOS_SECURE_BLOB:定义该变量,指定
OP-TEE OS镜像文件名;
·PLATFORM_FLAVOR:定义该变量,设定平
台变量。
编译bios_qemu_tz_arm目录时最终会将Linux内
核镜像、OP-TEE OS镜像、rootfs转换成.o文件,然
后再将这些转换后的.o文件与其他的.o文件一起链
接成biso.bin镜像文件。Linux内核镜像文件会被放在bios.bin中名称为nsec_blob的section里。OP-TEE
os image将会被放在bios.bin中名称为secure_blob的
section里。rootfs image将会被放在bios.bin中名称为
nsec_rootfs的section里。
上述将镜像文件转换成.o文件的操作是通过
OBJCOPY带--rename-section参数来实现的,具体的
内容可以在link.mk文件中找到。
bios_qemu_tz_armbiosentry.s文件存放的就是
在启动系统时bios.bin的入口文件。4.4.3 run-only目标的执行
qemu.mk文件中的run-only目标是用来启动使用
QEMU方式运行OP-TEE的起始目标,在qemu.mk文
件中,run-only目标的定义如下:
.PHONY: run-only
run-only:
(call check-terminal)
(call run-help)
(call launch-terminal, 54320,Normal World)
(call launch-terminal, 54321,Secure World)
(call wait-for-ports, 54320,54321)
(QEMU_PATH)arm-softmmuqemu-system-arm -nographic -serial tcp:localhost:54320 -serial tcp:localhost:54321 -s -S -machine virt -machine secure=on -cpu ARM核-a15 -m 1057 -bios (ROOT)outbios-qemubios.bin (QEMU_EXTRA_ARGS)
run-only目标的内容会调用各种函数,这些函
数会在相关的makefile文件中定义,下面是相关函
数的作用说明:
(call check-terminal):
check-terminal在QEMU的工程中不会被定义,该语句不会被执行,但是在其他工程中会定义,具
体可查看buildcommon.mk文件。(call run-help):
run-help函数定义在buildcommom.mk文件中,主要用来打印出相关的启动帮助信息。
(call launch-terminal,54320,Normal
World):
执行launch-terminal,54320,Normal
World指令,启动名字为Normal World的terminal,其中launch-terminal在buildcommon.mk文件中定
义。
(call launch-terminal,54321,Secure
World):
执行功能同上,只是在重定向时将端口换成了
54321,且启动的terminal名字为Secure World。
(call wait-for-ports,54320,54321):
调用wait-for-prots函数,该函数定义在
buildcommon.mk文件中,主要功能是检查上面启
动的两个terminal使用socket方式进行通信是否正
常。
(QEMU_PATH)arm-softmmuqemu-system-arm:
该指令就是调用qemu-system-arm指令,并设定
好QEMU启动的各种参数,然后开始启动Linux与
OP-TEE,该指令完全展开之后的内容如下:
homeicyshuaidevelopteebuild..qemuarm-softmmuqemu-system-arm -nographic -serial tcp:localhost:54320 -serial tcp:localhost:54321 -s -S -machine virt -machine secure=on -cpu ARM核-a15 -m 1057 -bios homeicyshuaidevelopteebuild..outbios-qemubios.bin
-nographic:不显示图形界面。
-serial:将串口重定向到后面的参数部分。
-S:使用C来控制启动(在QEMU的console界
面输入C之后才会正式启动系统)。
-m:设定虚拟的内存大小。
-bios:指定BIOS的文件(该image中会包含
OP-TEE、Linux、rootfs的镜像文件)。
1.launch-terminal函数
launch-terminal函数的主要功能是用来启动terminal。该函数定义在buildcommon.mk文件中,具体内容如下:
define launch-terminal
@nc -z 127.0.0.1 (1) || (gnome-terminal) -t (2) -x (SOC_TERM_PATH)soc_term (1)
endef
(gnome-terminal)的定义也在common.mk文件
中,定义如下:
gnome-terminal := (shell command -v gnome-terminal 2>devnull)
调用(call launch-terminal,54320,Normal
World)等价于:
gnome-terminal -t Normal World -x (SOC_TERM_PATH)soc_term 54320
调用该函数的作用是启动一个名字为Normal
World的terminal,并且在terminal中执行soc_term
54320,soc_term就是在soc_term目录中编译出来的
可执行文件。执行soc_term 54320命令的主要作用
是将该terminal的输入和输出通过54320端口重定向
到标准输入和输出端口。
2.soc_term可执行文件soc_term可执行文件用来实现Linux和OP-TEE
的两个terminal输入和输出重定向到标准输入输出
端口,该可执行文件的源代码存放在soc_term目录
中。soc_term.c文件中的main函数定义如下:
int main(int argc, char argv[])
{
int listen_fd;
char port;
bool have_handle_telnet_option = false;
switch (argc) {
case 2:
port = argv[1];
break;
case 3:
if (strcmp(argv[1], -t) != 0)
usage;
have_handle_telnet_option = true;
port = argv[2];
break;
default:
usage;
}
save_current_termios;获取当前terminal的信息(标准输入输出的terminal配置)
listen_fd = get_listen_fd(port);建立socket机制,并监听输入的端口号
printf(listening on port %s\n, port);
if (have_handle_telnet_option) 判定是否使用telent
printf(Handling telnet commands\n);
进入loop循环,完成端口监听和输入输出的重定向
while (true) {
int fd = accept_fd(listen_fd); 开始接收建立的监听端口的信息
handle_telnet = have_handle_telnet_option;
handle_telnet_codes(-1,NULL, NULL); 为使用telent时不起作用
warnx(accepted fd %d, fd);
复制当前terminal的信息,并配置其他参数,然后调用tcsetattr函数来设定当前启动的terminal的信息
set_tty_noncanonical;
开始处理监听收到的数据,并根据对应的revent进行重定向操作,server_fd函数的注释见后续章节
serve_fd(fd);
处理完成之后关闭该fd
if (close(fd)) err(1, close);
fd = -1;
保存当前terminos的配置
restore_termios;
}
}
server_fd函数用来接收受监控的端口的数据,并执行重定向操作,代码内容和解释如下:
static void serve_fd(int fd)
{
uint8_t buf[512];
struct pollfd pfds[2];
设定pollfd参数,用于实现重定向操作
memset(pfds, 0, sizeof(pfds));
pfds[0].fd = STDIN_FILENO;
pfds[0].events = POLLIN;
pfds[1].fd = fd;
pfds[1].events = POLLIN;
while (true) {
size_t n;
获取监听事件的pfds[0]和pfds[1]中定义的事件
if (poll(pfds, 2, -1) == -1)
err(1, poll);
如果pfds[0]中的POLLIN时间触发(在该terminal的标准输入中有输入操作),则进行读取操作
if (pfds[0].revents POLLIN) {
从该terminal的标准输入端口中读取输入的数据
n = read(STDIN_FILENO, buf, sizeof(buf));
if (n == -1)
err(1, read stdin);
if (n == 0)
errx(1, read stdin EOF);
将读取到的数据写入到重定向的port捆绑的socket
if (!write_buf(fd, buf, n)) {
warn(write_buf fd);
break;
}
}
如果pfds[1]中的POLLIN时间触发(监测到该terminal的port捆绑的socket有输入流操作),则读取监测的port对应的socket句柄中的数据 if (pfds[1].revents POLLIN) {
读取与port捆绑的socket的句柄中的数据
n = read(fd, buf, sizeof(buf));
if (n == -1) {
warn(read fd);
break;
}
if (n == 0) {
warnx(read fd EOF);
break;
}
handle_telnet_codes(fd, buf, n);
将读取到的数据写入到该terminal的标准输出
if (!write_buf(STDOUT_FILENO, buf, n))
err(1, write_buf stdout);
}
}
}4.5 小结
本章主要介绍OP-TEE开发和运行环境的搭
建,并提供了一个demo,介绍如何开发自己的TA
和CA并让其成功运行在OP-TEE中。为方便读者理
解整个OP-TEE工程的实际执行流程,知道如何生
成启动OP-TEE时使用的各种镜像文件,本章特意
介绍了OP-TEE工程的编译过程以及编译过程中各
种目标的依赖关系,同时对编译过程中的重要函数
做了进一步的介绍。第二篇 系统集成篇
第5章 QEMU运行OP-TEE的启动过程
第6章 安全引导功能及ATF的启动过程
第7章 OP-TEE OS的启动过程
第8章 OP-TEE在REE侧的上层软件
第9章 REE侧OP-TEE的驱动第5章 QEMU运行OP-TEE的启动过
程
使用QEMU的方式运行OP-TEE是通过在build
目录下执行make run-only来启动的,启动过程主要
是加载bios.bin文件,并从该镜像文件中分离出
Linux内核镜像和OP-TEE镜像以及rootfs镜像,并将
rootfs作为根文件系统挂到Linux系统中。本章将介
绍系统的启动过程,并详细介绍OP-TEE的启动流
程和相关的重要启动节点。5.1 bios.bin的入口函数
使用QEMU运行OP-TEE时首先加载的是编译
生成的bios.bin镜像文件,而bios.bin镜像文件的入
口函数是在bios_qemu_tz_armbiosentry.S文件中定
义的,该文件的入口函数为_start,该文件的主要内
容如下:
.section .text.boot
定义 _start函数,设定第一条指令跳转到reset函数执行
FUNC _start , :
b reset
b . Undef
b . Syscall
b . Prefetch abort
b . Data abort
b . Reserved
b . IRQ
b . FIQ
END_FUNC _start
reset 函数
LOCAL_FUNC reset , :
read_sctlr r0
orr r0, r0, SCTLR_A
write_sctlr r0
设置中断向量表
adr r0, _start
write_vbar r0
重新设定bios在RAM中的地址
mov r0, 0
ldr r1, =__text_start
ldr r2, =__data_end
sub r2, r2, r1
复制bios.bin文件中的__text_start到__data_end到地址为0的起始RAM中
bl copy_blob
跳转到上面重新定位的bios在RAM中的地址 ldr ip, =new_loc
bx ip
new_loc:
重新设定中断向量
adr r0, _start
write_vbar r0
清空BSS段的数据
ldr r0, =__bss_start
ldr r1, =__bss_end
sub r1, r1, r0
bl zero_mem
设定堆栈空间
ldr ip, =main_stack_top;
ldr sp, [ip]
push {r0, r1, r2}
mov r0, sp
ldr ip, =main_init_sec 获取main_init_sec函数地址
blx ip 跳转到main_init_sec函数中执行,加载OP-TEE OS的image
pop {r0, r1, r2}
mov ip, r0 OP-TEE OS的入口地址
mov r0, r1 argument (address of pagable part if != 0)
blx ip 跳转到OP-TEE OS的启动地址
设置Normal World的栈
ldr ip, =main_stack_top;
ldr sp, [ip]
ldr ip, =main_init_ns 获取main_init_ns函数的地址
bx ip 跳转到main_init_ns函数,加载Linux内核的image
END_FUNC reset
复制函数
LOCAL_FUNC copy_blob , :
ldrb r4, [r0], 1
strb r4, [r1], 1
subs r2, r2, 1
bne copy_blob
bx lr
END_FUNC copy_blob
清空内存数据的函数
LOCAL_FUNC zero_mem , :
cmp r1, 0
bxeq lr
mov r4, 0
strb r4, [r0], 1
sub r1, r1, 1
b zero_mem
END_FUNC zero_memmain_init_sec函数用来将Linux内核镜像、OP-
TEE OS镜像、rootfs镜像文件加载到RAM的对应位
置,并且解析出OP-TEE OS的入口地址、Linux内
核的加载地址、rootfs在RAM中的地址和其他相关
信息。main_init_sec函数执行完成后会返回OP-TEE
OS的入口地址以及设备树(device tree,DT)的地
址,然后在汇编代码中通过调用blx指令进入OP-
TEE OS的启动。
OP-TEE启动完成后会重新进入entry.S文件中
继续执行,最终执行main_init_ns函数来启动Linux
内核,在main_init_sec函数中会设定Linux内核的入
口函数地址、DT的相关信息,main_init_ns函数会
使用这些信息来开始Linux内核的加载。
上述两个函数都定义在
bios_qemu_tz_armbiosmain.c文件中。将各种镜像
文件复制到RAM的操作都是通过解析bios.bin镜像
的对应section来实现的,通过寻找特定的section来
确定各镜像文件在bios.bin文件中的位置。5.2 OP-TEE镜像的加载和启动
启动过程中entry.S文件通过汇编调用
main_init_sec函数将optee-os镜像、Linux镜像和
rootfs加载到RAM中,并定位DT的地址信息,以备
Linux和OP-TEE启动使用,这些操作是由
main_init_sec函数进行的,该函数定义在
bios_qemu_tz_armbiosmain.c文件中,其内容如
下:
void main_init_sec(struct sec_entry_arg arg)
{
void fdt;
int r;
定义OP-TEE OS 镜像文件存放的起始地址
const uint8_t sblob_start = __linker_secure_blob_start;
定义OP-TEE OS 镜像文件存放的末端地址
const uint8_t sblob_end = __linker_secure_blob_end;
struct optee_header hdr; 存放OP-TEE OS image头的信息
size_t pg_part_size; OP-TEE OS image除去初始化头部信息的大小
uint32_t pg_part_dst; OP-TEE OS image除去初始化头部信息后在RAM中的起始地址
msg_init; 初始化uart
加载device tree 信息。在qemu工程中,并没有将device tree信息编译到Bios.bin中,而默认存放在DTB_START地址中
fdt = open_fdt(DTB_START, __linker_nsec_dtb_start,__linker_nsec_dtb_end);
r = fdt_pack(fdt);
CHECK(r < 0);
判定OP-TEE OS image的大小是否大于image header的大小
CHECK(((intptr_t)sblob_end - (intptr_t)sblob_start) <
(ssize_t)sizeof(hdr));
将OP-TEE OS image header信息复制到hdr变量中
copy_bios_image(secure header, (uint32_t)hdr, sblob_start,sblob_start + sizeof(hdr));
校验OP-TEE OS image header中的magic和版本信息是否合法
CHECK(hdr.magic != OPTEE_MAGIC || hdr.version != OPTEE_VERSION); msg(found secure header\n);
sblob_start += sizeof(hdr); 将sblob_start的值后移到除去image header的位置
CHECK(hdr.init_load_addr_hi != 0); 检查OP-TEE OS的初始化加载地址是否为零
获取OP-TEE OS除去 image header和ini操作部分代码后的大小
pg_part_size = sblob_end - sblob_start - hdr.init_size;
确定存放OP-TEE OS除去image header和init操作部分代码后存放在RAM中的地址
pg_part_dst = (size_t)TZ_RES_MEM_START + TZ_RES_MEM_SIZE - pg_part_size;
将存放OP-TEE OS除去image header和init操作部分后的内容复制到RAM中
copy_bios_image(secure paged part,pg_part_dst, sblob_start + hdr.init_size, sblob_end);
sblob_end -= pg_part_size; 重新计算sblo_end的地址,剔除page part
将pg_part_dst赋值给arg中的paged_part以备跳转执行OP-TEE OS使用
arg->paged_part = pg_part_dst;
将hdr.init_load_addr_lo赋值给arg中的entry,该地址为op-TEE OS的入口地址
arg->entry = hdr.init_load_addr_lo;
将OP-TEE OS的实际image复制到起始地址为hdr.init_load_addr_l的RAM地址中
copy_bios_image(secure blob, hdr.init_load_addr_lo, sblob_start,sblob_end);
复制kernel image、rootfs到RAM,并复制device tree到对应地址,以备被kernel使用
copy_ns_images;
将device tree的地址赋值给arg->fdt变量,以备OP-TEE OS启动使用
arg->fdt = dtb_addr;
msg(Initializing secure world\n);
}
main_init_sec函数执行后将会返回一个
sec_entry_arg的变量,该变量包含启动OP-TEE OS
的入口地址、DT的地址以及paged_table的地址。
sec_entry_arg变量将会被entry.S文件用来启动OP-
TEE OS,entry.S会将OP-TEE OS的入口地址保存在
r0寄存器中,而paged_table部分的起始地址会被保
存在r1寄存器中,将r0赋值给ip,最终entry.S文件
通过执行blx ip指令进入OP-TEE OS的入口函数中去
执行OP-TEE OS的启动。当OP-TEE OS启动完成之
后,entry.S文件会调用main_init_ns函数来启动Linux内核。待Linux内核启动完成之后,整个系统
也就启动完成。5.3 Linux内核镜像的加载和启动
entry.S文件通过调用main_init_ns函数来完成对
Linux内核的启动,该函数会调用call_kernel函数来
完成Linux内核的启动,调用call_kernel函数时传入
的参数说明如下:
kernel_entry:Linux内核在RAM中的入口地
址,该值在main_init_sec函数中通过调用
copy_ns_images函数来进行赋值。
dtb_addr:DT存放的位置,该值在
main_init_sec函数中通过调用copy_ns_images函数来
进行赋值。
rootfs_start:复制到RAM中的rootfs的起始地
址,该值在main_init_sec函数中被赋值。
rootfs_end:复制到RAM中的rootfs的末端地
址,该值在main_init_sec函数中被赋值。
call_kernel函数定义在
bios_qemu_tz_armbiosmain.c文件中,该函数的内
容和相关注释如下:typedef void (kernel_ep_func)(uint32_t a0, uint32_t a1, uint32_t a2);
static void call_kernel(uint32_t entry, uint32_t dtb,uint32_t initrd, uint32_t initrd_end)
{
定义指向Linux内核入口地址的函数指针,并将函数指针的地址指向带入参数entry的位置
kernel_ep_func ep = (kernel_ep_func)entry;
void fdt = (void )dtb; 定义device tree的地址并赋值
const char cmdline[] = COMMAND_LINE; 定义存放command line的变量并进行赋值
int r;
const uint32_t a0 = 0;
MACH_VEXPRESS see linuxarcharmtoolsmach-types
const uint32_t a1 = 2272;
获取device tree的信息
r = fdt_open_into(fdt, fdt, DTB_MAX_SIZE);
CHECK(r < 0);
设置device tree中的相关节点、initrd的起始地址、initrd的末端地址、bootargs
setprop_cell(fdt, chosen, linux,initrd-start, initrd);
setprop_cell(fdt, chosen, linux,initrd-end, initrd_end);
setprop_string(fdt, chosen, bootargs, cmdline);
r = fdt_pack(fdt);
CHECK(r < 0);
打印相关信息
msg(kernel command line: \%s\\n, cmdline);
msg(Entering kernel at 0x%x with r0=0x%x r1=0x%x r2=0x%x\n,(uintptr_t)ep, a0, a1, dtb);
带入device tree信息和其他相关参数,调用Linux内核的入口函数,进而执行Linux内核的启动
ep(a0, a1, dtb);
}
ep的值是Linux内核的入口函数指针,所以最
终调用ep(a0,a1,dtb)函数就能开始Linux内核的启
动过程。5.4 rootfs的挂载
启动Linux系统时会加载rootfs,rootfs在启动
Linux系统之前会被拷贝到相应的内存地址中,系
统在启动Linux时会告知Linux内核rootfs在内存中的
地址,Linux内核启动时会到该地址中去获取rootfs
的内容,挂载起来作为Linux系统的根文件系统使
用。5.5 OP-TEE驱动的启动
在OP-TEE工程中,OP-TEE在REE侧的驱动会
被编译到Linux内核镜像中,Linux系统在启动的过
程中会自动挂载OP-TEE的驱动,驱动挂载过程中
会创建devtee0和devteepriv0设备,其中devtee0
设备将会被REE侧的用户空间的库(libteec)使
用,devteepriv0设备将会被系统中的常驻进程
tee_supplicant使用,并且在OP-TEE驱动的挂载过程
中会建立正常世界状态与安全世界状态之间的共享
内存,用于OP-TEE驱动与OP-TEE之间的数据共
享,同时还会创建两个链表,分别用于保存来自
OP-TEE的RPC请求和发送RPC请求的处理结果给
OP-TEE。5.6 tee_supplicant的启动
tee_supplicant是Linux系统中的常驻进程,该进
程用于接收和处理来自OP-TEE的RPC请求,并将
处理结果返回给OP-TEE。来自OP-TEE的RPC请求
主要包括socket操作、REE侧文件系统操作、加载
TA镜像文件、数据库操作、共享内存分配和注册
操作等。该进程在Linux系统启动过程中被自动创
建,在编译时,该进程的启动信息会被写入
到etcinit.d文件中,而该进程的可执行文件则被保
存在文件系统的bin目录下。该进程中会使用一个
loop循环接收来自OP-TEE的远程过程调用
(Remote Procedure Call,RPC)请求,且每次获取
到来自OP-TEE的RPC请求后都会自动创建一个线
程,用于接收OP-TEE驱动队列中来自OP-TEE的
RPC请求,之所以这么做是因为时刻需要保证在
REE侧有一个线程来接收OP-TEE的请求,实现RPC
请求的并发处理。5.7 小结
本章介绍了使用QEMU的方式运行OP-TEE的
启动过程,介绍了系统是如何启动的,在启动过程
中如何加载相应的镜像文件。即使在非QEMU方式
运行OP-TEE时,OP-TEE的驱动和tee_supplicant同
样会被挂载和启动,这属于运行OP-TEE不可或缺
的一部分。第6章 安全引导功能及ATF的启动过
程
安全引导(Secure Boot)功能是指在系统的整
个启动过程中,使用链式验证电子签名的方式来验
证系统中重要镜像文件的可靠性,然后再加载镜像
文件的引导过程。安全引导功能可以保护二级厂商
系统的独立性和完整性。在ARMv8架构中ARM提
供了ARM可信固件(ATF)。Bootloader、Linux内
核、TEE OS的启动都由ATF来加载和引导。对于
ARMv8,Bootloader、Linux内核和TEE OS镜像文
件的验签工作都是在ATF中完成的。本章将介绍安
全引导功能的原理以及ATF的启动过程。6.1 安全引导的作用
安全引导可用于保证系统的完整性,防止系统
中重要镜像文件被破坏或替换。一般情况下,安全
引导需要保护系统的BootLoader镜像文件、TEE镜
像文件、Linux内核镜像文件、Recover镜像文件以
及在ARMv8中使用的ATF镜像文件。将TEE镜像文
件的加载操作加入安全引导功能中可阻止黑客通过
替换TEE镜像文件的方式来窃取被TEE保护的重要
资料。当前使用ARM芯片的系统中大部分使能了安
全引导功能,该功能对于用户的最直接感受就是,当用户非法刷入其他厂商的ROM后手机无法正常启
动,这是因为非法刷机将导致系统中的重要镜像文
件被替换,系统在启动过程中对镜像文件的电子验
签失败,如果BootLoader验证失败,则系统在进入
BootLoader阶段之前就会挂死。6.2 安全引导的原理
安全引导功能的原理就是采用链式验签的方式
启动系统,也就是在系统启动过程中,在加载下一
个阶段的镜像之前都会对需要被加载的镜像文件进
行电子验签,只有验签操作通过后,该镜像才能被
加载到内存中,然后系统才会跳转到下一个阶段继
续执行,整个验签链中的任何一环验签失败都会导
致系统挂死,系统启动过程中的第一级验签操作是
由ChipRom来完成的。只要芯片一出厂,用户就无
法修改固化在芯片中的这部分代码,因此无法通过
修改第一级验签结果来关闭安全引导功能。而且验
签操作使用的RSA公钥或者哈希值将会被保存在
OTPefuse中,该区域中的数据一般只有ChipRom和
TEE能够读取且无法被修改。RSA公钥或者哈希值
将会在产品出厂之前被写入到OTPefuse中,而且不
同厂商使用的密钥会不一样。
在谷歌的安全引导功能白皮书中提出了安全引
导功能实现方案的设计建议。谷歌建议将镜像文件
的电子签名信息和验签使用的RSA公钥保存在电子
证书中,系统在启动的过程中首先会验证电子证书
的合法性,如果验证通过则需从电子证书中获取签
名信息和RSA公钥,然后再利用它们对镜像文件进
行验证。整个验证过程就是先验证证书,验证证书通过后再去验证镜像文件的合法性。但是在实际实
现过程中,大多数芯片厂商是将签名信息与需要被
验签的镜像文件打包在一起,而RSA公钥则会被打
包到执行验证操作的镜像文件中。
不同厂商可能会对镜像文件进行加密操作,使
保存在设备中的镜像文件都是以密文的形式存在。
在启动过程中,首先会验证密文镜像文件的合法性
然后再进行解密镜像文件的操作,这些都完成后才
会将明文的镜像文件加载到内存中然后再执行跳转
操作。6.2.1 ARMv7安全引导的过程
对于安全引导功能的实现和验证过程各家芯片
公司的方案都不一样,这是由该芯片的启动流程以
及启动所需镜像文件来决定的,但都会遵循链式验
签启动的原则。ARMv7架构并没有使用ATF,系统
的启动流程与以前一样使用BootLoader来引导Linux
内核和TEE OS。安全引导的启动流程如图6-1所
示。图6-1 安全引导的启动流程
系统启动过程使用链式验签的方式进行引导,其中任何一环验签失败都会导致系统启动失败,为
防止通过替换ramdisk来修改根文件系统中的内容,一般将ramdisk与Linux内核打包在同一个镜像文件
中,而且该镜像文件需要待验签通过后才可被使
用。签名信息一般是对镜像文件的内容进行哈希计
算获取摘要后再对该摘要使用RSA私钥进行电子签
名来获得,验证时同样会计算需要被引导的镜像文
件的摘要,然后使用该摘要、签名信息以及RSA公
钥进行RSA算法的验证。6.2.2 ARMv8安全引导的过程
ARMv8架构之后ARM提供了ATF,BootLoader、TEE镜像文件、Linux内核镜像文件、recovery镜像文件都是由ATF来进行引导和加载而
不是由ChipRom来完成的。ChipRom只会去验证
ATF中bl1的合法性,后续引导过程同样也是按照链
式验签的方式进行,符合TBBR规范。读者可使用
git命令从gitHub上获取ATF的所有源代码[1]。在
ARMv8架构中整个安全引导的流程如图6-2所示。图6-2 ARMv8的Secure Boot流程
ARMv8架构中引入了ATF,同时在ATF中提供
了安全引导的功能,BootLoader镜像、Linux内核、recovery镜像和TEE OS镜像文件的签名方式都由
ATF决定。当然开发者也可以对ATF进行定制化,修改ATF中的验签过程,但是修改后的验签方案需
要符合TBBR规范。
[1] ATF的git仓库链接可参阅ATF源代码链接:
https:github.comlinaro-swgarm-trusted-firmware。6.3 ATF的启动过程
ATF的启动过程根据ARMv8的运行模式
(AArch32AArch64)会有所不同,但基本一致。
在AArch32中是不会去加载bl31而是将EL3或者
Monitor模式的运行代码保存在bl32中执行。在
AArch64中,ATF的完整启动流程如图6-3所示。
图6-3 AArch64模式的ATF启动流程
在上述启动过程中,从一个镜像跳转到另外一
个镜像文件执行的方式各不相同,以下为镜像跳转的过程和方式说明。
1.bl1跳转到bl2执行
在bl1完成了将bl2镜像文件加载到RAM中的操
作、中断向量表的设定以及其他CPU相关设定后,bl1_main函数会解析出bl2镜像文件的描述信息,获
取入口地址,并设定下一个阶段的cpu上下文。这
些操作完成之后,调用el3_exit函数来实现bl1到bl2
的跳转,进入bl2中开始执行。
2.bl2跳转到bl31执行
在bl2中将会加载bl31、bl32、bl33的镜像文件
到对应权限的内存中,并将该三个镜像文件的描述
信息组成一个链表保存起来,以备bl31启动bl32和
bl33使用。在AArch64中,bl31为EL3的执行软件,其运行时的主要功能是对安全监控模式调用
(smc)指令和中断处理,运行在ARM的Monitor模
式中。
bl32一般为TEE OS镜像文件,本章以OP-TEE
为例进行说明。
bl33为正常世界状态的镜像文件,例如uboot、EKD2等。当前该部分为BootLoader部分的镜像文
件,再由BootLoader来启动Linux内核镜像。从bl2跳转到bl31是通过带入bl31的入口点信息
作为参数,然后调用安全监控模式调用指令,触发
在bl1中设定的安全监控模式调用请求,该请求处理
完成后会将中央处理器的执行权限交给bl31,并跳
转到bl31中去执行。
3.bl31跳转到bl32执行
在bl31中会执行runtime_service_inti函数,该函
数会调用注册到EL3中所有服务的初始化函数,其
中有一个服务项就是TEE服务,该服务项的初始化
函数会将TEE OS的初始化函数赋值给bl32_init变
量,当所有服务项执行完初始化后,在bl31中会调
用bl32_init执行的函数来跳转到TEE OS中并开始执
行TEE OS的启动。
4.bl31跳转到bl33执行
当TEE-OS镜像启动完成后会触发一个ID为
TEESMC_OPTEED_RETURN_ENTRY_DONE的安
全监控模式调用,该调用是用来告知EL3 TEE OS
镜像已经完成了初始化,然后将CPU的状态恢复到
bl31_init的位置继续执行。
bl31通过遍历在bl2中记录的所有镜像信息的链
表来找到需要执行的bl33的镜像。然后通过获取到bl33镜像的信息,设定下一个阶段的CPU上下文, ......
您现在查看是摘要介绍页, 详见PDF附件(18090KB,786页)。





