STM32MP157开发板TF-A移植与安全启动实践

1. STM32MP157开发板与Trusted Firmware-A概述

STM32MP157是STMicroelectronics推出的一款高性能、低功耗的Cortex-A7双核处理器,广泛应用于工业控制、消费电子和物联网设备。作为一款兼具MCU实时性和MPU计算能力的跨界处理器,其开发板在嵌入式领域备受关注。

Trusted Firmware-A(TF-A)是ARM官方推出的开源固件项目,为ARMv8-A和ARMv7-A架构提供安全启动和运行时服务的参考实现。它包含BL1、BL2、BL31等阶段,构成了ARM平台的安全启动链基础。在STM32MP157这类Cortex-A处理器上,TF-A负责从ROM代码执行后的早期硬件初始化、安全监控以及向下一阶段(如U-Boot或OP-TEE)的跳转。

移植TF-A到STM32MP157开发板的主要挑战在于:

  • 处理器特定的时钟树配置
  • DDR控制器初始化参数适配
  • 安全域与非安全域的划分
  • 与STM32MP1特有的硬件安全机制集成

2. 开发环境准备与源码获取

2.1 交叉编译工具链配置

对于STM32MP157的Cortex-A7核心,推荐使用Linaro GCC arm-none-eabi工具链。以下是具体安装步骤:

wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-none-eabi/gcc-linaro-7.5.0-2019.12-x86_64_arm-none-eabi.tar.xz tar xf gcc-linaro-7.5.0-2019.12-x86_64_arm-none-eabi.tar.xz export PATH=$PATH:/path/to/gcc-linaro-7.5.0-2019.12-x86_64_arm-none-eabi/bin

验证安装:

arm-none-eabi-gcc --version

2.2 TF-A源码获取与目录结构

ST官方提供了针对STM32MP1优化的TF-A分支:

git clone https://github.com/STMicroelectronics/arm-trusted-firmware.git -b v2.6-stm32mp cd arm-trusted-firmware

关键目录说明:

  • plat/st/stm32mp1/:STM32MP1平台特定代码
  • drivers/st/:ST外设驱动
  • include/drivers/st/:ST特定头文件

2.3 依赖项安装

构建TF-A需要以下工具:

sudo apt-get install device-tree-compiler bc python3 python3-pip pip3 install pyelftools

3. STM32MP157硬件适配层实现

3.1 时钟树配置

STM32MP157的时钟系统复杂,需要在plat/st/stm32mp1/stm32mp1_def.h中定义:

#define STM32MP_CLKCRYSTAL_FREQ 24000000 #define STM32MP_MPU_HSE_CLK 24000000 #define STM32MP_AXI_MAX_FREQ 650000000 #define STM32MP_APB_MAX_FREQ 208000000

时钟初始化流程:

  1. 使能HSI作为临时时钟源
  2. 配置PLL1/PLL2/PLL3/PLL4
  3. 切换系统时钟到PLL输出
  4. 配置各总线分频器

3.2 DDR控制器配置

DDR参数在plat/st/stm32mp1/stm32mp1_ddr.c中定义。以Micron MT41K256M16TW-107为例:

static const struct stm32mp1_ddr_config ddr_config = { .name = "DDR3-1066", .info = { .size = 0x20000000, // 512MB .nb_banks = 8, .cas_latency = 7, .t_rcd_ns = 15, .t_rp_ns = 15, .t_ras_ns = 38, }, .timing = { .rtr = 0x258, // Refresh rate .tpr0 = 0x42D00A32, .tpr1 = 0x0B0A870C, .tpr2 = 0x00006423, }, };

注意:DDR参数必须与硬件设计严格匹配,错误的时序配置会导致系统不稳定或无法启动。

3.3 设备树适配

TF-A使用精简的设备树片段,位于fdts/stm32mp157c-<board>.dts。关键节点包括:

/ { memory@c0000000 { device_type = "memory"; reg = <0xc0000000 0x20000000>; // DDR映射地址和大小 }; clocks { clk_hse: clk-hse { clock-frequency = <24000000>; }; }; };

4. 构建与烧写流程

4.1 编译配置选项

STM32MP157提供多种启动配置,常用编译命令:

make CROSS_COMPILE=arm-none-eabi- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 \ DTB_FILE_NAME=stm32mp157c-<board>.dtb STM32MP_SDMMC=1 STM32MP_EMMC=1 \ AARCH32_SP=sp_min BL33=<u-boot>.bin all

关键参数说明:

  • AARCH32_SP:选择SP_MIN或OP-TEE作为BL32
  • BL33:指定U-Boot镜像路径
  • STM32MP_SDMMC/EMMC:启用存储接口支持

4.2 生成镜像处理

编译完成后生成关键文件:

  • build/stm32mp1/release/tf-a-stm32mp157c-<board>.stm32:可直接烧写的二进制
  • build/stm32mp1/release/bl2.bin:BL2独立镜像
  • build/stm32mp1/release/bl32.bin:BL32镜像(如SP_MIN)

使用STM32CubeProgrammer烧写:

stm32programmer -c port=USB1 -w tf-a-stm32mp157c-<board>.stm32 0x01

4.3 启动流程验证

成功启动时,通过串口输出应看到类似日志:

BL2: v2.6(debug):<git hash> BL2: Built : 12:34:56, Jan 1 2023 NOTICE: CPU: STM32MP157CAC Rev.Z NOTICE: Model: STMicroelectronics STM32MP157C-<board> INFO: BL2: Doing platform setup INFO: BL2: Loading image id 3 INFO: Loading image id=3 at address 0x2ffc0000 INFO: Image id=3 loaded: 0x2ffc0000 - 0x2ffc3a00 INFO: BL2: Loading image id 4

5. 调试与问题排查

5.1 常见启动失败场景

  1. 卡在BL2阶段

    • 检查DDR配置参数
    • 确认时钟树配置正确
    • 验证电源管理单元(PMIC)初始化
  2. BL32加载失败

    • 检查BL33地址是否正确
    • 确认存储设备驱动已启用
    • 验证设备树中的内存映射
  3. 安全异常触发

    • 检查TrustZone配置
    • 验证内存保护单元(MPU)设置

5.2 调试技巧

  1. 串口日志增强: 在plat/st/stm32mp1/stm32mp1_def.h中增加:
#define DEBUG 1 #define LOG_LEVEL 40 // 最高详细级别
  1. JTAG调试: 使用OpenOCD连接:
openocd -f interface/stlink.cfg -f target/stm32mp15x.cfg
  1. 内存检测工具: 在BL2阶段添加内存测试:
void bl2_platform_setup(void) { ... ddr_test(0xc0000000, 0x20000000); // 简单的读写测试 }

6. 高级配置与优化

6.1 安全启动实现

  1. 密钥配置: 在plat/st/stm32mp1/stm32mp1_private.h中定义:
#define STM32MP_SSP_ENABLE 1 #define STM32MP_SKP_ENABLE 1
  1. 签名流程: 使用ST提供的签名工具:
python3 scripts/sign_helper.py \ --key keys/stm32mp1_key_private.pem \ --in tf-a.bin --out tf-a-signed.stm32

6.2 功耗管理集成

plat/st/stm32mp1/pm/中添加低功耗状态处理:

void stm32mp1_pwr_domain_off(const psci_power_state_t *target_state) { if (target_state->pwr_domain_state[MPIDR_AFFLVL0] == PLAT_MAX_OFF_STATE) { mmio_write_32(PWR_CR3, mmio_read_32(PWR_CR3) | PWR_CR3_DDRSREN); } }

6.3 多核启动控制

配置CPU1的启动流程:

void stm32mp1_cpu1_on(u_register_t mpidr) { mmio_write_32(RCC_MP_GRSTCSETR, RCC_MP_GRSTCSETR_MPUP1RST); mmio_write_32(RCC_MP_SSCR, mmio_read_32(RCC_MP_SSCR) | RCC_MP_SSCR_C1SS); }

在实际项目中,我发现STM32MP157的TF-A移植最关键的三个点是:DDR配置的精确性、时钟树的正确初始化、以及与ST特定安全机制的集成。特别是在DDR参数配置上,建议先用ST提供的DDR配置工具生成基础参数,再根据实际硬件进行微调。另外,当遇到无法解释的启动失败时,检查PMIC的默认配置往往能发现意外问题——有些开发板的PMIC默认处于低功耗模式,需要在BL1阶段就进行特殊处理。