3、U-Boot命令行基础:进入U-Boot命令行、常用命令分类、命令帮助系统
U-Boot启动后,如果你看到那个熟悉的 => 提示符,恭喜,你已经进入了U-Boot的命令行世界。很多刚接触嵌入式Linux的工程师,总觉得U-Boot就是个“黑盒子”,其实它更像一把瑞士军刀——功能多,但得知道怎么用。
我个人习惯把U-Boot命令行比作“裸机时代的救生员”。系统起不来?内核挂掉?文件系统损坏?只要串口还能输出,U-Boot命令行就是你排查问题的第一站。这一章,我们就把它的基本操作摸透。
3.1 如何进入U-Boot命令行
大多数开发板在启动时,串口终端会输出类似这样的信息:
U-Boot 2023.04 (Jan 15 2024 - 14:30:00 +0800)
CPU: i.MX6ULL
Board: MY-IMX6ULL
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Net: FEC [PRIME]
Hit any key to stop autoboot: 3
看到 Hit any key to stop autoboot 这行字了吗?在倒计时结束前,随便按键盘上的一个键(空格、回车都行),就能进入命令行。倒计时一般是3秒,有些板子设成1秒,手速要快。
如果倒计时已经结束,系统已经开始加载内核了怎么办?别急,按住复位键重启,或者重新上电,再来一次。实在不行,检查一下 bootdelay 环境变量是不是被改成了0。
3.2 常用命令分类
U-Boot的命令少说也有上百个,但常用的其实就那几十个。我按功能把它们分成四类,你记牢这四类,基本就能应付90%的调试场景。
3.2.1 信息查询类
这类命令用来“看”,看硬件状态、看环境变量、看内存布局。调试的第一步,永远是先搞清楚当前系统是什么状态。
| 命令 | 功能 | 典型用法 |
|---|---|---|
bdinfo |
显示板级信息(DRAM起始地址、大小、时钟等) | bdinfo |
printenv |
打印所有环境变量 | printenv 或 printenv bootargs |
version |
显示U-Boot版本和编译时间 | version |
iminfo |
检查镜像文件头(校验CRC) | iminfo 0x80800000 |
flinfo |
显示Flash信息(NOR/NAND) | flinfo |
举个例子,你拿到一块新板子,第一件事就是敲 bdinfo,看看DRAM起始地址是不是0x80000000,大小是不是512MB。如果这里就不对,后面所有操作都是白费。
printenv 把环境变量全部保存到终端日志里。这样万一改乱了,还能对照着恢复。别问我为什么知道——有一次我手滑把 bootcmd 清空了,系统直接变砖,还好有备份。
3.2.2 内存操作类
这类命令是调试的“手术刀”。内存读写、查看、修改、比对,全指望它们。
| 命令 | 功能 | 典型用法 |
|---|---|---|
md |
显示内存内容(Memory Display) | md.b 0x80000000 0x100(字节显示) |
mw |
填充内存(Memory Write) | mw.l 0x80000000 0xdeadbeef 0x100 |
mm |
交互式修改内存 | mm.b 0x80000000 |
cmp |
比较两段内存 | cmp.b 0x80000000 0x81000000 0x1000 |
cp |
拷贝内存 | cp.b 0x80000000 0x81000000 0x1000 |
注意 md 和 mw 后面的 .b、.w、.l 后缀,分别代表字节(8位)、半字(16位)、字(32位)操作。我见过不少新手用 md.l 查看寄存器,结果读出来的值完全不对——因为寄存器是32位的,但地址偏移是按字节算的,用 .l 一次读4字节,地址会自动加4。
md.l 0x020E0000 1 读一下,如果返回0xFFFFFFFF,说明要么地址没映射,要么外设没使能。
3.2.3 网络操作类
网络是U-Boot里最实用的功能之一。通过网络下载内核、文件系统,比从Flash烧写快得多。
| 命令 | 功能 | 典型用法 |
|---|---|---|
ping |
测试网络连通性 | ping 192.168.1.100 |
tftp |
通过TFTP下载文件 | tftp 0x80800000 zImage |
dhcp |
通过DHCP获取IP并下载 | dhcp 0x80800000 zImage |
nfs |
通过NFS挂载根文件系统 | nfs 0x80800000 192.168.1.100:/nfs/zImage |
使用网络命令前,必须先设置好环境变量:ipaddr(板子IP)、serverip(服务器IP)、netmask(子网掩码)。我习惯在 bootcmd 里加一段自动检测网络的逻辑,如果ping不通服务器,就回退到本地启动。
ethaddr 环境变量,就是板子的MAC地址。如果多块板子共用一个镜像,记得改掉这个变量,否则网络会随机掉线。我曾经在实验室里被这个问题折磨了一下午,最后发现三块板子MAC地址一模一样。
3.2.4 存储操作类
存储操作涉及Flash、MMC、SD卡、USB等设备。这类命令通常以设备名开头,比如 mmc、sf(SPI Flash)、nand。
| 命令 | 功能 | 典型用法 |
|---|---|---|
mmc info |
显示MMC/SD卡信息 | mmc info |
mmc read |
从MMC读取数据到内存 | mmc read 0x80800000 0x800 0x1000 |
mmc write |
从内存写入数据到MMC | mmc write 0x80800000 0x800 0x1000 |
sf probe |
探测SPI Flash | sf probe 0:0 |
sf read |
从SPI Flash读取数据 | sf read 0x80800000 0x100000 0x800000 |
sf erase |
擦除SPI Flash扇区 | sf erase 0x100000 0x800000 |
sf write |
写入数据到SPI Flash | sf write 0x80800000 0x100000 0x800000 |
fatls |
列出FAT分区文件 | fatls mmc 0:1 |
ext4ls |
列出ext4分区文件 | ext4ls mmc 0:2 |
存储操作的关键是理解“块地址”和“字节地址”的区别。比如 mmc read 的第二个参数是块号(LBA),第三个参数是块数,一块通常是512字节。而 sf read 的第二个参数是字节偏移,第三个参数是字节长度。搞混了,读出来的数据全是乱的。
sf write 写了一个错误的偏移地址,结果把启动引导区覆盖了,板子直接变砖。后来我学乖了:写Flash之前,先用 sf read 把原内容读到内存里备份一份,万一写错了还能恢复。
3.3 命令帮助系统
U-Boot的命令太多,记不住怎么办?别担心,它自带帮助系统。
help或?:列出所有可用命令。help 命令名:查看某个命令的详细用法。
比如你忘了 tftp 的用法,直接敲:
=> help tftp
tftp [loadAddress] [[hostIPaddr:]bootfilename]
输出会告诉你:第一个参数是加载地址,第二个参数是服务器上的文件名,还可以指定服务器IP。
我个人觉得,U-Boot的帮助系统虽然简陋,但足够用了。它不会像Linux的man手册那样长篇大论,而是直接告诉你参数格式和示例。如果你需要更详细的信息,可以查看U-Boot源码里的 doc/ 目录,或者直接读命令的C文件——很多命令的实现代码也就几百行,读起来并不难。
help 命令名,90%的情况下能解决问题。剩下的10%,要么是命令拼写错了,要么是板子没编译进那个命令。
3.4 本章知识体系
下面这张图总结了U-Boot命令行的核心知识结构,你可以把它当作一张“地图”,随时回来对照。
这张图把U-Boot命令行分成了五个模块:信息查询、内存操作、网络操作、存储操作,以及贯穿始终的命令帮助系统。每个模块下都有对应的核心命令和典型用途。调试时遇到问题,先定位属于哪一类,再去找对应的命令,效率会高很多。
help、printenv、md、tftp、mmc read 这五个命令练熟,剩下的遇到再查。我当年就是这么过来的,到现在也只用到了U-Boot命令集的20%,但这20%已经能解决80%的问题了。