4. 使用TZPC保护外设寄存器

任务说明

本案例基于ARM TrustZone技术,使用TZPC(TrustZone Protection Controller)将GPIO外设配置为安全属性,并验证非安全世界(Normal World)对该外设的访问被硬件拒绝。目标平台为带有TZPC的ARM Cortex-A系列SoC(例如ARM Juno或带有TZPC的FPGA平台)。

TZPC工作原理

TZPC通过DECPROT[0:3]信号控制外设的安全属性。当DECPROT[0]为高电平时,对应外设被标记为安全;为低电平时,外设对安全和非安全世界均可见。本案例将GPIO外设的DECPROT[0]置为高电平,使其仅允许安全世界访问。

代码实现

以下代码实现TZPC寄存器配置及非安全访问测试。代码分为安全世界驱动和非安全世界测试两部分,通过SMC(Secure Monitor Call)切换世界。


// tzpc_gpio_test.c
// 安全世界代码:配置TZPC并将GPIO设为安全属性
#include <stdint.h>
#include <stdio.h>

// 寄存器地址定义(以Juno平台为例)
#define TZPC_BASE       0x7C000000  // TZPC基地址
#define TZPC_DECPROT0   (*(volatile uint32_t *)(TZPC_BASE + 0x000))
#define TZPC_DECPROT1   (*(volatile uint32_t *)(TZPC_BASE + 0x004))
#define TZPC_DECPROT2   (*(volatile uint32_t *)(TZPC_BASE + 0x008))
#define TZPC_DECPROT3   (*(volatile uint32_t *)(TZPC_BASE + 0x00C))

#define GPIO_BASE       0x1C010000  // GPIO控制器基地址
#define GPIO_DATA       (*(volatile uint32_t *)(GPIO_BASE + 0x000))
#define GPIO_DIR        (*(volatile uint32_t *)(GPIO_BASE + 0x004))

// SMC调用函数,用于切换到非安全世界
static inline uint64_t smc_call(uint64_t func_id, uint64_t arg0, uint64_t arg1)
{
    register uint64_t x0 asm("x0") = func_id;
    register uint64_t x1 asm("x1") = arg0;
    register uint64_t x2 asm("x2") = arg1;
    asm volatile("smc #0" : "+r"(x0) : "r"(x1), "r"(x2));
    return x0;
}

// 配置TZPC将GPIO设为安全属性
void tzpc_config_gpio_secure(void)
{
    // 读取当前DECPROT0值,将bit0置1(GPIO对应DECPROT[0])
    uint32_t reg_val = TZPC_DECPROT0;
    reg_val |= 0x1;  // 将GPIO设为安全
    TZPC_DECPROT0 = reg_val;
    
    // 配置GPIO为输出模式
    GPIO_DIR = 0xFFFFFFFF;
    GPIO_DATA = 0x0;  // 初始输出低电平
}

// 安全世界GPIO操作测试
void secure_world_gpio_test(void)
{
    // 安全世界应能正常访问GPIO
    GPIO_DATA = 0x1;  // 输出高电平
    printf("[Secure World] GPIO write successful, value=0x1\n");
    
    uint32_t read_back = GPIO_DATA;
    printf("[Secure World] GPIO read back: 0x%x\n", read_back);
}

// 非安全世界GPIO访问测试(通过SMC进入)
void normal_world_gpio_test(void)
{
    // 尝试在非安全世界访问GPIO
    // 该函数将在非安全世界执行
    printf("[Normal World] Attempting GPIO access...\n");
    
    // 尝试写GPIO - 预期触发权限错误
    GPIO_DATA = 0x1;
    printf("[Normal World] GPIO write succeeded (unexpected!)\n");
}

int main(void)
{
    printf("=== TZPC GPIO Security Test ===\n");
    
    // 1. 配置TZPC将GPIO设为安全属性
    tzpc_config_gpio_secure();
    printf("[Secure World] TZPC configured: GPIO set to Secure\n");
    
    // 2. 安全世界访问测试
    secure_world_gpio_test();
    
    // 3. 切换到非安全世界进行访问测试
    printf("[Secure World] Switching to Normal World...\n");
    smc_call(0x84000001, (uint64_t)normal_world_gpio_test, 0);
    
    // 4. 返回安全世界,检查状态
    printf("[Secure World] Returned from Normal World\n");
    
    // 5. 验证GPIO仍可被安全世界访问
    uint32_t final_val = GPIO_DATA;
    printf("[Secure World] GPIO final value: 0x%x\n", final_val);
    
    return 0;
}

编译与运行

本代码需要运行在支持TrustZone的ARM平台上,并具备以下环境:

  • ARM TrustZone硬件支持(Cortex-A系列处理器)
  • EL3固件(如ARM Trusted Firmware)提供SMC调用支持
  • 安全世界操作系统或裸机环境

编译命令(使用ARM交叉编译器):


aarch64-linux-gnu-gcc -c tzpc_gpio_test.c -o tzpc_gpio_test.o
aarch64-linux-gnu-ld -Ttext=0x80000000 tzpc_gpio_test.o -o tzpc_gpio_test.elf

将编译后的镜像加载到安全世界内存地址并执行。

验证方法与判定标准

运行程序后,观察串口输出:

  1. 安全世界访问成功:程序应输出[Secure World] GPIO write successful, value=0x1,表明安全世界可以正常访问GPIO。
  2. 非安全世界访问被拒绝:当切换到非安全世界执行normal_world_gpio_test时,GPIO写操作应触发权限异常(Data Abort),程序不会输出[Normal World] GPIO write succeeded,而是进入异常处理或系统挂起。
  3. 安全世界状态保持:从非安全世界返回后,安全世界仍能正常读取GPIO值,输出[Secure World] GPIO final value: 0x1。

判定标准:若安全世界访问成功且非安全世界访问触发异常(无成功输出),则证明TZPC已正确将GPIO配置为安全属性,非安全世界访问被硬件拒绝。

安全世界 (Secure World) 非安全世界 (Normal World) TZPC DECPROT[0]=1 (Secure) GPIO 外设 安全属性 访问允许 访问被拒绝 (Data Abort) SMC 调用切换 安全世界可正常读写GPIO 非安全世界访问触发异常

预期输出

在安全世界正常执行时,预期串口输出如下(非安全世界部分因触发异常而中断):


=== TZPC GPIO Security Test ===
[Secure World] TZPC configured: GPIO set to Secure
[Secure World] GPIO write successful, value=0x1
[Secure World] GPIO read back: 0x1
[Secure World] Switching to Normal World...
[Normal World] Attempting GPIO access...
(此处触发Data Abort,系统进入异常处理)

若非安全世界访问未触发异常且成功写入,则说明TZPC配置未生效,需检查DECPROT寄存器映射是否正确。