跳转到内容
新建笔记

U-Boot:启动链、构建与 Lichee Pi Nano 实例

U-Boot 是面向嵌入式设备的开放源代码引导程序。它初始化启动所需的硬件,提供命令行和镜像加载能力,并按平台约定把控制权交给 Linux 等后续软件。网络下载、存储读写和恢复系统也是它常见的开发用途。

本文以 U-Boot v2024.01 文档解释通用概念,以 Lichee Pi Nano 的 nano-v2018.01 分支保留历史构建实例。两个版本不能混用配置名、目录位置和依赖要求。具体命令见 U-Boot 命令参考,写入流程见 加载、校验与存储更新。

常见的启动顺序是:

SoC Boot ROM → 可选的 SPL / 平台固件 → U-Boot proper → 操作系统或其他负载

Boot ROM 是芯片内固化的第一阶段;SPL 是体积受限的早期加载程序,常负责初始化 DRAM 并加载完整 U-Boot。实际链路还可能包含 TPL、TF-A、OP-TEE 等组件,取决于 SoC。不能把所有板子的上电入口都理解成 U-Boot。

U-Boot 通常在没有通用操作系统支持的环境中运行,但它拥有自己的驱动、内存管理和命令解释器。启动内核成功后通常不返回;启动失败可能回到提示符,也可以执行复位、关机或其他负载。因此“唯一出口是内核”不是程序机制上的限制。

U-Boot 从 PPCBoot 等早期项目发展而来。选型时更有用的检查是:目标板是否有可维护的配置、所需介质和设备是否受支持、平台固件如何组合、失败后如何恢复。

官方发布目录 中,2024.01-rc6 表示第六个候选版,2024.01 表示正式版;.sig 是对应归档的分离签名。验证签名还需要从可信渠道获得维护者公钥并核对指纹,下载到一个 .sig 文件本身并不证明来源可信。

目录阅读目的
arch/处理器架构、SoC 初始化和架构相关启动代码;具体层级随版本变化
board/板级初始化和平台差异
configs/板级 *_defconfig,注意是复数目录名
cmd/命令实现、参数解析和返回值
drivers/各类设备驱动
common/、lib/通用启动逻辑和基础支持
disk/、fs/、net/分区、文件系统和网络协议
include/头文件和接口定义
doc/文档源文件
tools/主机侧镜像制作等工具,如 mkimage
post/、api/上电自检、外部程序接口等可选功能

遇到厂商命令时,先记录 version 和 help <命令>,再查看同一 BSP 提交中的实现。上游最新版文档不能替代目标固件的帮助信息。

主机编译器负责生成主机工具,交叉编译器负责生成目标板代码。Linux、Ubuntu 虚拟机或 WSL 都可用作构建环境,但最终网络桥接、USB 和存储访问能力与宿主配置有关。

工具链前缀用于选择工具,不足以完整描述生成代码的浮点行为。GCC 的 -mfloat-abi=soft 使用软件浮点调用;softfp 可生成硬件浮点指令但采用软浮点调用约定;hard 使用硬件浮点调用约定。还须匹配 CPU/FPU 和所链接库的 ABI。gnueabi 与 gnueabihf 名称是工具链默认 ABI 的线索,不能直接推断每一次编译是否执行硬件浮点指令。

解压工具链时可以让 tar 自动识别压缩格式:

终端窗口
# 文件名按实际下载包替换;历史 .tar.xz 包不能用仅针对 bzip2 的 -j。
tar -xf gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi.tar.xz
export PATH="$PWD/gcc-linaro-7.2.1-2017.11-x86_64_arm-linux-gnueabi/bin:$PATH"
arm-linux-gnueabi-gcc --version

这里的 Linaro 7.2 是历史环境记录,不是当前所有 U-Boot 的推荐版本。优先使用所选 BSP 已验证的工具链。依赖按该版本构建文档安装;常见项目包括主机 C/C++ 编译器、make、Bison、Flex、SSL 开发头文件,交互配置还可能需要 ncurses。不要为一份 64 位工具链无条件安装整套旧 Android/32 位依赖。

一般构建流程如下,BOARD_defconfig 需替换为仓库中实际存在的板级配置:

终端窗口
make O=build CROSS_COMPILE=arm-linux-gnueabi- BOARD_defconfig
make O=build CROSS_COMPILE=arm-linux-gnueabi- menuconfig
make O=build CROSS_COMPILE=arm-linux-gnueabi- -j4

每次调用保持相同的 O=build 和工具链前缀。用命令行或环境变量传入 CROSS_COMPILE,不依赖 Makefile 的固定行号。menuconfig 配置的是 U-Boot,自身的 Kconfig 依赖也会参与选择;不要套用 Linux“选择 M 后生成可动态加载内核模块”的说明。需要保留配置变更时,再按项目约定生成、审查 defconfig。

4. Lichee Pi Nano 历史实例

跳转到“4. Lichee Pi Nano 历史实例”

下面的仓库分支含有 F1C100S/Nano 配置;它不是任意 F1C200S 产品板的通用配置。

终端窗口
git clone --branch nano-v2018.01 --single-branch https://github.com/Lichee-Pi/u-boot.git
cd u-boot
git rev-parse HEAD
make O=build CROSS_COMPILE=arm-linux-gnueabi- licheepi_nano_defconfig
make O=build CROSS_COMPILE=arm-linux-gnueabi- -j4

记录 git rev-parse HEAD 的结果才能复现一次构建。分支中另外有 licheepi_nano_spiflash_defconfig,增加了 SPI 启动和 SPI 环境存储等配置;两者应按实际介质选择。常见输出是 build/u-boot-sunxi-with-spl.bin,包含 SPL 与后续 U-Boot。本文核对了这些配置文件,但没有声称用旧工具链完成该历史分支的实际板上启动。

mmc 0:1 指 U-Boot 枚举的 MMC 设备 0 的第 1 个磁盘分区。介质可能是 SD 卡或 eMMC,不能仅凭 mmc 命令名判定为 eMMC。U-Boot 的设备编号也不保证与 Linux /dev/mmcblk0 一一对应。

对于使用 bootz 启动 ARM zImage 的配置,可以将启动流程组织为条件嵌套。以下地址是原 Nano 示例的历史值;执行前必须按本板 RAM 容量、内核解压范围、U-Boot 重定位区和 DTB 大小验证不重叠。

setenv kernel_addr_r 0x80008000
setenv fdt_addr_r 0x80c08000
if load mmc 0:1 ${kernel_addr_r} zImage; then
if load mmc 0:1 ${fdt_addr_r} suniv-f1c100s-licheepi-nano.dtb; then
bootz ${kernel_addr_r} - ${fdt_addr_r}
else
echo DTB load failed
fi
else
echo Kernel load failed
fi

这个写法要求目标启用相应命令和 hush 条件语法。bootz 中 - 表示未提供 initrd。不要用连续分号把加载失败与启动命令无条件串起来。调通后再决定是否把流程放入 bootcmd 和保存环境。

Linux 启动参数可先在 RAM 环境中设置:

setenv bootargs 'console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 rw'
参数准确含义和检查点
console=ttyS0,115200选择 Linux 控制台,串口名必须与本内核驱动匹配
panic=5内核 panic 后等待约 5 秒再重启
rootwait等待根设备出现;不会补齐缺失的控制器或文件系统驱动
root=/dev/mmcblk0p2Linux 的根块设备;稳定部署可按平台改用受支持的 PARTUUID=
rw请求根文件系统以可读写方式挂载,仍受文件系统状态和启动脚本影响
earlyprintk / earlycon早期控制台选项,架构、内核配置和具体参数均有版本差异

传参遵循目标架构的启动协议。在常见 DT 启动中,U-Boot 会处理 /chosen/bootargs 等设备树内容;不是所有平台都把字符串放在一个固定的物理地址。

Allwinner 的传统 SD 启动布局使用从介质起点算起的 8 KiB 偏移(512 字节扇区 16),原因是 Boot ROM 搜索启动头的规则。某些更新 SoC 还有 128 KiB 布局,不能直接套用到 Nano。8 KiB 处可能与 GPT 分区表项重叠,必须按目标板支持的分区布局处理。

先在普通文件中理解 dd 的位置语义:

终端窗口
# 新建练习目录中的镜像文件;该文件仅用于布局练习,不是完整可启动系统。
truncate -s 64M nano-layout.img
dd if=build/u-boot-sunxi-with-spl.bin of=nano-layout.img bs=1024 seek=8 conv=notrunc

seek=8 跳过 8 个输出块,因此写入偏移为 8 × 1024 = 8192 字节;conv=notrunc 避免截短已有镜像。真实写卡前,要核对整盘设备的型号、容量和序列号,卸载其分区,备份分区表与数据,并确认 [8192, 8192 + 镜像长度) 全部属于预留引导区域。输出应指向已经确认的整盘设备,不能按某台电脑曾经使用过的 /dev/sdb 猜测。写入后还需刷新、读回比较并实际断电启动;这些步骤不能由“命令没有报错”代替。