OP-TEE
由于工作原因接触过一些使用TEE代替HSM来实现密钥存储、密钥对生成的方案,因此想了解一下开源的TEE方案与TEE供应商提供的方案有什么不同。
关于OP-TEE
OP-TEE是设计为与在Arm上运行的非安全Linux内核配合使用的一种受信任执行环境(TEE),Cortex-A 内核使用 TrustZone 技术。 OP-TEE 实现 TEE 内部核心 API v1.3.1(向可信应用程序公开的 API)和 TEE 客户端 API v1.0(描述如何与 TEE 通信的 API)。这些 API 在 GlobalPlatform API 规范中定义。
非安全操作系统在 TEE 规范中被称为丰富执行环境 (REE)。它通常是 Linux 操作系统风格,如 GNU/Linux 发行版或 AOSP。
OP-TEE 的设计主要依靠 Arm TrustZone 技术作为底层硬件隔离机制。然而,它被设计为与任何适合 TEE 概念和目标的隔离技术兼容,例如作为虚拟机运行或在专用 CPU 上运行。
OP-TEE 的主要设计目标是:
- 隔离 - TEE 提供与非安全操作系统的隔离,并使用底层硬件支持保护加载的可信应用程序 (TA) 彼此隔离。
- 占用空间小 - TEE 应保持足够小,以便驻留在基于 Arm 的系统上的合理数量的片上内存中。
- 可移植性 - TEE 的目标是轻松插入不同的架构和可用的硬件,并且必须支持各种设置,例如多个客户端操作系统或多个 TEE。
OP-TEE组件
OP-TEE 分为多个组件:
- 安全特权层,在 Arm 安全 PL-1 (v7-A) 或 EL-1 (v8-A) 级别执行。
- 一组专为受信任的应用程序需求而设计的安全用户空间库。
- Linux 内核 TEE 框架和驱动程序
- 根据 GlobalPlatform TEE 客户端 API 规范设计的 Linux 用户空间库。
- Linux 用户空间请求者守护进程 (tee-supplicant) 负责 TEE 操作系统所需的远程服务。
- 测试套件 (xtest),用于进行回归测试并测试 API 实现的一致性。
- 包含几个简单的主机和 TA 示例的 git 示例。
- 还有一些构建脚本、调试工具,以简化其集成以及可信应用程序和安全服务的开发。
这些组件可从 github 中获取。主要是build、optee_os、optee_client、optee_test、optee_examples和Linux内核TEE框架。
TEE架构
核心
在此部分继续之前,需要先引用 https://www.cnblogs.com/ppddcsz/p/16858363.html 这篇文章的部分内容来对TrustZone的系统架构有一个更深入的了解。
TEE架构
TrustZone处理器结构
-
每个处理器核心都提供了两个虚拟核心,一个安全一个不安全,还有一个在他们之间进行上下文切换的 monitor mode:
发送到主系统总线上的NS位的值是由执行指令或数据访问的虚拟内核的身份标识间接得出的。非安全的虚拟处理器只能访问非安全系统资源,但安全虚拟处理器可以看到所有资源。
NS位指的是“非安全位”(Not Secure bit),具体来说,非安全的虚拟处理器(即NS位为0)只能访问非安全的系统资源,而安全虚拟处理器(NS位为1)则可以访问系统中的所有资源。 -
world切换:这两个虚拟处理器以时间切片的方式执行,在更改当前运行的虚拟处理器时,上下文将通过名为monitor mode的新核心模式进行切换。物理处理器进入monitor mode时可以通过执行特定指令的软件、SMC指令、硬件异常机制的子集进行触发。如果处理器不在监控模式下,它正在执行的世界由系统控制协处理器CP15中安全配置寄存器(SCR)中的NS位指示;如果处于监控模式下,无论SCR NS位的值是多少,处理器总是在安全的世界中执行;但是如果SCR NS位设置为1,则对banked CP15寄存器的操作将访问正常世界的副本。
-
一级保护内存系统:
- MMU:TrsutZone中硬件提供两个虚拟MMU,每个虚拟处理器一个。每个世界都有一组本地转换表,使它们能够独立地控制虚拟地址到物理地址的映射。为在两个世界之间实现高效的上下文切换,arm处理器会标记TLB中的条目,TLB缓存了地址转换表的遍历结果,并使用执行遍历的世界的标识,这允许非安全和安全条目在TLB中共存,从而加快切换,因为无需刷新TLB条目。
MMU指的是Memory Management Unit,主要用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权、多任务多进程操作系统。
如果处理器启用了MMU,CPU执行单元发出的内存地址将被MMU截获,从CPU到MMU的地址称为虚拟地址(Virtual Address,以下简称VA),而MMU将这个地址翻译成另一个地址发到CPU芯片的外部地址引脚上,也就是将VA映射成PA(Physical Address,以下简称PA)
TLB:因为从虚拟地址到物理地址的变换过程是通过查询页表完成的,而页表又存储在内存中,如果每次程序执行时去读取内存中的数据获取物理地址,代价很大。而程序在执行过程中可能只对页表中的少数几个单元进行访问,根据这一特点,采用一个容量更小(通常为8~16个字)、访问速度和CPU中通用寄存器相当的存储器件来存放当前访问需要的地址变换条目。这个小容量的页表称为TLB(Translation Lookaside buffer)。 - cache:目标是实现支持缓存中两种安全状态的数据,这样可以消除切换世界的时候需要刷新缓存的需要。解决方法是处理器缓存扩展了一个额外的标志位,该位录访问内存的事务的安全状态。
- 世界共享内存:允许安全世界直接访问非安全缓存。提供性能
- MMU:TrsutZone中硬件提供两个虚拟MMU,每个虚拟处理器一个。每个世界都有一组本地转换表,使它们能够独立地控制虚拟地址到物理地址的映射。为在两个世界之间实现高效的上下文切换,arm处理器会标记TLB中的条目,TLB缓存了地址转换表的遍历结果,并使用执行遍历的世界的标识,这允许非安全和安全条目在TLB中共存,从而加快切换,因为无需刷新TLB条目。
-
安全中断:直接向monitor捕获IRQ和FIQ的能力(不需要任何一个世界的代码干预)可以为安全中断源创建一个灵活的中断模型。ARM推荐的模型是使用IRQ作为非安全世界的中断源,FIQ作为安全世界的中断源。CPSR(Current Program Status Register)是ARM处理器中的一个程序状态寄存器,用于存储当前程序的状态信息,包括处理器模式、中断屏蔽状态等。在中断处理过程中,处理器会根据CPSR中的状态信息来确定如何响应和处理中断请求。
- IRQ(Interrupt Request)是一种常规的中断请求,用于处理一般的中断事件。当外部设备或其他系统组件需要引起处理器的注意时,会发出IRQ请求,处理器会在合适的时候响应这些中断请求,并调用相应的中断处理程序来处理中断事件。
- FIQ(Fast Interrupt Request)是一种优先级更高、响应速度更快的中断请求。FIQ通常用于处理紧急、时间敏感或者需要快速响应的中断事件。与IRQ相比,FIQ在处理器内部有更快的响应路径和更多的专用资源,因此可以更快速地处理中断事件。
启用TrustZone的处理器实现了三组异常向量表。其中一个表用于正常世界,一个用于安全世界,另一个用于监视模式。
-
安全处理器:协处理器CP15中的敏感的配置选项,或全局应用于核心的配置选项,只能由安全世界软件编写,非安全世界可以读取。
-
多处理器系统:集群中的处理器可以配置为以对称多处理(SMP)模式或非对称多处理(AMP)模式执行。当处理器以SMP模式执行时,集群的Snoop控制单(SCU)将透明地将跨SMP处理器共享的数据保存在L1数据缓存中。当处理器以AMP模式执行时,如果需要,执行软件必须手动保持内存一致性。多处理器集群中的每个处理器都有一个正常世界和一个安全世界。每个虚拟处理器都可以独立控制它们的MMU配置。
TEE软件架构
- 软件架构
- 安全操作系统:可以模拟多个独立的安全世界应用程序的并发执行,运行时下载新的安全应用程序,以及完全独立于正常世界环境的安全世界任务。使用MMU讲安全世界内存划分为多个用户空间沙盒,只要安全世界内核软件正确实现,多个安全任务可以不需要信任对方而同时运行。这种设计可以增强安全任务之间的逻辑隔离。
- 同步库:安全世界中的简单代码库可以解决很多应用的任务,这个代码库完全由非安全世界操作系统的软件调用来进行调度和管理。
- 中间方法:在这两个极端之间有一系列的选择。例如,一个安全世界的多任务操作系统可以被设计成没有专门的中断源,因此可以由普通世界提供一个虚拟中断。
- 安全启动系统:所有安全世界软件和潜在的正常世界软件生成一个信任链,该信任链是从不易被篡改的信任根建立的。
-
启动流程:开机时启用TrustZone的处理器在安全环境中启动。这使得任何敏感的安全检查都能在普通软件有机会修改系统的任何方面之前运行。开机流程:执行基于ROM的引导加载程序 -> 闪存中的设备引导加载程序 -> 安全世界操作系统初始化 ->切换到非安全世界 -> 正常世界引导加载程序 -> 启动正常的操作系统 -> 系统运行。
-
安全方案:安全启动方案将加密检查添加到安全世界启动进程的每个阶段。此过程旨在维护执行的所有安全世界软件映像的完整性,防止任何未经授权或恶意修改的软件运行。
- 密码签名协议:基于公钥签名算法的协议。受信任的供应商使用他们的私钥(PrK)生成要部署的代码的签名,并将其与软件二进制文件一起推送到设备上。设备包含供应商的公钥(PuK),该公钥可用于验证二进制文件是否未被修改,以及该二进制文件是否由相关的受信任供应商提供。
- 信任链:从一个隐式可信组件开始,在执行之前,可以对每个其他组件进行身份验证。信任根位于ROM中,不容易被修改或替换。
- 片内/片外安全世界:最简单的防御hack攻击的方法是将任何安全世界资源的执行放在SoC片内内存位置。如果代码和数据从未暴露在SoC封装之外,则很难窥探或修改数据值。
-
- 监控模式软件:管理安全和非安全处理器状态之间的切换,类似于上下文切换。正常世界进入监控模式需通过以下异常实现:中断、外部中止或通过SMC指令的显式调用;从安全世界进入监控模式除了通过正常世界可用的异常机制外,还可以通过直接写入CPSR来实现。
- 上下文切换:监视器保存的任何安全状态都应该保存在安全内存的某个区域中,这样正常世界就不会对其进行篡改。可以通过过SMC指令、惰性上下文切换(仅在必要时保存协处理器的上下文,而不是在每次操作系统上下文切换或TrustZone世界切换时都保存)来实现世界切换。
- 安全软件和多处理器系统:安全世界软件实现可以选择实现一个单处理器安全世界(不具备多处理能力)。
- 将安全世界的执行固定在一个特定的处理器上:在这种设计中,与安全世界通信的普通世界驱动程序通常需要使用处理器间通信将使用安全世界的请求路由到正确的处理器。此外,必须防止普通世界通过安全世界不使用的处理器上的监控软件(monitor)造成恶意的世界切换。
- 将安全世界的执行固定在一个特定的处理器上:在这种设计中,与安全世界通信的普通世界驱动程序通常需要使用处理器间通信将使用安全世界的请求路由到正确的处理器。此外,必须防止普通世界通过安全世界不使用的处理器上的监控软件(monitor)造成恶意的世界切换。
中断处理
本节介绍 optee_os 如何根据 SMC 异常和中断通知处理世界执行上下文的切换。中断通知是 IRQ/FIQ 异常,这也可能意味着世界执行上下文的切换:正常世界到安全世界,或安全世界到正常世界。
当正常世界调用安全世界时,正常世界执行SMC指令。 SMC 异常始终被监视器捕获。如果相关服务针对受信任的操作系统,监视器将切换到 OP-TEE 操作系统世界执行。当安全世界返回正常世界时,OP-TEE OS 会执行一个 SMC,监控器会捕获该 SMC,并切换回正常世界。
当 Arm GIC (Arm Generic Interrupt Controller,Arm通用中断控制器)到达 OP-TEE OS 中断异常向量,它发出安全中断信号。如果安全世界正在执行,OP-TEE OS 将直接从其异常向量处理中断。如果安全中断引发时正常世界正在执行,则监视器向量必须处理异常并调用 OP-TEE OS 来为中断服务。
当 Arm GIC 发出非安全中断信号时,它应到达正常世界中断异常向量。如果正常世界正在执行,它将直接处理异常向量中的异常。如果在非安全中断发生时安全世界正在执行,OP-TEE OS将通过监视器暂时返回正常世界,让正常世界服务中断。
异常向量是指示处理器处理异常情况的一组固定地址,当处理器检测到发生异常,例如访问非法内存地址、执行非法指令、中断请求等情况时,它会跳转到对应的异常向量地址,以执行相应的异常处理程序。
核心异常向量
监控向量在 AArch64 中为 VBAR_EL3 ,在 Armv7-A/AArch32 中为 MVBAR 。当正常世界或安全世界正在执行时可以访问监视器。监视器通过 SCR_NS 了解正在执行的安全状态。
可以通过 SMC 异常、IRQ 或 FIQ 异常(所谓的中断)以及异步中止访问监控器。显然,监控中止(数据、预取、undef)是监控器执行的本地中止。
监视器可以位于 OP-TEE OS 外部(例 CFG_WITH_ARM_TRUSTED_FW=y )。如果没有,则提供本地安全监视器 core/arch/arm/sm 。 Armv7-A 平台应使用 OP-TEE OS 安全监视器。 Armv8-A 平台可能依赖于可信固件 A。
当在监视器外部执行时,系统要么在正常世界( SCR_NS=1 )中执行,要么在安全世界( SCR_NS=0 )中执行。每个世界都有自己的异常向量表(状态向量):
- VBAR_EL2 or VBAR_EL1 non-secure or VBAR_EL1 secure for AArch64.
- HVBAR or VBAR non-secure or VBAR secure for Armv7-A and AArch32.
所有 SMC 异常都被捕获在监视器向量中。 IRQ/FIQ 异常可以被捕获在监视器向量中或执行世界的状态向量中。
当正常世界正在执行时,系统配置为路由:
- 将安全中断路由至监控器,并转发至 OP-TEE 操作系统
- 非安全中断将转发到正在执行的世界异常向量。
当安全世界执行时,系统配置为路由: - 执行 OP-TEE OS 异常向量的安全和非安全中断。 OP-TEE OS 应将非安全中断转发到正常世界。
可信固件-A (TF-A) 是适用于 Arm A-Profile 架构(Armv8-A 和 Armv7-A)的安全世界软件的参考实现,包括异常级别 3 (EL3) 安全监视器。它为 AArch32 或 AArch64 执行状态下的安全世界启动和运行时固件的产品化提供了合适的起点。
TF-A实现了Arm接口标准,包括: - Power State Coordination Interface (PSCI)
- Trusted Board Boot Requirements CLIENT (TBBR-CLIENT)
- SMC Calling Convention
- System Control and Management Interface (SCMI)
- Software Delegated Exception Interface (SDEI)
后续会更新与之相关的文章
加密
本节描述了 TEE 加密操作 API 的实现方式、如何在编译时配置默认加密提供程序以及如何将其替换为其他实现。
从可信应用程序到实际的加密算法有几个层次。大多数加密代码在 TEE 核心内以内核模式运行。以下是加密 API 的典型调用的示意图。方括号([1]、[2]…)中的数字指的是以下部分。
- some_function() (Trusted App) -
[1] TEE_*() User space (libutee.a)
------- utee_*() ----------------------------------------------
[2] tee_svc_*() Kernel space
[3] crypto_*() (libtomcrypt.a and crypto.c)
[4] /* LibTomCrypt */ (libtomcrypt.a)
从可信应用程序到实际的加密算法有几个层次,每个层次都有不同的功能和实现方式:
- 可信应用程序(Trusted App)中调用some_function()来执行加密操作。
- some_function()中调用TEE_*()函数,这些函数位于用户空间(User space)的libutee.a库中。
- TEE_()函数再调用utee_()函数,这些函数实现在内核空间(Kernel space)中,通过系统调用(SVC指令)切换到OP-TEE内核中的相应系统服务。
- 在OP-TEE内核中,utee_()函数最终调用了tee_svc_()函数,这些函数执行加密操作的具体功能,如消息摘要、对称密码、MAC等,它们的实现在libtomcrypt.a和crypto.c中,基于LibTomCrypt库。
[1] TEE 加密操作 API
OP-TEE 实现了 TEE 内部核心 API 中 GlobalPlatform 协会定义的加密操作 API。这包括跨越各种加密需求的加密功能:消息摘要、对称密码、消息认证码 (MAC)、认证加密、非对称操作(加密/解密或签名/验证)、密钥派生和随机数据生成。这些函数构成了 TEE 加密操作 API。
内部 API 在 tee_api_operations.c 中实现,它被编译成静态库: ${O}/ta_arm{32,64}-lib/libutee/libutee.a
utee_* 函数在 utee_syscalls.h 中声明并在 utee_syscalls_asm.S 中实现。它们是简单的系统调用包装器,使用 SVC 指令切换到 OP-TEE 内核中相应的系统服务。
[2] 加密服务
所有与加密相关的系统调用都在 tee_svc_cryp.h 中声明并在 tee_svc_cryp.c 中实现。除了处理用户/内核接口所需的常规工作(检查参数以及在用户和内核空间之间复制内存缓冲区)之外,系统调用还调用一个私有抽象层:Crypto API,它在 crypto.h 中声明。它有两个主要目的:
- 允许替代实现,例如硬件加速版本。
- 提供一种简单的方法来在编译时禁用某些算法系列以节省空间。请参阅下面的 LibTomCrypt。
[3] crypto_*()
crypto_*() 函数实现实际的算法和辅助函数。 TEE Core 有一个该接口的全局主动实现。默认实现主要基于 LibTomCrypt,如下所示:
点击查看代码
/*
* Default implementation for all functions in crypto.h
*/
#if !defined(_CFG_CRYPTO_WITH_HASH)
TEE_Result crypto_hash_get_ctx_size(uint32_t algo __unused,
size_t *size __unused)
{
return TEE_ERROR_NOT_IMPLEMENTED;
}
...
#endif /*_CFG_CRYPTO_WITH_HASH*/
点击查看代码
#if defined(_CFG_CRYPTO_WITH_HASH)
TEE_Result crypto_hash_get_ctx_size(uint32_t algo, size_t *size)
{
/* ... */
return TEE_SUCCESS;
}
#endif /*_CFG_CRYPTO_WITH_HASH*/
如上所示,可以禁用一系列算法,并且 crypto.c 将提供默认的 null 实现,该实现将返回 TEE_ERROR_NOT_IMPLEMENTED。
公钥/私钥格式
crypto.h 使用特定于实现的类型来保存非对称算法的关键数据。例如,RSA 公钥的表示方式如下:
点击查看代码
struct rsa_public_key {
struct bignum *e; /*用于存储RSA公钥的公开指数 Public exponent */
struct bignum *n; /*用于存储RSA公钥的模数 Modulus */
};
这也是这些密钥在 TEE 对象属性中的存储方式(在本例中为 TEE_ATTR_RSA_PUBLIC_KEY )。 struct bignum 是一种不透明类型,仅底层实现已知。 struct bignum_ops 提供了一些函数,使得系统服务可以操作这种类型的数据。这包括分配/释放、复制以及与大端二进制格式之间的转换。
点击查看代码
struct bignum *crypto_bignum_allocate(size_t size_bits);/*用于分配指定位数的大整数内存空间*/
TEE_Result crypto_bignum_bin2bn(const uint8_t *from, size_t fromsize,
struct bignum *to);/*将二进制数据转换为大整数格式。*/
void crypto_bignum_bn2bin(const struct bignum *from, uint8_t *to);/*将大整数数据转换为二进制格式。*/
/*...*/
[4] LibTomCrypt
如果它某些算法不被需要,它们可能会在编译时被禁用,以减小 OP-TEE 映像的大小并减少其内存使用量。这是通过设置适当的配置变量来完成的。例如:
点击查看代码
$ make CFG_CRYPTO_AES=n # disable AES only
$ make CFG_CRYPTO_{AES,DES}=n # disable symmetric ciphers
$ make CFG_CRYPTO_{DSA,RSA,DH,ECC}=n # disable public key algorithms
$ make CFG_CRYPTO=n # disable all algorithms
添加新的基于软件的加密实现
要添加新的基于软件的实现,应使用 core/lib/libtomcrypt 中的默认实现与 core/crypto 中的内容相结合作为参考。添加新的加密货币提供商时需要考虑以下主要事项:
将新的加密提供商的代码放置在core/lib目录下的自己的目录中。如果这些代码不受加密提供程序的选择影响(即无论使用哪个加密提供程序(如AES-GCM)都可以使用),则可以将其放在core/lib目录中。
在添加新的加密实现时,应该参考core/lib/libtomcrypt中的默认实现和core/crypto中的内容。这样可以确保新的实现与现有的框架相匹配,并且符合GlobalPlatform规范。
避免修改 tee_svc_cryp.c。不应该需要它
虽然不是所有加密货币系列都需要定义,但所有加密货币系列都应符合GlobalPlatform规范。这意味着添加新的加密货币提供商时,需要确保其符合相关的规范和标准。
如果打算使某些算法可选,建议尝试重用与默认实现相同的配置变量名称。这样可以确保配置变量的一致性和易于理解。
[5] 支持加密 IC
OP-TEE支持一些加密协处理器和安全元件,这些设备可以通过通用加密驱动程序接口连接到TEE的加密通用API。
通用加密驱动程序接口位于core/drivers/crypto/crypto_api目录下,当要添加对新设备的支持时,需要按照这个接口规范来实现。
OP-TEE目前不支持GP TEE安全元件API,因此对于某些安全元件(如NXP EdgeLock® SE05x),其访问是通过向设备呈现单个会话的加密操作API实现的。
这个会话既可以通过PKCS#11接口与正常世界共享,也可以通过更通用的接口(libseetec)进行共享,后者允许客户端直接向设备发送应用协议数据单元(APDU)。
请注意,加密协处理器不一定符合 OP-TEE 健全性测试套件 ( optee_test) 测试和涵盖的所有 GP 要求。在不支持加密操作的情况下(即:SE05x 未实现所有 RSA 密钥大小),我们选择在构建时禁用这些特定测试,而不是让它们失败。
某些加密协处理器可能在密钥大小和支持的密码范围方面存在限制。例如,AMD/Xilinx Versal ACAP 加密驱动程序可能对密钥大小有限制,而 NXP SE5X HSM 模块可能缺乏对 RSA 或 ECC 的支持。在这种情况下,特别是在处理不受支持的密钥大小时,可能需要求助于密码的软件实现,通常使用 LibTomCrypt。
注意:虽然 OP-TEE 支持的硬件安全模块或加密硬件处理器可能会获得 3 级 FIPS 140-2 认证,但某些算法的软件实现的 OP-TEE 可能回退到无法获得超过 2 级的认证。
NXP SE05X 系列安全元件
该系列 I2C 总线设备由位于 core/drivers/crypto/se050 的 se050 加密驱动程序支持。在 REE 启动之前,使用 OP-TEE 支持的 I2C 平台设备驱动程序之一建立与设备的会话。一旦 REE 启动,加密驱动程序就可以配置为使用 REE 中的 I2C 驱动程序(通过 RPC 服务)或继续使用 OP-TEE 中的驱动程序。
除非安全元件拥有I2C总线(总线上没有其他元件,没有运行时电源管理等),建议通过正常世界路由所有流量。与设备的初始通信并不数据密集,因此使用较慢的I2C驱动程序(可能不使用DMA通道)并不会对性能造成太大影响;但一旦客户端开始大量请求设备,情况就会发生变化。
如果使用 REE 进行 I2C 传输,还必须配置驱动程序,以便在退出安全世界之前启用 GP 安全通道协议 03;这样,处理器和安全元件之间的所有通信都会被加密并经过 MAC 认证。请检查 CFG_CORE_SE05X_SCP03_EARLY 配置选项的用法。
除了作为 OP-TEE 加密驱动程序的安全元件集成之外,OP-TEE 还通过其 OP-TEE 客户端向用户提供应用程序协议数据单元 (APDU) 接口。
使用此接口,特权应用程序可以控制安全元件来注入或删除密钥或证书、加密、解密、签名和验证数据等。可以在 Foundries.io 存储库中看到实现这些功能的子集的应用程序:fio-se05x-cli
该参考代码在主线中尚未完全发挥作用,因为尚无法将密钥和证书从安全元件导入到 OP-TEE 的 PKCS#11 实现中。但是,用户仍然可以清除安全元件 NVM 内存并读取存储在其中的证书。
设备树
OP-TEE 核心可以使用设备树格式在平台初始化期间以及可能的一些运行时上下文期间注入平台配置信息。
设备树技术允许从 ASCII 源文件(即 DTS 文件)描述平台。这些可用于生成平台描述二进制映像,即所谓的 DTB,嵌入到平台启动介质中,以便在平台初始化期间应用预期的配置设置。
该方案放宽了对 OP-TEE 核心实现的设计限制,因为大多数特定于平台的硬件都可以在不修改 C 源文件或在构建环境中添加配置指令的情况下进行调整。
安全和非安全设备树
目标系统中可以嵌入多个设备树,其中一些可以在引导阶段共享。
- 引导加载阶段可以在存储器中加载设备树结构,以供所有引导阶段从中获取平台配置。如果此类设备树数据要由非安全世界访问,则它们应位于非安全存储器中。安全世界可以在 OP-TEE 核心初始化期间使用其内容。
- 引导加载程序阶段可以将设备树结构加载到安全存储器中,只是为了安全世界的利益。此类设备树 blob 应位于安全存储器中。 Secure world 可以使用其内容,但目前在最新的 OP-TEE 版本中尚未实现。
- OP-TEE核心还可以嵌入设备树结构来描述平台。
- 非安全世界可以嵌入其自己的设备树结构和/或依赖于安全世界在其初始化期间加载的设备树结构,该初始化发生在非安全世界启动之前。
显然,非安全世界将无法访问位于非安全世界无法访问的安全存储器中的设备树映像。
当使用 CFG_DT=y 构建 OP-TEE core 时,OP-TEE core 可以访问非安全和安全设备树,以获取一些平台配置信息。
通用引导和 DTB
通用启动序列从首选嵌入式 DTB(嵌入式安全设备树部分)发现主存储器地址范围,默认为早期引导外部 DTB(早期引导外部设备树部分)。
通用启动使用早期启动外部 DTB(早期启动外部设备树部分)与非安全世界共享平台配置信息。
平台和驱动程序可以调用 OP-TEE DT API ( core/include/kernel/dt.h ) 来访问嵌入式和/或外部 DTB。
早期启动外部设备树
引导加载程序在引导时向 OP-TEE 核心提供参数。其中,OP-TEE 核心可访问的非安全设备树映像的物理内存基地址,或者在没有此类 DTB 的情况下为空地址值。
平台配置可以使用构建配置指令 CFG_DT_ADDR 静态定义此类 DTB 位置。
当引用外部 DTB 时,如果平台已通过向定义的 const struct dt_driver 实例添加属性__dt_driver 来注册兼容驱动程序,则 OP-TEE 核心将获取控制台配置。
当引用外部DTB时,OP-TEE核心将一些OP-TEE资源的描述添加到该DTB中。非安全世界可以使用这些信息与 OP-TEE 正确通信。该方案假设图像位于非安全存储器中。
OP-TEE 核心对早期启动提供并传递到非安全世界的非安全设备树映像进行的修改如下:
- 如果没有找到相关调用参数,则添加 OP-TEE 节点。
- 为少数应保留给安全世界且不被非安全世界访问的内存区域添加保留内存节点。
- 如果未找到,则添加 PSCI 描述节点。
早期启动 DTB 只能在非安全世界启动之前的初始化期间由 OP-TEE 核心访问,因为预计 DTB 内存位置可能已被运行时上下文内容替换。
假设没有嵌入式 DTB(嵌入式安全设备树部分),OP-TEE 内核从非安全 DTB 中发现主存储器地址范围。
早期启动设备树覆盖
OP-TEE 核心有两种可能性为非安全世界提供设备树覆盖。
- 将 OP-TEE 节点附加到位于早期启动 DTB 中的现有 DTB 覆盖。 ( CFG_DT_ADDR 或引导参数寄存器 R2 / X2 )。
- 在 CFG_DT_ADDR 定义的位置生成新的 DTB 叠加图像。
在后一种情况下,当 OP-TEE 内核启动时,配置指令 CFG_DT_ADDR 引用的内存不应包含有效的 DTB 映像。随后的非安全启动阶段应将 OP-TEE DTB 覆盖映像合并到另一个 DTB 中。
典型的引导流程是受信任的固件-A -> OP-TEE -> U-Boot,其中 U-Boot 负责将位于 CFG_DT_ADDR 的 OP-TEE DTB 覆盖合并到 DTB U-Boot 中从其他地方加载。
当 CFG_EXTERNAL_DTB_OVERLAY=y 时启用此功能。
嵌入式安全设备树
当使用配置指令 CFG_EMBED_DTB=y 构建 OP-TEE 核心时,指令 CFG_EMBED_DTB_SOURCE_FILE 应提供生成 DTB 的目录 core/arch/$(ARCH)/dts 内 DTS 文件的相对路径并嵌入 OP-TEE 核心的只读部分。
访问嵌入式DTB的API请参见 core/include/kernel/dt.h 。
通用启动和 DTB 部分记录了针对嵌入式 DTB 的通用启动顺序。
TA
Directory | Description |
---|---|
trusted_keys | Trusted key TA |
trusted_keys/include | Header file of the ABI provided by the trusted key TA |
arch | Architecture specific files needed to compile a TA |
mk | Makefile includes needed to build TAs and the TA dev kit |
avb | TA to support AVB (Android Verified Boot) |
avb/include | Header file of the ABI provided by the AVB TA |
pkcs11 | TA to support PKCS#11 |
pkcs11/src | Source code for the PKCS#11 TA |
pkcs11/include | Header file for the ABI provided by the PKCS#11 TA |
本文参考:
源码:https://github.com/OP-TEE/optee_os
文档:https://optee.readthedocs.io/en/latest/general/index.html