阅读 U-Boot 启动日志
本章把启动日志当作一条时间线:从版本横幅、内存与设备初始化,一直读到自动启动和 => 提示符。你将学会识别关键里程碑,并用“最后成功阶段”快速缩小故障范围。
本教程以 Mainline U-Boot v2026.07、
qemu_arm64_defconfig和 QEMU ARM64virt为基准。日志会随版本、配置和 QEMU 参数变化,文中的省略号和设备数量仅用于讲解,不是逐字匹配的标准答案。
学习目标
阅读本章后,你将能够:
- 按启动阶段拆分 U-Boot 日志,并解释版本、DRAM、Environment 与 Console 信息
- 理解自动启动倒计时和 Standard Boot 扫描输出
- 区分 U-Boot 日志与 Linux Kernel 日志,判断 Warning 是否致命
- 记录最后一个成功里程碑,并用只读命令验证假设
- 保存、筛选和对比两 次 QEMU 启动日志
前置知识
请先完成运行 U-Boot与命令行与常用命令。完整启动链(Boot ROM、SPL、TF-A 等)见从上电到 Linux 的启动过程;本章是第二部分收尾,重点解读 QEMU 上 U-Boot proper 的串口日志。
1. 启动日志是一条事件时间线
U-Boot 在初始化和启动操作系统的过程中,会把关键状态写到 Console。对于本教程的 QEMU 实验,可以把日志大致分为以下阶段:
日志最重要的价值不是告诉你“出现了多少行”,而是回答三个问题:
- 系统已经成功执行到哪个阶段?
- 哪一行开始偏离正常日志?
- 这次偏离是否阻止了后续阶段继续执行?
如果只搜索 error 并从最后一个匹配结果开始排查,很容易被无关信息误导。
2. 先保存一份完整日志
排查启动问题时,不要只依赖终端滚动缓冲区或手机拍照。文本日志便于搜索、标注、比较,也适合 作为后续实验的正常基线。
2.1 记录一次自动启动
先在 Host Shell 中创建日志目录:
# [Host]
mkdir -p "$HOME/uboot-lab/logs"
使用 Ubuntu 的 script 命令启动上一章创建的脚本:
# [Host]
script \
--quiet \
--flush \
--command "$HOME/uboot-lab/scripts/run-uboot.sh" \
"$HOME/uboot-lab/logs/qemu-uboot-autoboot.log"
这次不要按键中断倒计时。等待 U-Boot 完成自动启动扫描并回到 =>,再使用 Ctrl+A、松开后按 x 退出 QEMU。
script 会把终端会话写入文件,同时保留交互式终端。--flush 会及时刷新输出,即使 QEMU 意外结束,也更容易保留此前的日志。
2.2 再记录一次中断启动
重新执行:
# [Host]
script \
--quiet \
--flush \
--command "$HOME/uboot-lab/scripts/run-uboot.sh" \
"$HOME/uboot-lab/logs/qemu-uboot-interrupted.log"
这次在 Hit any key to stop autoboot 出现时按空格键。看到 => 后退出 QEMU。现在你拥有两种启动路径的基线:
| 日志 | 启动路径 |
|---|---|
qemu-uboot-autoboot.log | 倒计时结束,执行默认 bootcmd |
qemu-uboot-interrupted.log | 用户中断倒计时,直接进入命令行 |
倒计时通常使用回车符反复更新同一行,所以日志中可能带有 ^M 等控制字符。这是终端记录的正常现象,不是 U-Boot 输出损坏。
2.3 快速筛选关键行
先完整阅读一次日志,再用 rg 定位关键里程碑:
# [Host]
rg -a -ni \
'U-Boot|DRAM|Core:|Flash:|Environment|In:|Out:|Err:|Net:|autoboot|bootflow|Starting kernel|Booting Linux|warning|error' \
"$HOME/uboot-lab/logs/qemu-uboot-autoboot.log"
其中:
-a把带有控制字符的日志按文本处理-n显示行号-i忽略大小写
筛选结果只适合导航,不能替代上下文。找到可疑行后,仍要回到完整日志查看它前后的内容。若系统 没有 rg,可用 grep -a -ni ... 达到类似效果。
3. 一份用于讲解的启动日志
基准实验可能显示与下面相似的内容:
U-Boot 2026.07 (...)
DRAM: 512 MiB
Core: ... devices, ... uclasses, devicetree: board
Flash: 64 MiB
Loading Environment from Flash...
*** Warning - bad CRC, using default environment
In: serial@9000000
Out: serial@9000000
Err: serial@9000000
Net: ...
Hit any key to stop autoboot: 0
Scanning for bootflows in all bootdevs
...
=>
这是一份经过简化的教学示例。实际输出中的构建时间、Device 数量、Environment 提示、Network Device 和 Bootflow 列表都可能不同。
接下来按照出现顺序阅读。
4. 版本横幅:确认正在运行什么
日志第一行通常类似:
U-Boot 2026.07 (...)
它可能包含:
- U-Boot Release Version
- Git Commit 或提交数量
- Build Date
- Build Host
- Compiler Version
先确认版本,是因为不同版本的命令、默认配置和日志文字可能变化。如果文件名写着 v2026.07,但横幅显示另一个版本,说明 QEMU 实际加载的镜像不是你以为的那个。
开发版本中还可能出现类似:
U-Boot 2026.07-00012-g123456789abc-dirty
常见字段含义如下:
| 字段 | 含义 |
|---|---|
00012 | 当前版本标签之后还有 12 个提交 |
g123456789abc | 构建所用 Git Commit 的缩写 |
dirty | 构建时源码工作区存在未提交修改 |
dirty 本身不是启动错误,但复现实验时必须记录,因为两个名称相近的镜像可能实际包含不同代码。
进入命令行后,可以再次确认:
# [U-Boot]
=> version
5. DRAM、Core 与 Flash:硬件初始化概况
5.1 DRAM
DRAM: 512 MiB
它表示 U-Boot 识别到的 Guest RAM,不是 Host 的物理内存总量。在 QEMU ARM virt 中,RAM 容量由 -m 512M 创建,并通过 QEMU 生成的 Device Tree 描述给 U-Boot。
如果这里不是 512 MiB,可以在命令行交叉检查:
# [U-Boot]
=> bdinfo
重点查看 DRAM bank 的 start 和 size。地址和容量换算见内存、地址与数据操作。
5.2 Core
Core: ... devices, ... uclasses, devicetree: board
这里的 Device 和 Uclass 来自 U-Boot Driver Model:
- Device 是已经绑定到 Driver Model 的软件设备实例
- Uclass 按功能组织设备,例如 Serial、Block、Network
- 数量会随 U-Boot 配置、版本和 QEMU 外设变化
它们不是开发板上物理芯片的数量,不应作为跨版本测试的固定值。
devicetree: board 表示 U-Boot 当前 Control Device Tree 的来源类别。它不表示 Linux 已经启动,也不是传给 Linux 的 Device Tree 文件名。可以使用以下命令继续检查:
# [U-Boot]
=> bdinfo
=> dm tree
dm tree 显示的是 U-Boot Driver Model 中的 Device。Control FDT 与传给 Linux 的 OS FDT 不要混淆,见U-Boot 的架构与组成。
5.3 Flash
Flash: 64 MiB
它表示 U-Boot 识别到的模拟 Flash 容量,不是 u-boot.bin 文件大小。QEMU 把 U-Boot 作为固件放入模拟 Flash;Flash Address Range 中没有被镜像文件占用的空间,仍属于模拟设备的一部分。
6. Environment:先判断是否影响继续启动
日志可能出现:
Loading Environment from Flash...
*** Warning - bad CRC, using default environment
这两行应该连起来理解:
- U-Boot 尝试从 Flash Backend 读取持久化环境
- 读取到的数据未通过 CRC 校验
- U-Boot 改用编译进镜像的默认环境继续运行
在当前 QEMU 实验中,我们没有连接单独的 envstore.img。第一次启动时看到这条 Warning 并不意外,也不等于 u-boot.bin 已损坏。只要后续继续出现 In、Out、倒计时和 =>,它就不是这次启动的致命错误。
环境变量要始终区分三个层次:
| 层次 | 含义 |
|---|---|
| 默认环境 | 构建时放入 U-Boot 的初始值 |
| 当前环境 | 本次运行期间位于 RAM 中的工作副本 |
| 持久化环境 | Flash、MMC 等 Backend 中可跨复位保存的数据 |
如果真实设备过去能够加载持久化环境,某次升级后突然出现 bad CRC,则应继续检查 Environment Offset、Size、Redundancy 和 Storage Layout 是否变化。
不要在未确认存储范围前执行 env save / saveenv,否则可能覆盖仍有价值的数据。当前最小 QEMU 命令也没有可写环境镜像;持久化方案见环境变量与启动脚本。
7. In、Out、Err:Console 路由
In: serial@9000000
Out: serial@9000000
Err: serial@9000000
三行分别表示 U-Boot 的标准输入、标准输出和标准错误连接到哪个 Console Device。
| 字段 | 主要方向 | 典型用途 |
|---|---|---|
In | 终端到 U-Boot | 接收按键和命令 |
Out | U-Boot 到终端 | 普通输出 |
Err | U-Boot 到终端 | 错误输出 |
serial@9000000 是 QEMU Device Tree 中 PL011 Serial Node 的名称。它不是 Linux 启动后的 /dev/ttyAMA0;此时 Linux 还没有运行。
如果能看到输出但不能输入,应优先检查 In、Terminal 设置和串口连接。进入命令行后可以使用:
# [U-Boot]
=> coninfo
真实开发板还要检查 Baud Rate、Data Bits、Parity、Stop Bits 和 Flow Control。基准实验的 Baud Rate 通常为 115200。
8. Net:设备是否被发现
日志可能列出一个或多个 Ethernet Device,也可能表示没有可用网卡:
Net: ...
本教程当前的最小 QEMU 启动脚本没有添加虚拟网卡,因此没有 Ethernet Device 是合理结果。它不影响进入命令行,也不影响以后从虚拟磁盘启动 Linux。
看到 Network Device 只表示 U-Boot 已经识别它,不代表 DHCP、TFTP 或 Internet 已经可用。网络启动还需要 QEMU Network Backend、IP 配置和 Server,见后续通过网络启动 Linux。
9. 自动启动倒计时:决定下一条路径
Hit any key to stop autoboot: 0
倒计时由环境变量 bootdelay 控制(常见为正整数,如 2;以 env print 为准)。倒计时结束后,U-Boot 执行 bootcmd;在结束前按键,则中断自动启动并进入命令行。
可以在 => 后查看:
# [U-Boot]
=> env print bootdelay bootcmd
常见取值包括:
bootdelay | 行为 |
|---|---|
| 正整数 | 等待指定秒数,然后执行 bootcmd |
0 | 不延时,但通常仍检查中断按键 |
-1 | 禁用自动启动 |
-2 | 不延时,并且不检查中断 按键 |
不要为了方便排查,就在不熟悉的设备上把 bootdelay 保存为 -2。一旦启动脚本出错,你可能失去进入命令行的常规窗口。当前 QEMU 实验即使临时 env set,也不要执行 env save。
10. Standard Boot 扫描
基准配置的默认 bootcmd 通常会触发类似扫描(以 env print bootcmd 的实际内容为准):
Scanning for bootflows in all bootdevs
Standard Boot 使用三个核心概念:
| 概念 | 要回答的问题 | 示例 |
|---|---|---|
| Bootdev | 从哪里寻找系统? | MMC、NVMe、USB、Ethernet |
| Bootmeth | 用什么规则寻找? | extlinux、EFI、Script |
| Bootflow | 找到的一套可执行启动方案是什么? | 某分区上的 extlinux.conf |
排查时可手动执行 bootflow scan。常见选项:
-l:列出扫描过程中的结果-a:显示更多细节(常与-l连用,如bootflow scan -la)-b:对有效 Bootflow 尝试启动(如bootflow scan -lb)
如果列表中出现 ready,表示对应 Bootflow 已经准备好启动。如果只看到扫描信息、没有有效结果,然后返回 =>,通常表示所有候选项都已用尽。
当前最小 QEMU 命令没有连接磁盘、USB Storage 或 Network Device,因此“没有找到可启动内容”正是预期结果。它说明 U-Boot 的自动启动流程运行了,但实验环境尚未提供 Linux Payload。
还要注意:Standard Boot 会依次尝试多个 Bootdev 和 Bootmeth。某个候选项输出 Error,不代表整个启动流程已经失败;后面的候选项仍可能成功。判断结论时要看扫描是否继续、是否找到 ready 状态,以及最终走向哪里。
Standard Boot 的完整配置、扫描顺序和 extlinux.conf 见Standard Boot 启动框架。本章只需要把它识别为“寻找可启动方案”的阶段。
11. => 到底说明了什么
日志最后出现:
=>
可以确认:
- U-Boot 已完成进入 Main Loop 所需的初始化
- Console 可以输出提示符
- 当前正在等待 U-Boot Command
但它不能证明:
- 所有硬件都初始化成功
- 存储设备和网络都可用
- 已经找到 Kernel、Device Tree 或 Root Filesystem
- Linux 已经启动
在本章的自动启动实验中,=> 通常意味着启动扫描结束但没有成功转交控制权;在中断实验中,则意味着用户主动阻止了 bootcmd 执行。
12. U-Boot 日志和 Linux 日志的分界
U-Boot 成功加载 Kernel、Device Tree 和可选的 Initramfs 后,会执行相应的启动命令。转交前可能出现:
Starting kernel ...
随后出现的内容才属于 Linux,例如:
Booting Linux on physical CPU ...
[ 0.000000] Linux version ...
具体分界文字取决于 Architecture、Image Format 和 EFI 等启动路径,不能只依赖某一条固定字符串。更可靠的判断是:
=>、bootflow、Loading Environment属于 U-Boot- 带 Kernel Timestamp 的
[ 0.000000]日志属于 Linux Kernel panic说明控制权已经进入 Linux
U-Boot 正 常把控制权交给 Kernel 后,通常不会再返回命令行。因此:
Starting kernel ...后完全无输出:优先检查 Linux Console 参数、Device Tree 和 Kernel- 已出现 Linux 日志后发生 Panic:优先检查 Kernel、Root Filesystem 和
bootargs
这两个故障都发生在启动后段,但责任边界不同。更完整的阶段划分与真板启动链对照,见从上电到 Linux 的启动过程。
13. Warning、Error 和致命故障
日志中的单词不能脱离上下文解释。可以按“是否继续到达后续里程碑”进行初步分类:
| 现象 | 初步判断 |
|---|---|
| Warning 后继续出现 Console、倒计时和提示符 | 通常可以继续分析,不是当前致命点 |
| 某个 Bootflow Candidate 失败后继续扫描 | 局部失败,整体尚未结束 |
| 同一阶段反复报错,始终到不了下一里程碑 | 很可能是当前阻塞点 |
| 日志突然停止,且没有提示符或后续阶段 | 检查停止前最后一个成功阶段 |
| 复位、异常或 Hang 每次都发生在同一位置 | 优先调查该阶段及其输入条件 |
例如,bad CRC, using default environment 已经说明存在备用路径;如果默认环境能够继续启动,就不能把它当作唯一根因。相反,如果 DRAM 初始化前日志停止,后面的 Environment 和 Bootflow 根本还没有机会执行。