4. U-Boot基础与配置:U-Boot源码结构、编译配置(defconfig)、设备树(dts)在U-Boot中的作用、常用命令与脚本化
U-Boot(Das U-Boot)是嵌入式Linux系统中应用最广泛的引导加载程序(Bootloader)。它负责初始化硬件、加载内核和设备树,并将控制权移交给操作系统。本章将深入剖析U-Boot的源码结构、配置体系、设备树的作用以及常用命令与脚本化方法,为后续的启动优化打下坚实基础。
4.1 U-Boot源码结构
U-Boot的源码采用模块化、层次化的组织方式,主要分为平台相关代码、通用驱动、命令处理、文件系统支持等部分。理解其目录结构是进行定制和优化的前提。
核心目录说明:
- arch/:存放与CPU架构相关的代码,如arm、riscv、x86等。每个架构下又细分board、cpu、include等子目录。
- board/:特定板级(Board)的初始化代码,包括内存初始化、引脚复用、时钟配置等。
- configs/:存放所有支持的板级默认配置文件(defconfig),是编译配置的入口。
- drivers/:各类设备驱动,如串口、网卡、MMC、NAND Flash、USB等。
- cmd/:U-Boot命令行命令的实现,如bootm、load、setenv等。
- common/:通用功能模块,如控制台、环境变量、HUSH shell等。
- dts/:设备树源文件(.dts)和绑定(bindings),用于描述硬件拓扑。
- include/:全局头文件,包括配置宏、数据结构定义等。
- net/:网络协议栈实现,支持TFTP、NFS、DHCP等。
- fs/:文件系统支持,如ext4、fat、ubifs等。
4.2 编译配置:defconfig 体系
U-Boot 采用 Kconfig 配置系统(与Linux内核类似),通过 make menuconfig 或直接编辑 defconfig 文件来定制功能。默认配置文件位于 configs/ 目录下,命名规则通常为 <board_name>_defconfig。
4.2.1 配置流程
- 选择默认配置:
make <board_name>_defconfig,例如make stm32mp157d_atk_defconfig。 - 自定义配置(可选):
make menuconfig进行图形化调整。 - 保存配置:
make savedefconfig生成精简的defconfig文件。 - 编译:
make生成u-boot.bin或u-boot.itb。
4.2.2 defconfig 文件示例
# configs/stm32mp157d_atk_defconfig
CONFIG_ARM=y
CONFIG_TARGET_STM32MP15X=y
CONFIG_DEFAULT_DEVICE_TREE="stm32mp157d-atk"
CONFIG_SYS_EXTRA_OPTIONS="RAM_BASE=0xC0000000"
CONFIG_BOOTCOMMAND="ext4load mmc 0:4 0xC2000000 /boot/Image; ext4load mmc 0:4 0xC4000000 /boot/stm32mp157d-atk.dtb; booti 0xC2000000 - 0xC4000000"
CONFIG_CMD_EXT4=y
CONFIG_CMD_FAT=y
CONFIG_CMD_NET=y
CONFIG_CMD_PING=y
CONFIG_NET_RANDOM_ETHADDR=y
# CONFIG_VIDEO is not set
优化提示: 在启动优化场景中,应通过
defconfig 禁用不必要的驱动和命令(如 CONFIG_CMD_USB、CONFIG_CMD_SOUND),以减小U-Boot体积并缩短加载时间。使用 make savedefconfig 可以自动生成最小化配置。
4.3 设备树(DTS)在U-Boot中的作用
设备树(Device Tree)是一种描述硬件拓扑结构的数据结构,在U-Boot中扮演着关键角色。U-Boot使用设备树来获取板级硬件信息,从而避免为每个板子编写硬编码的初始化代码。
4.3.1 设备树在U-Boot中的主要用途
| 用途 | 说明 |
|---|---|
| 硬件探测 | U-Boot在启动早期解析设备树,获取内存基址与大小、时钟频率、外设地址等。 |
| 驱动匹配 | U-Boot驱动模型(Driver Model)通过设备树中的 compatible 属性匹配对应的驱动。 |
| 内核传递 | U-Boot将设备树二进制(DTB)加载到内存,并在跳转内核时通过寄存器传递DTB地址。 |
| 配置覆盖 | 通过设备树覆盖(overlay)机制,可以在不修改主DTB的情况下调整硬件配置。 |
4.3.2 U-Boot中的设备树处理流程
4.4 常用命令与脚本化
U-Boot提供了一套丰富的命令行接口,用于调试、加载镜像、配置环境变量和启动系统。在启动优化中,脚本化是减少人工干预、实现自动化启动的关键手段。
4.4.1 核心命令分类
| 类别 | 命令 | 功能描述 |
|---|---|---|
| 存储操作 | mmc read / mmc write |
从/向 MMC/SD 卡读写数据 |
| 存储操作 | sf read / sf write |
从/向 SPI Flash 读写数据 |
| 网络操作 | tftp |
通过 TFTP 协议从服务器下载文件 |
| 网络操作 | dhcp |
通过 DHCP 获取 IP 地址 |
| 文件系统 | ext4load / fatload |
从 ext4/FAT 文件系统加载文件到内存 |
| 内存操作 | md / mw |
内存显示与写入 |
| 设备树 | fdt |
设备树操作(设置、查看、修改) |
| 启动 | booti / bootm |
启动内核镜像(booti 用于 AArch64,bootm 用于 uImage) |
| 环境变量 | setenv / saveenv |
设置/保存环境变量 |
4.4.2 脚本化启动示例
U-Boot支持HUSH shell脚本语法,可以将多个命令组合成自动执行序列。以下是一个典型的脚本化启动流程:
# 设置环境变量 bootcmd
setenv bootcmd 'echo "=== Starting Boot Sequence ==="; \
mmc dev 0; \
ext4load mmc 0:4 ${kernel_addr_r} /boot/Image; \
ext4load mmc 0:4 ${fdt_addr_r} /boot/board.dtb; \
fdt addr ${fdt_addr_r}; \
fdt set /chosen bootargs "console=ttySTM0,115200 root=/dev/mmcblk0p5 rootwait"; \
booti ${kernel_addr_r} - ${fdt_addr_r}'
# 保存环境变量
saveenv
# 执行启动
run bootcmd
避坑指南:
- 环境变量存储在固定的存储区域(如MMC的特定分区或SPI Flash),
saveenv操作会擦写存储介质,频繁操作可能缩短寿命。 - 脚本中的地址(如
${kernel_addr_r})需与defconfig中定义的CONFIG_SYS_LOAD_ADDR一致,否则可能导致加载失败。 - 在优化启动时间时,建议将
bootcmd中的echo和冗余延时命令移除,以缩短U-Boot执行时间。
4.4.3 启动时间测量命令
U-Boot提供了 bootstage 命令用于测量各阶段的耗时,是启动优化的利器。
# 启用 bootstage
setenv bootstage_report 1
saveenv
# 启动后查看时间报告
=> bootstage report
Timer summary in microseconds:
Elapsed(s) Stage
---------- -----
0.000 start
0.015 board_init_f
0.120 board_init_r
0.350 main_loop
0.450 bootm_start
0.520 bootm_load_os
0.680 start_kernel
优化实践: 通过
bootstage 报告,可以精准定位耗时瓶颈。例如,如果 board_init_f 阶段过长,应检查时钟初始化或DDR训练代码;如果 main_loop 阶段过长,应检查 bootdelay 是否设置过大或 bootcmd 中是否存在冗余命令。
4.5 本章小结
- U-Boot源码采用 arch/board/drivers/cmd 的模块化结构,便于移植和裁剪。
- defconfig 是编译配置的核心,通过
make menuconfig和make savedefconfig可以高效管理配置。 - 设备树(DTS/DTB) 在U-Boot中用于硬件描述、驱动匹配和内核传递,是硬件与软件解耦的关键。
- 常用命令 覆盖存储、网络、文件系统、内存和启动操作,脚本化 通过
bootcmd实现自动化启动。 - 使用
bootstage命令进行启动时间测量,是启动优化的第一步。