TrustZone是ARM架构中实现硬件级安全隔离的核心技术,特别针对ARMv8-M架构的嵌入式系统进行了优化。这项技术通过在处理器层面建立安全状态和非安全状态,为嵌入式设备提供了硬件级别的安全防护机制。
TrustZone将系统资源划分为安全域(Secure Domain)和非安全域(Non-secure Domain)两个独立的世界:
状态切换通过专用的硬件机制实现,确保安全域和非安全域之间的隔离性。当处理器处于安全状态时,可以访问所有资源;处于非安全状态时,任何尝试访问安全资源的操作都会触发安全错误。
ARMv8-M架构对TrustZone进行了专门优化,使其更适合资源受限的嵌入式设备:
提示:与ARMv7-A的TrustZone实现不同,ARMv8-M的TrustZone设计更注重低功耗和实时性,特别适合物联网和边缘计算设备。
SAM L11系列微控制器基于ARM Cortex-M23核心,主频可达32MHz,集成了丰富的安全特性:
SAM L11的存储器系统通过IDAU(Implementation Defined Attribution Unit)实现安全分区:
| 存储区域 | 安全属性 | 典型用途 |
|---|---|---|
| Flash 0-31KB | 安全(S) | 安全固件、加密算法 |
| Flash 32KB-32.4KB | 非安全可调用(NSC) | 安全接口函数 |
| Flash 32.4KB-64KB | 非安全(NS) | 应用程序代码 |
| SRAM 0-8KB | 安全(S) | 安全数据 |
| SRAM 8KB-32KB | 非安全(NS) | 应用数据 |
每个外设通过PAC(Peripheral Access Controller)独立配置安全属性:
c复制// 示例:在MDK中配置USART0为安全外设
PAC->PER_REG[USART0_PAC_INDEX].PER = PAC_SECURE;
关键外设安全策略建议:
开发SAM L11 TrustZone应用需要以下组件:
MDK开发工具:
软件包:
编译器:
TrustZone开发需要同时创建安全和非安全两个项目:
code复制NoRTOS.uvmpw (工作区)
├── sApp (安全项目)
│ ├── sApp.sct (分散加载文件)
│ ├── DebugConfig (调试配置)
│ └── interface.c (安全接口)
└── nsApp (非安全项目)
├── nsApp.sct
└── main_ns.c
关键配置步骤:
通过.dbgconf文件配置设备安全参数:
xml复制<DebugConfiguration>
<DebuggerSetup>
<ChipErase>CE2</ChipErase>
<ChipEraseKey1>0xFFFFFFFF</ChipEraseKey1>
<ChipEraseKey2>0xFFFFFFFF</ChipEraseKey2>
</DebuggerSetup>
<DeviceSetup>
<DebugAccessLevel>DAL2</DebugAccessLevel>
<ProgramUROW>1</ProgramUROW>
<FlashSecureSize>0x8000</FlashSecureSize> <!-- 32KB安全Flash -->
<FlashNSCSize>0x190</FlashNSCSize> <!-- 400B NSC区域 -->
</DeviceSetup>
</DebugConfiguration>
安全项目的启动代码需要完成以下关键任务:
c复制void Secure_Init(void)
{
// 1. 配置内存保护
SCB_NS->VTOR = NS_APP_START; // 设置非安全向量表
// 2. 配置外设安全属性
PAC->PER_REG[USART0_INDEX].PER = PAC_SECURE;
// 3. 初始化安全服务
Crypto_Init();
// 4. 切换到非安全状态
__TZ_set_MSP_NS(__initial_sp_ns);
__TZ_set_CONTROL_NS(0);
__TZ_set_PSP_NS(0);
__asm volatile("bxns %0" : : "r" (NS_APP_START));
}
安全域暴露给非安全域的接口需要特殊处理:
__attribute__((cmse_nonsecure_entry))标记cmse_check_pointed_object()进行安全检查c复制// interface.c - 放置在NSC区域
#include <arm_cmse.h>
int32_t __attribute__((cmse_nonsecure_entry)) secure_func1(int32_t x)
{
// 安全检查示例
if(!cmse_check_pointed_object(&x, CMSE_NONSECURE | CMSE_MPU_READ))
return -1;
return x * 2; // 简单示例
}
安全项目的sApp.sct文件需要明确定义各区域:
code复制LR_IROM1 0x00000000 0x00008000 { ; 32KB安全Flash
ER_IROM1 0x00000000 0x00007E00 { ; 主安全代码
*.o (RESET, +First)
*(+RO)
}
ER_IROM2 0x00007E00 0x00000200 { ; 400B NSC区域
interface.o (+RO)
}
}
RW_IRAM1 0x20000000 0x00002000 { ; 8KB安全RAM
.ANY (+RW +ZI)
}
在MDK中设置非安全项目:
非安全代码通过特定方式调用安全函数:
c复制// main_ns.c
#include "interface.h" // 安全接口头文件
int main(void)
{
int result = secure_func1(42); // 调用安全函数
while(1) {
// 应用主循环
}
}
非安全代码必须遵守以下规则:
c复制// 错误示例:尝试直接访问安全内存
volatile uint32_t *secure_var = (uint32_t*)0x20000000;
*secure_var = 42; // 将触发安全错误
// 正确方式:通过安全接口访问
set_secure_variable(42); // 由安全函数处理
混合调试:
调试访问级别:
安全错误诊断:
函数调用问题:
cmse_nonsecure_entry属性内存访问错误:
实现安全OTA更新的关键要素:
产品化阶段建议:
c复制// 生产编程前配置
BOCOR->CEKEY1 = 0x12345678; // 修改默认密钥
BOCOR->CEKEY2 = 0x9ABCDEF0;
UROW->SECURITY |= SECURITY_LOCK;
在实际项目中,我们测量到典型的Secure-to-Non-secure调用开销约为20-30个时钟周期,而Non-secure-to-Secure调用约为50-60个周期。合理的设计应使跨域调用保持在最低必要水平。