STM32MP157 移植笔记(一):U-Boot 编译与启动调试
EMTime

近年来,异构多核 SoC 在嵌入式领域越来越常见,其中 STM32MP157 凭借其** Cortex-A7 + Cortex-M4 双核架构** 和良好的开源生态,成为许多开发者进行 Linux 系统移植和定制的理想平台;

相比传统的裸机 MCU 开发,STM32MP157 的启动流程更为复杂,涉及 BootROM → FSBL(如 TF-A)→ U-Boot → 内核 → 根文件系统 多个阶段,每一步都可能出现问题,每一步也都值得理解与掌控;

本系列文章将以实战为核心,记录我在 STM32MP157 平台上进行完整 Linux 移植的过程:包括 U-Boot 编译与调试、设备树配置、内核裁剪与驱动适配、根文件系统构建与挂载 等等;本篇作为第一篇,将重点放在 U-Boot 的获取、配置、编译、启动与串口调试,为后续系统启动打下基础;

本系列内容主要参考自 华清远见的嵌入式 Linux 教程,并结合我自己的实操经验进行补充、优化和归纳,力求贴近真实开发场景,便于读者实践与扩展;

如果你也在迈入 STM32MP1 系列的世界,希望本博客能帮你少走一些弯路,快速搭建起属于自己的 Linux 开发环境;

环境准备

本次移植所使用的环境基于 Ubuntu 18.04 LTS,配套的是较旧版本的 SDK 与源码(20 年),即便版本偏旧,但在 STM32MP1 系列开发中仍具代表性,适合入门与学习;

本文采用 Docker 容器方式搭建开发环境(Ubuntu 18.04 LTS),优点是可快速部署、便于还原;如果你不熟悉 Docker,也可以选择直接在虚拟机或实体机中安装 Ubuntu 18.04 环境;
容器采用 bind 挂载本地 SDK 和源码目录,便于调试与编译;建议通过 –privileged 启动容器,并开启 /dev、/proc 等挂载;

在进行 U-Boot 编译之前,请先确保以下必要工具链已安装:

1
2
sudo apt update
sudo apt install -y file tar xz-utils findutils patch make gcc bc libssl-dev g++ cpio unzip device-tree-compiler python3 nano

TF 卡分区准备

为实现 TF 卡启动,我们需要手动对 TF 卡进行分区和格式化,确保各阶段引导程序能被正确识别与加载;

  • 清除原有分区表并建立新的 MSDOS 分区表:
1
sudo parted -s /dev/sdX mklabel msdos
  • 使用 sgdisk 对 TF 卡重建分区表(含启动及根文件系统):
1
2
3
4
5
6
7
sudo sgdisk --resize-table=128 -a 1 \
-n 1:34:545 -c 1:fsbl1 \
-n 2:546:1057 -c 2:fsbl2 \
-n 3:1058:5153 -c 3:ssbl \
-n 4:5154:136225 -c 4:bootfs \
-n 5:136226 -c 5:rootfs \
-A 4:set:2 -p /dev/sdX -g

执行成功如下所示:

请将 /dev/sdX 替换为实际的 TF 卡设备名,如 /dev/sdb,确保避免误操作破坏其他磁盘数据;

SDK 与源码获取

SDK 与源码
提取密码: Tac3I

文件为 tar.xz 格式,请使用 tar -xvf 命令解压,解压之后,可进入 stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24 查看文件,其中 sdk 就是 SDK 安装文件所在文件夹,source 就是源码所在文件夹;

SDK 安装

该 SDK 是 ST 官方构建的交叉编译工具链(Toolchain),提供了用于构建 U-Boot、Linux 内核、Buildroot 系统和用户空间程序所需的编译器、头文件与库文件;
后续进行 STM32MP157 架构相关的编译工作时,均需依赖该 SDK 环境;

SDK 在解压时需要选择一个目录,作为 SDK 的安装目录,在这里我就把 sdk 文件夹改名为 sdk_installer ,然后新建一个名为 sdk 的文件夹,作为 SDK 的实际安装目录;

1
mv sdk sdk_installer && mkdir sdk

执行之后文件结构如下:

1
2
3
4
stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/
├── sdk_installer/ # 安装器(.sh文件)
├── sdk/ # 安装完成的 SDK 目录
└── source/ # 包含 u-boot、kernel、buildroot 等源码

进入 sdk_installer 的目录,执行名为 st-image-weston-openstlinux-weston-stm32mp1-x86_64-toolchain-3.1-openstlinux-5.4-dunfell-mp1-20-06-24.sh 的文件,接下来会提示你输入安装目录,输入我们刚刚创建的 sdk 目录,继续根据提示输入 y ,然后等待安装完成即可,整个过程提示如下:

根据提示,我们只需要执行下面的命令,就可以使用 SDK 了:

1
2
3
4
. /root/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sdk/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

# 或者可以执行
source /root/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sdk/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

每次手动执行 source 命令略显繁琐,我们可以将它加入 .bashrc 文件,让终端在启动时自动加载,提升开发效率:

1
nano ~/.bashrc

在文件末尾添加下面这条命令,然后保存退出:

1
. /root/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sdk/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi

再执行 . ~/.bashrc 命令,使配置生效;

配置成功之后,执行 echo $CC 和 echo $ARCH 命令,如果输出如下,则表示 SDK 配置成功:

提示:安装 SDK 的意义不只是配置 $CC 和 $ARCH,更重要的是自动设置了交叉工具链、动态库路径、头文件路径等编译参数,使得我们可以一条命令完成 U-Boot、内核、应用程序等的 ARM 架构编译;
sdk_installer 以及 tar.xz 文件可以删除,后续编译过程中不再需要;

源码获取

进入 source 目录,可以看到里面包含多个源码文件夹,其中 u-boot、kernel、buildroot 是我们移植过程中需要用到的,其他文件夹可以忽略;

1
cd sources/arm-ostl-linux-gnueabi && ls

可以看到,u-boot、kernel、buildroot 等源码文件夹都在这里:

U-Boot 编译

源码准备

上一步看到的源码仍然是压缩包,我们进入 U-Boot 的目录,在目录下能看到 README.HOW_TO.txt 文件,里面介绍了依赖的安装和源码的解压,我们按照文件操作即可;

安装依赖

1
sudo apt install libncurses5-dev libncursesw5-dev
  • SDK 环境加载,这一步和之前 SDK 安装时一样,不再赘述;

源码解压与补丁

1
2
3
4
5
tar xfz u-boot-stm32mp-2020.01-r0.tar.gz

cd u-boot-stm32mp-2020.01

for p in `ls -1 ../*.patch`; do patch -p1 < $p; done

配置与编译

复制默认配置文件

首先,基于 ST 官方的 trusted 配置,我们复制一份作为我们板子的基础配置:

1
2
3
cp configs/stm32mp15_trusted_defconfig configs/stm32mp15_emtime_trusted_defconfig

make stm32mp15_emtime_trusted_defconfig

板级设备树修改

ST 的默认设备树不包含我们自定义的板卡信息,因此需要在 arch/arm/dts/ 中添加我们自己的设备树文件,设备树可以参考 arch/arm/dts/ 目录下的其它配置,甚至可以先直接用官方的(只要你的硬件契合),主要涉及以下几个文件(以官方文件为例):

1
2
3
arch/arm/dts/stm32mp15xx-dkx.dtsi
arch/arm/dts/stm32mp157a-dk1.dts
arch/arm/dts/stm32mp157a-dk1-u-boot.dtsi

可以将文件复制一份,自己定义一个名字,然后修改文件,添加自己板子的硬件信息:

1
2
3
cp arch/arm/dts/stm32mp15xx-dkx.dtsi arch/arm/dts/stm32mp15xx-emtime.dtsi
cp arch/arm/dts/stm32mp157a-dk1.dts arch/arm/dts/stm32mp157a-emtime.dts
cp arch/arm/dts/stm32mp157a-dk1-u-boot.dtsi arch/arm/dts/stm32mp157a-emtime-u-boot.dtsi

修改其中的文件,修改文件引用相关的信息,如下:

  • 将 arch/arm/dts/stm32mp157a-emtime.dts 的 #include “stm32mp15xx-dkx.dtsi” 改为 #include “stm32mp15xx-emtime.dtsi”
  • 修改 arch/arm/dts/Makefile,在 stm32mp157 (dtb-$(CONFIG_STM32MP15x)) 中加入我们自己刚刚添加的文件,即加上 stm32mp157a-emtime.dtb \

注意:这一部分必须按照你实际情况进行修改(比如修改电源配置、增加 SD 卡支持、增加 eMMC 驱动、添加网卡驱动等),否则会导致无法正常引导或者无法正常驱动的问题;

之后有机会,我会专门写一篇关于设备树的文章,介绍如何编写设备树;
虽然我现在还没有编写这一部分的文章,但是设备树是 Linux 内核中非常重要的一部分,也是嵌入式开发中非常重要的一部分,所以建议有时间的话,一定要去学习一下;

bootcmd 修改

  • 若要进行bootcmd的修改,则可以修改 include/configs/stm32mp1.h 文件,在 #ifdef CONFIG_BOOTCOMMAND 之后添加如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifdef CONFIG_BOOTCOMMAND

#undef CONFIG_BOOTCOMMAND

#endif

#define CONFIG_BOOTCOMMAND \
"if ext4ls mmc 1:2 /; then " \
"setenv bootcmd ext4load mmc 1:2 c2000000 uImage; " \
"setenv bootcmd \"${bootcmd};ext4load mmc 1:2 c4000000 stm32mp157a-emtime.dtb\"; " \
"setenv bootcmd \"${bootcmd};bootm c2000000 - c4000000\"; " \
"setenv bootargs rootwait rw console=ttySTM0,115200 root=/dev/mmcblk1p3; " \
"saveenv; run bootcmd; " \
"else run bootcmd_stm32mp; fi"
  • 这里采用了偷懒的方法,在判断 CONFIG_BOOTCOMMAND 宏定义的地方,直接将 CONFIG_BOOTCOMMAND 宏定义取消,然后重新定义了 CONFIG_BOOTCOMMAND 宏定义,这样在编译的时候,就会使用我们重新定义的宏定义,而不会使用默认的宏定义;

  • 为什么我要重写 bootcmd?因为他们设计的这一版板子,boot 拨码开关有问题,无论怎么拨,都会进入 USB 烧录模式,但 如果是 USB 烧录的情况,那么是不会正常加载eMMC的,所以我使用了 ext4ls 命令判断 eMMC 是否正常挂载,如果正常挂载,则加载内核和设备树,然后启动,否则执行默认的 bootcmd_stm32mp 进入下载模式;

后续找到了 bootcmd 和 bootargs 的定义,在 include/env_default.h 文件下,可以自行去修改;

U-Boot 配置

执行 make menuconfig 命令,进入配置界面:

1
make menuconfig

其实大部分我们都不用修改,因为我们复制的是官方的配置,官方的配置已经包含了我们需要的配置,而且因为是 U-Boot 的配置,并不需要配置太多驱动相关的东西,这里提一嘴 bootcmd value 吧,在这里我们也可以修改 bootcmd,如果这里修改了,那么上面代码中的操作是可以省略的;

配置完之后记得保存,然后就可以退出 menuconfig 界面了;

如果修改了配置,那么记得更新配置文件:

1
cp .config configs/stm32mp15_emtime_trusted_defconfig

编译

修改上一个目录中的 Makefile 文件:

1
nano ../Makefile.sdk

对文件进行如下操作:

  • 在UBOOT_CONFIGS中添加stm32mp15_emtime_trusted_defconfig,trusted,u-boot.stm32
  • 在DEVICE_TREE中添加stm32mp157a-emtime

然后进行编译即可:

1
2
3
make distclean

make -f $PWD/../Makefile.sdk all UBOOT_CONFIGS=stm32mp15_emtime_trusted_defconfig,trusted,u-boot.stm32 -j$(nproc) 2> error.log

可以通过 cat error.log 查看编译错误,如果编译成功,那么会在 ../build-trusted 目录下会生成对应的文件,比如我们这里就是如下图框住的 u-boot-stm32mp157a-emtime-trusted.stm32 文件;

使用 sudo dd if=../build-trusted/u-boot-stm32mp157a-emtime-trusted.stm32 of=/dev/sdXY bs=1024 seek=8 conv=fsync,notrunc 命令将生成的文件烧录到 SD 卡中,然后就可以正常引导了;

未完待续…

 Comments
Comment plugin failed to load
Loading comment plugin
💡 请正确填写您的邮箱,以确保能接收到来自本博客的评论回复通知~(*^▽^*)