4. TF-A与U-Boot板级移植基础
在STM32MP系列平台上,从芯片上电到Linux内核启动,中间要经过两段关键的引导程序:TF-A(Trusted Firmware-A)和U-Boot。TF-A负责建立安全运行环境、初始化DDR、加载并校验后续镜像;U-Boot则承担板级外设初始化、镜像加载和启动参数传递。板级移植的核心工作,就是让这两段引导程序“认识”你的板子——知道DDR怎么配、串口在哪、从哪里启动、设备树长什么样。本节围绕TF-A启动阶段、U-Boot板级文件结构、设备树与配置头文件、编译烧录与串口控制台调试四条主线展开。
一、TF-A启动阶段:从ROM Code到BL33
STM32MP1的启动链遵循ARM Trusted Firmware的分级模型。芯片内部固化的ROM Code(BL0)最先运行,它根据BOOT引脚和OTP配置选择启动介质(eMMC、SD卡、NAND、串口/USB),把FSBL(First Stage Boot Loader)加载到SYSRAM中执行。在STM32MP1上,FSBL就是TF-A的BL2阶段。
BL2运行在SYSRAM(约256KB)中,主要完成三件事:初始化时钟树、初始化DDR控制器、把后续镜像从启动介质搬到DDR。DDR初始化参数来自设备树中的DDR节点,包括时序参数、容量、位宽等,这部分参数一旦配错,BL2会在DDR训练阶段卡死,串口通常只输出到“DDR init”附近就停止。BL2随后加载BL31(运行时服务,处理安全监控和电源管理)、BL32(OP-TEE,可选)和BL33(即U-Boot),并通过SMC指令逐级跳转。
BL31常驻DDR,负责PSCI电源管理、系统复位和安全世界切换;BL33即U-Boot的SPL或完整U-Boot。理解这条链路的意义在于:当串口停在某个阶段时,你能迅速判断是DDR参数、镜像签名还是加载地址出了问题。
二、U-Boot板级文件结构
U-Boot的板级支持集中在几个固定位置。以STM32MP15x为例,板级目录位于board/st/stm32mp1/,其中board.c包含板级初始化函数board_init()、DDR相关回调、以及环境变量默认值;stm32mp1.c处理电源、USB、按键等板级特性。配置头文件位于include/configs/stm32mp1.h,定义环境变量、启动命令、内存布局等。
设备树文件位于arch/arm/dts/,如stm32mp157a-dk1.dts及其包含的stm32mp15xx-dkx.dtsi、stm32mp151.dtsi。U-Boot的设备树与Linux设备树同源,但U-Boot只使用其中一部分节点(串口、MMC、以太网、PMIC等),内核启动时会把控制权交给Linux自己的设备树。移植时通常先复制一份参考板的dts,修改模型名、内存大小、串口引脚和启动介质节点。
配置层面,U-Boot使用defconfig文件(如stm32mp15_defconfig)和Kconfig体系。板级差异通过CONFIG_TARGET_STM32MP15、CONFIG_DEFAULT_DEVICE_TREE等选项控制。编译时make stm32mp15_defconfig生成.config,再make DEVICE_TREE=stm32mp157a-dk1指定设备树。
三、设备树与配置头文件
设备树是板级移植的“地图”。以串口为例,U-Boot需要知道使用哪个UART、引脚如何复用、时钟从哪来。典型节点如下:
&usart3 {
pinctrl-names = "default";
pinctrl-0 = <&usart3_pins_a>;
status = "okay";
};
&usart3_pins_a {
pins1 {
pinmux = <STM32_PINMUX('B', 10, AF7)>; /* USART3_TX */
};
pins2 {
pinmux = <STM32_PINMUX('B', 11, AF7)>; /* USART3_RX */
};
};
配置头文件stm32mp1.h中常见宏包括CONFIG_SYS_MMC_ENV_DEV(环境变量存储设备)、CONFIG_BOOTCOMMAND(默认启动命令)、CONFIG_EXTRA_ENV_SETTINGS(自定义环境变量)。例如把启动命令改为从eMMC读取内核:
#define CONFIG_BOOTCOMMAND \
"mmc dev 1; " \
"ext4load mmc 1:4 0xc2000000 /boot/uImage; " \
"ext4load mmc 1:4 0xc4000000 /boot/stm32mp157a-custom.dtb; " \
"bootm 0xc2000000 - 0xc4000000"
这里0xc2000000是内核加载地址,0xc4000000是设备树地址,需与DDR布局一致。地址写错会导致bootm报“Bad Magic Number”或内核解压失败。
四、编译烧录与串口控制台调试
典型编译流程分两步。TF-A:进入tf-a-stm32mp目录,执行make ARM_ARCH_MAJOR=7 ARCH=aarch32 PLAT=stm32mp1 STM32MP15=1 DTB_FILE_NAME=stm32mp157a-custom.dtb,生成tf-a-stm32mp157a-custom.stm32。U-Boot:进入u-boot-stm32mp目录,执行make stm32mp15_defconfig和make DEVICE_TREE=stm32mp157a-custom all,生成u-boot.stm32和u-boot-spl.stm32。
烧录到SD卡时,使用dd写入分区:
dd if=tf-a-stm32mp157a-custom.stm32 of=/dev/sdX bs=1M seek=0 conv=fsync
dd if=u-boot-spl.stm32 of=/dev/sdX bs=1M seek=1 conv=fsync
dd if=u-boot.stm32 of=/dev/sdX bs=1M seek=3 conv=fsync
串口控制台是调试的第一现场。STM32MP1默认使用UART4(DK1板为USART3,视板而定),波特率115200,8N1。上电后应依次看到:
NOTICE: CPU: STM32MP157AAC Rev.Z
NOTICE: Model: STMicroelectronics STM32MP157A-DK1
NOTICE: Board: MB1272 Var1.0 Rev.C-01
INFO: BL2: v2.6(debug):...
INFO: BL2: Loading image id 3
INFO: BL2: Loading image id 5
NOTICE: BL2: Booting BL31
NOTICE: BL31: v2.6(debug):...
U-Boot SPL 2021.10 (Jan 01 2024 - 00:00:00)
U-Boot 2021.10 (Jan 01 2024 - 00:00:00 +0000)
Hit any key to stop autoboot: 0
若串口无输出,先检查波特率、TX/RX是否接反、引脚复用是否被其他功能占用;若停在“DDR init”之后,重点核对DDR设备树参数;若U-Boot能启动但找不到内核,检查bootcmd中的分区号和加载地址。进入U-Boot命令行后,可用mmc list、mmc dev 1、ext4ls mmc 1:4 /boot逐级确认存储和文件系统,再用printenv查看环境变量,最后boot验证启动。
板级移植不是一次写对,而是“串口有输出→DDR稳定→U-Boot命令行可用→内核能加载”的逐级收敛过程。掌握TF-A启动阶段、U-Boot文件结构、设备树与配置头文件、编译烧录与串口调试这四块,就具备了STM32MP平台板级适配的基本能力。