2. ARMv8-A 启动流程与异常等级切换分析
任务说明
本案例目标是在ARM TrustZone环境中配置TZASC(TrustZone Address Space Controller),实现将外部DDR内存划分为安全区域与非安全区域。完成配置后,安全世界处理器可访问全部内存,非安全世界处理器仅可访问被标记为非安全的区域,访问安全区域时将触发异常。
环境依赖
本案例基于ARMv8-A架构,使用QEMU的virt平台进行验证(已内置TZASC控制器模型)。需要以下工具:
- QEMU 7.0+(支持ARMv8-A virt平台)
- aarch64-linux-gnu-gcc交叉编译器
- GNU Make
核心实现
以下代码实现TZASC初始化与区域配置。系统共1GB DDR内存(地址0x40000000~0x7FFFFFFF),将其划分为:安全区域(低512MB)与非安全区域(高512MB)。
// tzasc_config.c
#include <stdint.h>
// TZASC寄存器基地址(QEMU virt平台)
#define TZASC_BASE 0xFFFF000000000000ULL // 实际映射由平台决定,此处为逻辑地址
// 实际QEMU中TZASC位于内存映射控制器的GIC之后,此处使用物理地址
#define TZASC_PHYS_BASE 0x0C000000ULL
// TZASC寄存器偏移
#define TZASC_REG_CONFIG 0x000
#define TZASC_REG_ACTION 0x004
#define TZASC_REG_GATE_KEEPER 0x008
#define TZASC_REG_REGION_BASE 0x100
#define TZASC_REG_REGION_TOP 0x104
#define TZASC_REG_REGION_ATTR 0x108
// 区域属性位定义
#define REGION_ATTR_ENABLE (1ULL << 0)
#define REGION_ATTR_SECURE (1ULL << 1) // 1=安全, 0=非安全
#define REGION_ATTR_SIZE_SHIFT 32
#define REGION_ATTR_SIZE_MASK (0xFULL << REGION_ATTR_SIZE_SHIFT)
// 内存布局
#define DDR_BASE 0x40000000ULL
#define DDR_SIZE 0x40000000ULL // 1GB
#define SECURE_SIZE 0x20000000ULL // 512MB安全区域
static volatile uint32_t* tzasc_reg(uint32_t offset) {
return (volatile uint32_t*)(TZASC_PHYS_BASE + offset);
}
static void tzasc_write(uint32_t offset, uint32_t value) {
*tzasc_reg(offset) = value;
__asm volatile("dsb sy");
}
static uint32_t tzasc_read(uint32_t offset) {
return *tzasc_reg(offset);
}
// 配置TZASC:将DDR分为安全/非安全两个区域
void tzasc_init(void) {
// 1. 关闭TZASC(通过GATE_KEEPER)
tzasc_write(TZASC_REG_GATE_KEEPER, 0x0);
// 2. 设置全局配置:启用TZASC,非安全访问安全区域时触发异常
tzasc_write(TZASC_REG_CONFIG, 0x1);
tzasc_write(TZASC_REG_ACTION, 0x1); // 错误响应类型
// 3. 配置区域0:整个DDR默认为安全(由硬件复位决定)
// 此处显式设置以确保一致性
tzasc_write(TZASC_REG_REGION_BASE + 0x00, (uint32_t)(DDR_BASE >> 32));
tzasc_write(TZASC_REG_REGION_BASE + 0x04, (uint32_t)(DDR_BASE & 0xFFFFFFFF));
tzasc_write(TZASC_REG_REGION_TOP + 0x00, (uint32_t)((DDR_BASE + DDR_SIZE - 1) >> 32));
tzasc_write(TZASC_REG_REGION_TOP + 0x04, (uint32_t)((DDR_BASE + DDR_SIZE - 1) & 0xFFFFFFFF));
// 区域0属性:启用,安全,粒度1KB(size字段=0表示1KB粒度)
uint64_t attr0 = REGION_ATTR_ENABLE | REGION_ATTR_SECURE;
tzasc_write(TZASC_REG_REGION_ATTR + 0x00, (uint32_t)(attr0 >> 32));
tzasc_write(TZASC_REG_REGION_ATTR + 0x04, (uint32_t)(attr0 & 0xFFFFFFFF));
// 4. 配置区域1:非安全区域(DDR高512MB)
uint64_t ns_base = DDR_BASE + SECURE_SIZE;
uint64_t ns_top = DDR_BASE + DDR_SIZE - 1;
tzasc_write(TZASC_REG_REGION_BASE + 0x10, (uint32_t)(ns_base >> 32));
tzasc_write(TZASC_REG_REGION_BASE + 0x14, (uint32_t)(ns_base & 0xFFFFFFFF));
tzasc_write(TZASC_REG_REGION_TOP + 0x10, (uint32_t)(ns_top >> 32));
tzasc_write(TZASC_REG_REGION_TOP + 0x14, (uint32_t)(ns_top & 0xFFFFFFFF));
// 区域1属性:启用,非安全(清除SECURE位)
uint64_t attr1 = REGION_ATTR_ENABLE; // 不设置SECURE位
tzasc_write(TZASC_REG_REGION_ATTR + 0x10, (uint32_t)(attr1 >> 32));
tzasc_write(TZASC_REG_REGION_ATTR + 0x14, (uint32_t)(attr1 & 0xFFFFFFFF));
// 5. 使能TZASC
tzasc_write(TZASC_REG_GATE_KEEPER, 0x1);
}
// 验证函数:读取当前配置
void tzasc_dump_config(void) {
uint32_t gate = tzasc_read(TZASC_REG_GATE_KEEPER);
uint32_t cfg = tzasc_read(TZASC_REG_CONFIG);
// 读取区域0属性
uint32_t attr0_hi = tzasc_read(TZASC_REG_REGION_ATTR + 0x00);
uint32_t attr0_lo = tzasc_read(TZASC_REG_REGION_ATTR + 0x04);
uint64_t attr0 = ((uint64_t)attr0_hi << 32) | attr0_lo;
// 读取区域1属性
uint32_t attr1_hi = tzasc_read(TZASC_REG_REGION_ATTR + 0x10);
uint32_t attr1_lo = tzasc_read(TZASC_REG_REGION_ATTR + 0x14);
uint64_t attr1 = ((uint64_t)attr1_hi << 32) | attr1_lo;
// 通过串口输出(实际环境中使用UART驱动)
// 此处仅作示意,验证时通过调试器读取寄存器
(void)gate; (void)cfg; (void)attr0; (void)attr1;
}
// 简单测试入口
void tzasc_test(void) {
tzasc_init();
tzasc_dump_config();
// 预期结果:
// - GATE_KEEPER = 0x1
// - 区域0属性 = 0x3 (ENABLE | SECURE)
// - 区域1属性 = 0x1 (仅ENABLE,非安全)
}
编译与运行
创建最小启动文件并编译:
# start.S - 最小启动代码
.section .text
.global _start
_start:
ldr x0, =0x0C000000
// 调用tzasc_test
bl tzasc_test
1: b 1b // 死循环
# Makefile
CROSS = aarch64-linux-gnu-
CFLAGS = -nostdlib -ffreestanding -Wall
all: tzasc.bin
tzasc.bin: start.o tzasc_config.o
$(CROSS)ld -Ttext=0x40000000 -o tzasc.elf $^
$(CROSS)objcopy -O binary tzasc.elf tzasc.bin
%.o: %.c
$(CROSS)gcc $(CFLAGS) -c $< -o $@
%.o: %.S
$(CROSS)gcc $(CFLAGS) -c $< -o $@
clean:
rm -f *.o *.elf *.bin
# 运行(QEMU)
# qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 1G -nographic -kernel tzasc.bin
验证方法
使用QEMU的调试接口验证配置结果:
# 启动QEMU并暂停执行
qemu-system-aarch64 -machine virt -cpu cortex-a57 -m 1G -nographic -kernel tzasc.bin -S -s
# 在另一个终端连接GDB
aarch64-linux-gnu-gdb tzasc.elf
(gdb) target remote :1234
(gdb) continue
(gdb) Ctrl+C # 暂停执行
# 检查TZASC寄存器
(gdb) x/4wx 0x0C000000 # GATE_KEEPER应为0x1
(gdb) x/4wx 0x0C000100 # 区域0属性应为0x3
(gdb) x/4wx 0x0C000110 # 区域1属性应为0x1
通过条件:
- GATE_KEEPER寄存器值为0x1,表示TZASC已使能
- 区域0属性寄存器值为0x3(启用且安全)
- 区域1属性寄存器值为0x1(启用且非安全)
- 区域1的基地址寄存器指向0x60000000,顶地址指向0x7FFFFFFF
若以上条件全部满足,则TZASC地址空间隔离配置成功。非安全世界访问0x60000000~0x7FFFFFFF地址段将正常进行,而访问0x40000000~0x5FFFFFFF地址段将触发异常。