Armv8-M TrustZone 安全软件开发全解析
1. TrustZone 技术细节
1.1 安全 API 执行流程
在执行安全 API 时,若有需要,可先将处理器切换到非特权状态,随后执行安全 API 的函数体。在非特权状态下运行安全 API,可借助安全内存保护单元(MPU)将 API 的操作限制在特定的内存区域。
1.2 安全状态转换与异常优先级的关系
部分系统异常在安全状态之间是有存储区分的,例如 SysTick、SVC 和 PendSV 等异常。因此,对于这些异常,存在安全和非安全异常优先级寄存器。当上述系统异常处理程序调用来自相反安全域的函数时,会使用首次触发异常的异常优先级。
若在应用中断和复位控制寄存器(AIRCR)中设置 PRIS(优先处理安全异常)位来对安全异常进行优先级排序,通过函数调用/返回在不同安全状态之间切换可能会影响处理器当前的异常优先级。例如,执行特定代码示例时,若 AIRCR.PRIS 设置为 1,非安全 SVC 处理程序执行期间的有效异常优先级为 0xC0,而在执行安全函数时,有效优先级会变为 0x80。
1.3 其他 TrustZone 指令
为支持各种 TrustZone 操作,Armv8 - M 引入了若干指令,如下表所示:
| 指令 | 用途 |
| — | — |
| Test Target (TT, TTA, TTT, TTAT) | 用于指针检查 |
| VLSTM, VLLDM | 仅适用于 Armv8 - M 主线(如 Cortex - M33 处理器),不适用于 Armv8 - M 基线(如 Cortex - M23 处理器)。当安全代码需要调用非安全函数时,这些指令用于保存和恢复 FPU 中的安全数据。若非安全代码不使用 FPU,通过复用延迟堆叠支持硬件可减少调用非安全函数的延迟 |
VLSTM 和 VLLDM 指令由 C/C++ 编译器生成,具体操作流程如下:
1. 在调用非安全函数之前,安全软件在堆栈上为 FPU 寄存器预留一小部分内存空间,然后执行 VLSTM 指令。此操作标记 FPU 中的数据,表明需要保护这些数据不被非安全状态软件访问,但不会将 FPU 寄存器压入分配的堆栈空间。
2. 调用并执行非安全 C 函数:
- 当非安全 C 函数执行 FPU 指令时,会对 FPU 寄存器进行堆叠操作,将 FPU 中的安全数据保存到安全堆栈中,然后清除 FPU 寄存器以防止安全信息泄露。完成寄存器的堆叠和清除后,继续执行非安全函数。
3. 从非安全 API 返回后,处理器执行 VLLDM 指令:
- 若执行的非安全函数未使用 FPU,FPU 寄存器不会被触动,VLLDM 指令仅清除待处理的 FPU 堆叠请求。
- 若执行的非安全函数使用了 FPU,之前的安全 FPU 数据会存储在安全堆栈中,VLLDM 指令会恢复安全 FPU 上下文。
当 FPU 未实现或被禁用时,VLSTM 和 VLLDM 指令将作为空操作(NOP)执行。
graph TD;
A[调用非安全函数前] --> B[执行 VLSTM 指令];
B --> C[调用并执行非安全 C 函数];
C --> D{FPU 指令执行?};
D -- 是 --> E[堆叠 FPU 寄存器,清除数据];
E --> F[继续执行非安全函数];
D -- 否 --> F;
F --> G[从非安全 API 返回];
G --> H[执行 VLLDM 指令];
H --> I{FPU 是否被使用?};
I -- 是 --> J[恢复安全 FPU 上下文];
I -- 否 --> K[清除待处理 FPU 堆叠请求];
2. 安全软件开发
2.1 安全软件开发概述
构建安全项目时,需完成以下步骤:
1. 告知 C/C++ 编译器正在构建安全项目。对于 Arm Compiler 6 和 GCC,可使用“-mcmse”选项;对于 IAR 编译器,使用“–cmse”选项。
2. 在 C/C++ 代码中包含头文件:
#include <arm_cmse.h>
若使用 Keil® 微控制器开发套件(MDK),可在目标选项菜单中指定安全项目选项。
编译安全软件时,C 编译器会生成一个名为 __ARM_FEATURE_CMSE 的内置预处理宏,其值为 3。该宏可使软件适应安全和非安全环境,示例代码如下:
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
function_1();
#endif
此特性常用于 Armv8 - M 软件项目中,例如在 CMSIS - CORE 头文件和设备驱动库中常能看到 __ARM_FEATURE_CMSE 预处理宏的使用。
2.2 安全设置
在初始化安全软件时,通常需要对设备进行安全配置。基于 Cortex - M23/Cortex - M33 的系统在设置安全配置时,需考虑以下方面:
1. 配置内存映射 :
- 对 SAU 进行编程,以定义非安全和 NSC 区域。
- 若 IDAU 可编程,则对其进行配置。系统可能包含控制 IDAU 配置的可编程寄存器。
- 配置系统级内存保护控制器(MPCs)。
- 配置系统级外设保护控制器(PPCs)。
2. 配置异常的安全域及其他异常相关设置 :
- 对于每个中断,使用中断目标非安全寄存器(NVIC->ITNS)定义其应指向安全还是非安全状态。
- 对于 Cortex - M33 处理器,可选择启用 SecureFault 异常及其他系统异常。
- 设置应用中断和复位控制寄存器(AIRCR),与 TrustZone 相关的位字段包括:
- AIRCR.BFHFNMIHF:使用 TrustZone 时,通常将此位保持为 0,使 NMI、HardFault 和 BusFault 异常保持在安全状态。
- AIRCR.PRIS:可选择设置此位以优先处理安全异常。
- AIRCR.SYSRESETREQS:可选择设置此位以决定非安全软件是否可触发自复位。
3. 定义非安全软件可用的功能 :对于 Cortex - M33 处理器,需对非安全访问控制寄存器(SCB->NSACR)进行编程,以定义 FPU、协处理器和 Arm 自定义指令功能是否可从非安全状态访问。此外,可能还需配置 CPPWR 以防止非安全软件访问 FPU 和自定义加速器的电源控制。
4. 配置 FPU 设置(仅适用于带有 FPU 的 Armv8 - M 处理器) :若安全软件需使用 FPU(或 Armv8.1 - M 处理器中的 Helium 特性)处理敏感数据,在启动时应将 FPU 浮点上下文控制寄存器(FPU->FPCCR)中的 TS、CLRONRET 和 CLRONRETS 控制位设置为 1,且这些位不应更改,应始终保持为高。若安全软件不使用 FPU/Helium 寄存器处理敏感数据,可将 TS 和 CLRONRETS 控制位设置为 0,非安全特权软件可将 CLRONRET 控制位设置为 1,以防止 FPU 中的特权数据被非特权软件访问。
5. 决定非安全软件是否可控制 SLEEPDEEP 特性 :通过对系统控制寄存器(SCB->SCR)中的 SLEEPDEEPS 位进行编程来实现。
6. 配置系统级/设备特定的安全管理功能 :每个芯片设计可能有额外的安全特性,在使用前可能需要进行配置或启用。
7. 配置调试安全设置 :必要时,安全软件可覆盖安全调试认证设置。
对于具有 CMSIS - CORE 兼容驱动的 Armv8 - M 设备,大部分配置在名为“TZ_SAU_Setup()”的函数中完成。该函数及其参数位于名为 partition_ .h 的文件中,此文件名与设备相关。“TZ_SAU_Setup()”函数从“SystemInit()”函数中调用,并在复位处理程序执行期间执行。使用 Keil MDK 进行安全软件开发项目时,可使用配置向导轻松编辑 partition_ .h 中的参数。
2.3 初始切换到非安全世界
完成安全初始化过程后,在启动非安全世界的应用程序之前,可能需要执行以下操作:
1. 根据应用需求,初始化安全外设(如安全看门狗定时器)。
2. 初始化安全固件框架。
3. 设置安全堆栈指针的堆栈限制。
完成上述操作后,即可分支到非安全应用程序。非安全软件的起始点(即非安全复位处理程序的起始地址)位于非安全向量表中。安全固件通过读取起始地址并分支到该地址来启动非安全世界。为确保非安全软件(如 RTOS)正常运行,启动非安全软件时处理器必须处于特权线程模式。
以下是一个用于分支到非安全世界的示例代码:
// Non-secure int function typedef with cmse_nonsecure_call attribute
typedef int __attribute__((cmse_nonsecure_call)) nsfunc(void);
int nonsecure_init(void) {
// Example modified from Arm website
// https://community.arm.com/developer/ip-products/processors/trustzone-for-armv8-m/b/blog/posts/a-few-intricacies-of-writing-armv8-m-secure-code
// If needed, setup the Non-secure VTOR.
// In the Cortex-M33 based FPGA platform used for creating this example
// (i.e. a system called IoT Kit, which runs on the MPS2 FPGA board), the
// Non-secure code image starting address is 0x00200000
SCB_NS->VTOR = 0x00200000UL; // Optional for most hardware platforms.
// But it is needed in the FPGA platform used here
// The following line creates a pointer to the Non-secure vector table
uint32_t *vt_ns = (uint32_t *) SCB_NS->VTOR;
// Setup the Non-secure Main Stack Pointer (MSP_NS)
__TZ_set_MSP_NS(vt_ns[0]);
// Setup the function pointer to the NS reset vector
nsfunc *ns_reset = (nsfunc*)(vt_ns[1]);
// Branch into the Non-secure reset handler
ns_reset(); // Branches to the Non-secure world
#ifdef VERBOSE
// Displays error for debug
printf("ERROR: should not be here\n");
#endif
while(1);
}
2.4 创建简单的安全 API
开发安全软件时,创建简单的安全 API 较为容易。以下是一个返回 x 的平方值的简单安全函数示例:
// Non-secure int function prototype
int __attribute__((cmse_nonsecure_entry)) entry1(int x);
int __attribute__((cmse_nonsecure_entry)) entry1(int x)
{
return (x * x);
}
由于 SG 指令(即入口点)由链接器生成,上述“简单”代码足以创建一个安全 API。为防止安全信息泄露,C 编译器会生成一个代码序列,确保在将数据返回给非安全世界之前,清除寄存器组中除返回结果外的安全数据。
在链接阶段,链接器会识别安全项目中的所有安全 API,并生成一个入口点表,该表放置在链接器设置配置(如链接脚本)指定的位置。同时,链接器会生成一个导出库,该文件包含入口点的符号和地址,供非安全软件开发者处理非安全项目的链接过程。
在某些情况下,安全程序映像可能是现有映像的新版本,需要添加额外的安全函数。为避免重新编译现有的非安全项目(这可能意味着需要更新已发布产品中的非安全程序),更新后,现有安全程序映像中入口点的地址应保持不变。为确保这一点,链接器必须导入旧版本的导出库,以便知道旧入口点的地址并保持不变。
3. 内存保护与外设保护控制器配置
3.1 MPC 和 PPC 配置代码位置
配置内存保护控制器(MPC)和外设保护控制器(PPC)的代码通常可在 CMSIS - CORE 文件“partition_ .h”或“system_ .c”中找到。MPC 和 PPC 的编程模型因设备而异。
3.2 MPC 分区方法
MPC 有两种基于不同分区方法的类型,如下表所示:
| 分区方法 | 特点 |
| — | — |
| 基于内存块的设计 | 连接到 MPC 的内存块被划分为多个内存页,每个内存页的目标安全状态由 MPC 硬件内的一个小型可编程查找表(LUT)定义。LUT 的每一位代表一个页面的目标安全状态,这种设计具有高度的灵活性 |
| 基于水印级别的设计 | 连接到 MPC 的内存块被划分为两个部分,边界位置由一个可编程寄存器控制,这种设计相对较小,适用于一系列超低功耗系统 |
对于基于 Arm Corstone™ - 200 基础 IP 的微控制器,MPC 设计是基于内存块的。其编程模型可通过链接 https://developer.arm.com/documentation/ddi0571/e/programmers-model/ahb5 - trustzone - memory - protection - controller 找到。在 Arm Corstone - 200 MPC 设计中:
1. 查找表可通过 BLK_LUT[n] 寄存器访问,该寄存器中的每一位代表一个内存块的安全状态。由于可能有超过 32 个内存块,因此可能有多个 BLK_LUT 寄存器。
2. 一个名为 BLK_IDX(即块索引寄存器)的读写寄存器定义索引“n”的值,用于选择应访问哪个 BLK_LUT[n] 寄存器。在访问查找表之前,需要通过 BLK_IDX 设置 BLK_LUT[n] 的索引“n”。
3. 还有额外的只读寄存器,可通过 BLK_CFG 寄存器确定块大小,通过 BLK_MAX 寄存器确定可用的最大块数。
4. 若检测到安全异常,MPC 可选择向处理器发送中断。例如,当非安全程序试图通过非安全别名地址访问分配给安全世界的内存块时。为协助管理中断生成和中断处理,MPC 具有中断控制和状态寄存器。
graph LR;
A[内存块] --> B[基于内存块的设计];
A --> C[基于水印级别的设计];
B --> D[多个内存页];
D --> E[可编程 LUT 定义安全状态];
C --> F[两个部分];
F --> G[可编程寄存器控制边界];
3.3 PPC 编程模型
与 MPC 类似,PPC 也是特定于供应商/设备的。通常,使用一个简单的可编程寄存器提供查找表,每一位代表一个外设是安全的还是非安全的。一些 PPC 设计,包括 Arm Corstone™ - 200 基础 IP 的设计,还允许外设以特权级别或特权和非特权级别进行访问。Armv8 - M 中 Arm Corstone - 200 的 PPC 编程模型可通过以下链接找到:
- AMBA® AHB5 PPC: https://developer.arm.com/documentation/ddi0571/e/functional - description/ahb5 - trustzone - peripheral - protection - controller/functional - description
- AMBA APB4 PPC: https://developer.arm.com/documentation/ddi0571/e/functional - description/apb4 - trustzone - peripheral - protection - controller/functional - description
Armv8 - M 中 Arm Corstone - 200 的 AHB5 PPC 和 APB4 PPC 的控制寄存器不包含在 PPC 组件中,因此是特定于供应商/设备的。
3.4 内存分区与项目设置一致性
在进行安全和非安全软件开发项目时,TrustZone 内存分区设置必须与项目的内存使用设置相匹配。TrustZone 内存分区设置包括:
1. 配置 SAU,若 IDAU 可编程,可选择配置 IDAU。
2. 配置 MPC 和 PPC。
软件项目的内存使用通常由项目的链接器设置(链接脚本或命令行选项)定义。因此,安全软件开发人员需要配置:
1. 安全项目的内存映射,包括 NSC 区域的位置,可选择配置。
2. 非安全项目的内存映射。
若 TrustZone 分区和软件项目设置之间存在不一致,设备的安全性可能会受到影响。为降低这种风险,CMSIS - Zone 项目应运而生,它提供了一个工具,可使用单一数据源自动生成各种设置代码和链接脚本。使用基于 XML 的文件和 CMSIS - Zone 实用程序(一个软件工具),可以生成:
1. SAU、IDAU、MPC 和 PPC 的设置代码。
2. 链接脚本。
3. 可选的用于进程隔离的 MPU 设置代码,可由可信固件 - M 和 RTOS 使用。
这种方法使开发过程更加容易,且不易出错。
4. 关键要点总结
4.1 安全软件开发要点回顾
- 构建安全项目时,要告知编译器并包含特定头文件,利用
__ARM_FEATURE_CMSE宏适应不同安全环境。 - 安全设置涵盖内存映射、异常安全域、非安全软件功能定义、FPU 设置、SLEEPDEEP 控制、系统级安全管理和调试安全设置等多方面,多数配置通过“TZ_SAU_Setup()”函数完成。
- 初始切换到非安全世界前要完成安全外设初始化、安全固件框架初始化和安全堆栈指针设置,启动非安全软件时处理器需处于特权线程模式。
- 创建安全 API 时,链接器会生成入口点表和导出库,为保证更新后入口点地址不变需导入旧导出库。
4.2 MPC 和 PPC 配置要点
- MPC 有基于内存块和基于水印级别的两种设计,不同设计适用于不同场景,基于 Arm Corstone - 200 的 MPC 设计有其特定的编程模型。
- PPC 特定于供应商/设备,部分设计允许外设以不同级别访问,其编程模型可通过特定链接查找。
- 要确保 TrustZone 分区和软件项目设置一致,可借助 CMSIS - Zone 项目工具降低风险。
4.3 整体技术细节要点
- 安全 API 可在非特权状态下运行以限制操作内存区域,执行时可能需先切换处理器状态。
- 安全状态转换与异常优先级相关,部分系统异常在不同安全状态有不同优先级寄存器,设置 AIRCR.PRIS 位会影响异常优先级。
- Armv8 - M 引入的 VLSTM 和 VLLDM 指令用于安全代码调用非安全函数时 FPU 数据的保存和恢复,FPU 未实现或禁用时指令为空操作。
通过以上对 Armv8 - M TrustZone 安全软件开发的全面解析,开发者可以更深入地理解和应用相关技术,确保系统的安全性和稳定性。
超级会员免费看
1004

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



