TrustZone-M 安全执行环境配置与应用实践
1. TrustZone-M 基础配置
TrustZone-M 是一种增强嵌入式系统安全性的技术。默认情况下,启用系统访问单元(SAU)会将所有区域标记为安全区,每个区域配置会在可寻址内存空间中划分出非安全或非安全可调用的“窗口”。例如,在特定配置中,区域 0 是唯一标记了非安全可调用(NSC)标志的区域,安全应用程序会将 NSC 代码安装在此处。而区域 1、2 和 3 是启用 TrustZone - M 的非安全域运行时唯一可访问的内存区域。
IDAU/SAU 只是 TrustZone - M 保护机制的第一层过滤器。闪存和随机存取存储器(RAM)还受到额外的安全门保护,这些安全门可以基于块或水印。以 STM32L552 微控制器为例,它配备了全局 TrustZone 控制器(GTZC),其中包括一个用于闪存的基于水印的门控制器和一个用于定义安全/非安全 RAM 块的基于块的控制器。
2. 闪存与安全水印配置
在目标平台上,闪存可以配置为单个连续空间,也可以通过启用双存储体配置将其分成两半。为了便于说明 TrustZone - M 的应用,这里将闪存保持在单存储体模式。
当启用 TrustZone 时,可以在连续闪存空间的上半部分(从地址 0x08040000 开始)分配一个非安全区域。如果闪存分为两个存储体,每个存储体可以配置自己独立的安全水印。起始/结束地址之间的闪存区域标记为安全区,标记之外的区域为非安全区。每个存储体中的安全区由选项字节 SECWMx_PSTRT 和 SECWMx_PEND 的值界定。如果这些分隔符重叠(即 SECWMx_PEND 的值大于 SECWMx_PSTRT 的值),则整个区域标记为非安全区。
可以使用提供的编程工具修改这些值,具体命令如下:
STM32_Programmer_CLI -c port=swd -ob SECWM1_PSTRT=0
SECWM1_PEND=0x39
在单存储体模式下,每个闪存扇区为 4096 字节。通过设置这些选项字节,将前 64 个扇区(从 0x00 到 0x39)标记为安全区,从而使闪存的另一半(从地址 0x08040000 开始)可由非安全应用程序使用。使用
-ob displ
选项启动编程工具,将显示如下信息:
Secure Area 1:
SECWM1_PSTRT : 0x0 (0x8000000)
SECWM1_PEND : 0x39 (0x8039000)
3. GTZC 配置与基于块的 SRAM 保护
参考平台的 TrustZone 控制器中存在一个额外的访问控制门。GTZC 的基于块的门组件允许为 SRAM 的部分区域配置仅安全位。STM32L552 上的 SRAM 分为两个主要存储体:
- SRAM1:192 KB 的 RAM,映射地址为 0x08000000。
- SRAM2:64 KB 的 RAM,映射地址为 0x30000000,并在 IDAU 中设置为 NSC。
在示例中,将 SRAM1 的上半部分(从地址 0x2018000 开始)标记为非安全区。GTZC 为每个存储体提供两组寄存器,用于为 RAM 中的每个页面配置基于块的门。每个块代表 256 字节,每个 32 位寄存器通过为每个块保存一个安全位,可以映射 32 页,也称为 8 KB 超级块。映射 SRAM1 中的 24 个超级块(总共 192 KB)需要 24 个寄存器,而映射 SRAM2 中的 64 KB 区域仅需要 8 个寄存器。
示例中使用一个方便的宏来配置基于块的门,该宏根据存储体、超级块编号和寄存器值计算指向超级块的正确寄存器地址并生成正确的赋值语句:
#define SET_GTZC_MPCBBx_S_VCTR(bank,n,val) \
(*((volatile uint32_t *)(GTZC_MPCBB##bank##_S_VCTR_BASE )\
+ n ))= val
通过这个宏,可以在循环中轻松配置连续区域的基于块的门。安全世界应用程序示例使用以下函数为两个存储体配置基于块的门:
static void gtzc_init(void)
{
int i;
/* Configure lower half of SRAM1 as secure */
for (i = 0; i < 12; i++) {
SET_GTZC_MPCBBx_S_VCTR(1, i, 0xFFFFFFFF);
}
/* Configure upper half of SRAM1 as non-secure */
for (i = 12; i < 24; i++) {
SET_GTZC_MPCBBx_S_VCTR(1, i, 0x0);
}
/* Configure SRAM2 as secure */
for (i = 0; i < 8; i++) {
SET_GTZC_MPCBBx_S_VCTR(2, i, 0xFFFFFFFF);
}
}
4. 外设安全访问配置
参考平台上的外设分为两类:
- 可安全外设:这些外设不直接连接到本地总线,而是通过 TrustZone 安全控制器(TZSC)控制的门系统连接。
- TrustZone 感知外设:这些外设与 TrustZone 机制集成,例如根据执行域提供单独的接口来访问其资源。
对于第一类外设,可以通过 GTZC 中的 TZSC 寄存器配置安全访问和特权访问。系统启动时,所有设备默认设置为安全设备,因此要启用对 UART、I2C、定时器等外设的访问,需要关闭与特定控制器关联的安全位。
TrustZone 感知外设为两个安全域都提供了存储体寄存器。例如,配置连接到 Nucleo - 144 板上 LED 的三个 GPIO 控制器(GPIOA、GPIOB 和 GPIOC)。当启用 TrustZone 时,GPIO 控制器寄存器分为两个区域。在安全版本的
led.h
中,定义 GPIO 控制器寄存器的地址基址如下:
#define GPIOA_BASE 0x52020000
#define GPIOB_BASE 0x52020400
#define GPIOC_BASE 0x52020800
在非安全世界应用程序中,这些控制器映射到非安全外设地址空间:
#define GPIOA_BASE 0x42020000
#define GPIOB_BASE 0x42020400
#define GPIOC_BASE 0x42020800
这样可以确保在非安全域运行时,只能通过分配给非安全空间的接口访问 GPIO 配置。
每个 GPIO 控制器还提供一个接口来保护每个受控引脚。这通过一个只写寄存器实现,该寄存器使用与每个引脚对应的标志控制安全和非安全访问。该寄存器称为 GPIOx_SECCFG,位于每个 GPIO 控制器空间的偏移量 0x24 处,只有在安全域运行时才可写入。
在示例中,定义了函数来设置/清除连接到三个 LED 的每个 GPIO 引脚的安全位。例如,设置红色 LED 的安全状态的函数实现如下:
void red_led_secure(int onoff)
{
if (onoff)
GPIOA_SECCFG |= (1 << RED_LED);
else
GPIOA_SECCFG &= ~(1 << RED_LED);
}
安全世界示例应用程序在部署前限制对蓝色和红色 LED 的访问,同时允许访问绿色 LED:
red_led_secure(1);
green_led_secure(0);
blue_led_secure(1);
域切换后,非安全应用程序将尝试打开所有三个 LED,但只有绿色 LED 会实际亮起,因为通过非安全接口的访问受到安全世界中设置的 SECCFG 位的限制,对 GPIO 无影响。
5. 构建和运行示例
5.1 启用 TrustZone - M
默认情况下,微控制器处于出厂状态时 TrustZone - M 是关闭的。开启 TrustZone 是单向操作,通常不可逆,除非与其他硬件辅助保护机制结合使用,使得嵌入式系统部署后无法禁用它。启用后禁用 TrustZone 需要比清除寄存器中的一个位更复杂的过程。
在参考平台上,通过以下命令在选项字节中设置关联标志来启用 TrustZone:
STM32_Programmer_CLI -c port=swd mode=hotplug -ob TZEN=1
启用 TrustZone 后,就可以构建和安装安全固件。
5.2 安全应用程序入口点
安全世界链接脚本中定义的区域反映了安全固件所看到的系统资源。分配一个覆盖 SRAM1 存储体下半部分的 RAM 区域:
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00018000
.text
和
.data
段最终位于 FLASH 区域,映射到其安全域地址:
FLASH (rx) : ORIGIN = 0x0C000000, LENGTH = 0x1000
对于简单示例,4 KB 足以存储引导加载程序映像。此外,定义一个非安全可调用区域,用于存储安全存根的实现:
FLASH_NSC(rx): ORIGIN = 0x0C001000, LENGTH = 0x1000
参考平台上安全应用程序的入口点硬编码在选项字节中。在安装映像之前,必须确保选项字节 SECBOOTADD0 配置为指向地址 0x0C000000,即安全系统视图中闪存的起始地址。如果该值之前被修改过,可以通过以下命令恢复:
STM32_Programmer_CLI -c port=swd mode=hotplug -ob
SECBOOTADD0=0x180000
这是因为 SECBOOTADD0 的粒度为 128 字节,设置值为 0x180000 将指向地址 0x0C000000。
5.3 编译和链接安全世界应用程序
在安全世界应用程序的 Makefile 中,构建过程引入了两个新标志。gcc 需要使用
-mcmse
标志来指示正在为 TrustZone 系统编译安全代码。添加此标志后,编译器将为标记为非安全可调用的函数生成安全网关(SG)存根。
例如,定义一个允许非安全代码切换连接到蓝色 LED 的 GPIO 引脚值的安全 API 调用:
void __attribute__((cmse_nonsecure_entry))
nsc_blue_led_toggle(void)
{
if ((GPIOB_ODR & (1 << BLUE_LED)) == (1 << BLUE_LED))
blue_led_off();
else
blue_led_on();
}
__attribute__((cmse_nonsecure_entry))
编译器属性告诉 gcc 为该函数生成 SG 存根。链接脚本中定义的 FLASH_NSC 部分用于存储配置的安全 API 的 SG 存根。SG 存根自动放置在名为
.gnu.sgstubs
的部分,在示例链接脚本中,将其放置在 FLASH_NSC 区域:
.gnu.sgstubs :
{
. = ALIGN(4);
*( .gnu.sgstubs*) /* Secure Gateway stubs */
. = ALIGN(4);
} >FLASH_NSC
额外的链接器标志
--cmse - implib
和
--out - implib = led_cmse.o
有不同的用途,不会直接影响安全域。链接安全应用程序时,添加这些标志会要求链接器创建一个新的目标文件,该文件不会链接到最终的安全应用程序中,而是会链接到非安全世界应用程序中,并包含安全 API 的 veneers。这些 veneers 用于准备从非安全域跳转到非安全可调用世界。
综上所述,构建安全应用程序需要引入两组特定标志:
-
-mcmse
编译时标志,告诉 gcc 正在为 TrustZone 生成安全代码,并为非安全入口点启用 SG 存根。
-
--cmse - implib
和
--out - implib = ...
链接器标志,告诉链接器生成目标文件格式的 veneers,这些 veneers 将链接到非安全域以访问关联的安全 API 调用。
使用
make
构建安全固件映像后,可以使用以下命令将其上传到设备闪存:
STM32_Programmer_CLI -c port=swd -d bootloader.bin 0x0C000000
5.4 编译和链接非安全应用程序
非安全应用程序的链接脚本定义了从非安全执行域看到的世界边界。安全和 NSC 区域在此无法访问。非安全应用程序中的
target.ld
链接脚本定义这些区域如下:
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 256K
RAM (rwx) : ORIGIN = 0x20018000, LENGTH = 96K
从这一点开始,构建过程与构建不支持 TrustZone 的普通应用程序类似。与安全应用程序不同,非安全应用程序不需要任何特殊的编译器或链接器标志。
不过,安全应用程序构建过程中生成的额外目标文件允许非安全应用程序与安全世界进行短暂交互。安全和非安全域之间的契约由安全世界定义的安全 API 组成。在示例中,只定义了一个安全函数
nsc_blue_led_toggle
。包含 veneers 的目标文件(在示例中称为
cmse_led.o
)在编译安全域代码时自动生成,并链接到非安全应用程序中,它实际上满足了安全应用程序中这些特殊符号的符号依赖。
使用
make
构建非安全应用程序后,将非安全固件映像上传到目标设备的内部闪存,从地址 0x08040000 开始:
STM32_Programmer_CLI -c port=swd -d image.bin 0x08040000
6. 域间转换
当安全世界示例引导加载程序准备部署非安全世界应用程序时,准备 CPU 寄存器并跳转到非安全域的汇编代码有一些不同之处。
首先,启用 TrustZone 时,VTOR 系统寄存器会进行存储体划分。这意味着有两个单独的寄存器分别保存向量表的偏移量,一个用于安全域(VTOR_S),另一个用于非安全域(VTOR_NS)。在跳转到非安全世界代码的入口点之前,VTOR_NS 寄存器应包含非安全世界应用程序的中断向量偏移量。由于中断向量位于二进制映像的开头,引导加载程序主程序中的以下赋值确保最终非安全域代码能够执行中断服务例程:
/* Update IV */
VTOR_NS = ((uint32_t)app_IV);
设置此系统寄存器后,获取部署所需的两个重要指针,这与没有 TrustZone - M 功能的引导加载程序类似。这些指针存储在非安全应用程序二进制映像的前两个 32 位字中,分别是初始堆栈指针和包含
isr_reset
处理程序地址的实际入口点。在部署之前,将这两个地址读取到本地堆栈变量中:
app_end_stack =
(*((uint32_t *)(NS_WORLD_ENTRY_ADDRESS)));
app_entry =
(void *)(*((uint32_t *)(NS_WORLD_ENTRY_ADDRESS + 4)));
在示例中,预先为非安全应用程序确定堆栈区域大小,计算堆栈允许的最低地址:
app_stack_limit = app_end_stack - MAX_NS_STACK_SIZE;
然后将此值分配给 MSPLIM_NS 寄存器。由于 MSPLIM_NS 是特殊寄存器,因此必须使用
msr
指令:
asm volatile("msr msplim_ns, %0" ::"r"(app_stack_limit));
接着设置新堆栈指针的值,该指针将在域转换完成后替换 SP:
asm volatile("msr msp_ns, %0" ::"r"(app_end_stack));
跳转到非安全代码的实际操作与之前的引导加载程序有很大不同。在 ARMv8 - M 中,执行跳转并同时进行域转换到非安全世界的指令是
blxns
。但在调用
blxns
或任何其他意味着跳转到非安全地址的指令时,必须确保跳转的目标地址的最低有效位为 0。因此,在执行
blxns
之前,将
app_entry
的值减 1:
/* Jump to non-secure app_entry */
asm volatile("mov r12, %0" ::"r"
((uint32_t)app_entry - 1));
asm volatile("blxns r12" );
这是在最终部署非安全应用程序之前在安全域中执行的最后一条指令。使用调试器检查这些最后指令执行过程中寄存器的值,可以看到 CPU 寄存器的值被更新,最终 SP 寄存器将指向非安全域中的新上下文。
从这一点开始,任何尝试跳回安全域的操作都是不允许的,会产生异常。不过,放置在 NSC 区域的函数的目的是为非安全域提供临时和受控的安全函数执行。
在示例中,在过渡到非安全执行域之前,通过设置 GPIOx_SECCFG 寄存器中的相应位,对连接到三个 LED 的 GPIO 线的访问施加了一些限制。
当示例的两个映像都上传到目标平台后,可以进行电源循环,并通过观察三个 LED 来观察效果。重启后,应该看到红色 LED 在启动时亮起,并在引导加载程序中的安全代码运行时保持亮起。经过任意数量的循环后,红色 LED 将熄灭并被保护。蓝色 LED 也通过部署前执行的
blue_led_secure(1)
调用被保护。绿色 LED 未被保护,可以在非安全域中正常访问。
当非安全应用程序启动时,可以看到绿色 LED 持续亮起,蓝色 LED 快速闪烁。后者之所以能够实现,是因为非安全应用程序可以访问安全 API 中的一个函数。
运行
arm - none - eabi - objdump - D
命令查看安全世界 elf 文件生成的汇编代码,可以看到生成的非安全可调用函数存根实际上是一个短程序,位于非可调用部分的开头:
0c001000 <nsc_blue_led_toggle>:
c001000: e97f e97f sg
c001004: f7ff bdd2 b.w c000bac
<__acle_se_nsc_blue_led_toggle>
在 NSC 区域运行的代码中,最有趣的部分是使用特殊汇编指令
sg
,这是 ARMv8 - M 中引入的用于实现从非安全域进行安全调用的指令。该指令准备跳转到安全闪存空间中的安全调用,并且只有在从非安全可调用区域执行时才合法。
实际实现实际上包含在
__acle_se_nsc_blue_led_toggle
函数中,该函数由编译器生成并放置在闪存的 S 区域。
通过同样方式反汇编包含生成目标文件的非安全应用程序后,
nsc_blue_led_toggle
的 veneer 生成的汇编代码如下:
080408e8 <__nsc_blue_led_toggle_veneer>:
80408e8: f85f f000 ldr.w pc, [pc] ; 80408ec \
<__nsc_blue_led_toggle_veneer+0x4>
80408ec: 0c001001
从非安全域调用安全函数的过程的结束部分在切换蓝色 LED 的安全函数
__acle_se_nsc_blue_led_toggle
的实际实现的尾部:
c000bee: 4774 bxns lr
综上所述,通过一系列的配置和操作,我们成功地在系统中实现了 TrustZone - M 的应用,包括安全和非安全域的划分、外设的安全访问配置以及域间的转换等,为嵌入式系统的安全性提供了有力保障。
TrustZone-M 安全执行环境配置与应用实践(续)
7. 技术点分析与总结
7.1 内存区域配置总结
在整个配置过程中,内存区域的划分和配置是基础且关键的部分。以下是对不同内存区域配置的总结表格:
| 内存区域 | 配置说明 | 相关操作 |
| — | — | — |
| 闪存(单存储体模式) | 前 64 个扇区(从 0x00 到 0x39)标记为安全区,从地址 0x08040000 开始的另一半为非安全区 | 使用
STM32_Programmer_CLI -c port=swd -ob SECWM1_PSTRT=0 SECWM1_PEND=0x39
命令设置选项字节 |
| SRAM1 | 下半部分(前 12 个超级块)为安全区,上半部分(后 12 个超级块)为非安全区 | 通过
gtzc_init
函数中的循环调用
SET_GTZC_MPCBBx_S_VCTR
宏进行配置 |
| SRAM2 | 全部配置为安全区 | 通过
gtzc_init
函数中的循环调用
SET_GTZC_MPCBBx_S_VCTR
宏进行配置 |
7.2 编译器和链接器标志作用
编译器和链接器标志在构建安全和非安全世界应用程序时起到了重要作用,具体如下:
-
安全世界应用程序
:
-
-mcmse
标志:指示 gcc 为 TrustZone 系统编译安全代码,为标记为非安全可调用的函数生成安全网关(SG)存根。
-
--cmse - implib
和
--out - implib = ...
链接器标志:要求链接器创建一个新的目标文件,包含安全 API 的 veneers,用于非安全世界应用程序与安全世界的交互。
-
非安全世界应用程序
:不需要特殊的编译器或链接器标志,但需要链接包含 veneers 的目标文件,以满足安全应用程序中特殊符号的符号依赖。
7.3 域间转换关键要点
域间转换是实现安全和非安全域交互的重要环节,其关键要点如下:
-
VTOR 寄存器设置
:启用 TrustZone 时,VTOR 系统寄存器分为 VTOR_S 和 VTOR_NS,跳转到非安全域前需将 VTOR_NS 寄存器设置为非安全世界应用程序的中断向量偏移量。
-
堆栈指针和入口点设置
:获取非安全应用程序的初始堆栈指针和入口点地址,并设置 MSPLIM_NS 和 msp_ns 寄存器。
-
跳转指令
:使用
blxns
指令跳转到非安全域,但跳转地址的最低有效位必须为 0。
8. 应用场景与优势
TrustZone - M 技术在嵌入式系统中具有广泛的应用场景和显著的优势:
-
物联网设备
:在物联网设备中,安全性至关重要。TrustZone - M 可以将设备的敏感数据和关键功能隔离在安全域中,防止非安全域的恶意攻击和数据泄露。例如,智能家居设备中的用户认证信息和加密密钥可以存储在安全域中,确保设备的安全性和用户隐私。
-
工业控制系统
:工业控制系统通常涉及到关键的生产过程和设备控制。TrustZone - M 可以对控制系统的核心代码和数据进行保护,防止外部攻击导致系统故障或生产事故。例如,工厂中的自动化生产线控制系统可以使用 TrustZone - M 来确保控制程序的安全性和稳定性。
-
金融支付设备
:金融支付设备需要高度的安全性来保护用户的资金和交易信息。TrustZone - M 可以将支付处理模块和敏感数据存储在安全域中,防止非安全域的恶意软件窃取用户信息和篡改交易数据。例如,移动支付终端可以使用 TrustZone - M 来确保支付过程的安全可靠。
9. 未来展望
随着嵌入式系统的不断发展和安全需求的日益增长,TrustZone - M 技术也将不断演进和完善。未来可能会出现以下发展趋势:
-
更强大的安全功能
:未来的 TrustZone - M 可能会提供更高级的安全功能,如硬件加密加速、安全审计和实时监测等,以应对日益复杂的安全威胁。
-
更广泛的应用支持
:随着物联网、人工智能等技术的发展,TrustZone - M 可能会在更多的应用领域得到支持,如智能医疗设备、自动驾驶汽车等,为这些领域的安全提供保障。
-
与其他安全技术的融合
:TrustZone - M 可能会与其他安全技术,如区块链、零信任架构等进行融合,形成更强大的安全防护体系,为嵌入式系统提供全方位的安全保障。
10. 总结
通过对 TrustZone - M 技术的详细介绍和实践操作,我们了解了如何在嵌入式系统中实现安全和非安全域的划分、外设的安全访问配置以及域间的转换。TrustZone - M 技术为嵌入式系统的安全性提供了一种有效的解决方案,可以帮助开发者构建更安全、可靠的嵌入式应用程序。
在实际应用中,开发者需要根据具体的需求和场景,合理配置 TrustZone - M 的各项参数,确保系统的安全性和性能。同时,随着技术的不断发展,开发者也需要关注 TrustZone - M 技术的最新进展,不断优化和完善系统的安全防护机制。
希望本文对大家理解和应用 TrustZone - M 技术有所帮助,让我们共同为嵌入式系统的安全发展贡献力量。
以下是整个配置和运行过程的 mermaid 流程图:
graph TD;
A[启用 TrustZone - M] --> B[配置安全应用程序入口点];
B --> C[编译和链接安全世界应用程序];
C --> D[上传安全固件到设备闪存];
D --> E[编译和链接非安全世界应用程序];
E --> F[上传非安全固件到设备闪存];
F --> G[进行域间转换];
G --> H[观察系统运行效果];
通过这个流程图,我们可以清晰地看到整个 TrustZone - M 应用的配置和运行流程,从启用 TrustZone - M 开始,经过安全和非安全应用程序的配置、编译、链接和上传,最后进行域间转换并观察系统运行效果。
超级会员免费看
335

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



