# U-Boot 中的 bootcmd 和 bootargs 详解
在嵌入式 Linux 开发中,尤其是内核、设备树和驱动开发过程中,经常会接触到 **`bootcmd`** 和 **`bootargs`**。它们都不是 Linux shell 命令,而是 **U-Boot 的环境变量**。
很多初学者容易把它们混淆,其实它们分别解决的是两个不同的问题:
– **`bootcmd`**:U-Boot 如何启动 Linux
– **`bootargs`**:Linux 内核启动时携带哪些参数
—
## 1. bootcmd 是什么
`bootcmd` 表示:
> **U-Boot 倒计时结束后默认自动执行的启动命令。**
它的作用是决定:
– 从哪里加载内核镜像
– 从哪里加载设备树文件 `dtb`
– 是否加载 `initrd/initramfs`
– 最终调用 `bootz`、`bootm` 还是 `booti` 启动内核
可以把它简单理解为:
> **“U-Boot 怎样把 Linux 启动起来”**
—
## 2. bootargs 是什么
`bootargs` 表示:
> **传递给 Linux 内核的启动参数。**
它的作用是决定内核启动后的运行方式,例如:
– 串口控制台是谁
– 根文件系统在哪里
– 根文件系统是只读还是读写
– 是否启用早期调试输出
– 网络参数如何设置
– 是否通过 NFS 启动
– 某些内建驱动的启动参数
可以把它理解为:
> **“Linux 内核启动时带哪些参数运行”**
—
## 3. bootcmd 和 bootargs 的关系
系统启动流程大致是:
“`text
上电
-> BootROM
-> U-Boot
-> 执行 bootcmd
-> bootcmd 加载内核和设备树
-> U-Boot 将 bootargs 传给 Linux 内核
-> Linux 内核启动
-> 驱动初始化/probe
“`
所以两者关系很清晰:
– **`bootcmd`**:决定“如何启动内核”
– **`bootargs`**:决定“内核启动后按什么参数运行”
—
## 4. 在驱动开发中为什么重要
### 4.1 bootcmd 很重要
在驱动开发里,`bootcmd` 决定:
– 你加载的是不是最新编译的 `zImage`
– 你加载的是不是最新修改过的 `.dtb`
– 加载地址是否正确
– 启动方式是否正确
很多时候你以为驱动代码没生效,其实不是驱动有问题,而是 **U-Boot 启动的根本不是你刚更新的内核或设备树**。
### 4.2 bootargs 很重要
在驱动开发里,`bootargs` 直接影响:
– 串口日志能不能看到
– 根文件系统能不能挂载成功
– 网络根文件系统能不能启动
– 调试参数是否打开
– 是否能看到更早期的启动日志
常见调试参数例如:
“`bash
console=ttymxc0,115200
loglevel=8
ignore_loglevel
initcall_debug
earlycon
“`
这些对调试驱动初始化、probe 顺序、卡死位置等都很有帮助。
—
## 5. 你的参数示例
你当前给出的参数是:
“`bash
bootargs=console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.100:/home/eh/linux/nfs/xuexi/rootfs,proto=tcp rw ip=192.168.10.50:192.168.10.100:192.168.10.1:255.255.255.0::eth0:off
bootcmd=tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 – 83000000
“`
这是一种非常典型的开发模式:
> **通过 TFTP 下载内核和设备树,通过 NFS 挂载根文件系统。**
也就是:
> **TFTP 启动内核 + NFS 根文件系统启动**
—
## 6. 你的 bootcmd 逐项解释
“`bash
bootcmd=tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 – 83000000
“`
它可以拆成 3 步。
### 6.1 `tftp 80800000 zImage`
意思是:
– 通过 **TFTP** 从服务器下载 `zImage`
– 下载到内存地址 **0x80800000**
这里的 `zImage` 就是 Linux 内核镜像。
### 6.2 `tftp 83000000 imx6ull-alientek-emmc.dtb`
意思是:
– 通过 **TFTP** 从服务器下载设备树文件 `imx6ull-alientek-emmc.dtb`
– 下载到内存地址 **0x83000000**
这个 `.dtb` 文件描述开发板硬件信息。
### 6.3 `bootz 80800000 – 83000000`
意思是:
– 使用 `bootz` 启动 **zImage 格式** 的内核
– 内核地址是 **0x80800000**
– 中间的 `-` 表示 **没有 initrd/initramfs**
– 设备树地址是 **0x83000000**
### 6.4 整个 bootcmd 的含义
把三步连起来就是:
> **U-Boot 先通过 TFTP 下载内核和设备树到内存,然后启动 Linux。**
—
## 7. 你的 bootargs 逐项解释
“`bash
bootargs=console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.100:/home/eh/linux/nfs/xuexi/rootfs,proto=tcp rw ip=192.168.10.50:192.168.10.100:192.168.10.1:255.255.255.0::eth0:off
“`
它是传给 Linux 内核的启动参数。
### 7.1 `console=ttymxc0,115200`
意思是:
– Linux 控制台输出到 `ttymxc0`
– 串口波特率为 `115200`
作用:
– 你在串口终端上看到的内核启动日志主要依赖它
– 驱动里的 `printk` 输出通常也通过这里查看
### 7.2 `root=/dev/nfs`
意思是:
– 根文件系统不是本地存储设备
– 而是通过 **NFS 网络文件系统** 挂载
这表示 Linux 启动后会把网络服务器上的某个目录作为根目录 `/` 使用。
### 7.3 `nfsroot=192.168.10.100:/home/eh/linux/nfs/xuexi/rootfs,proto=tcp`
意思是:
– NFS 服务器地址:`192.168.10.100`
– 根文件系统目录:`/home/eh/linux/nfs/xuexi/rootfs`
– 挂载 NFS 时使用 `TCP` 协议
也就是说,Linux 的根文件系统来自:
“`bash
192.168.10.100:/home/eh/linux/nfs/xuexi/rootfs
“`
### 7.4 `rw`
意思是:
– 根文件系统以 **读写方式** 挂载
如果写成 `ro`,则表示只读挂载。
### 7.5 `ip=192.168.10.50:192.168.10.100:192.168.10.1:255.255.255.0::eth0:off`
这是给 Linux 内核设置网络参数的。
格式大致是:
“`bash
ip=<本机IP>:<服务器IP>:<网关IP>:<子网掩码>:<主机名>:<网卡名>:<自动配置方式>
“`
你的参数展开后含义如下:
– **本机 IP**:`192.168.10.50`
– **服务器 IP**:`192.168.10.100`
– **网关 IP**:`192.168.10.1`
– **子网掩码**:`255.255.255.0`
– **主机名**:空
– **网卡名**:`eth0`
– **自动配置方式**:`off`
其中 `off` 表示:
– 不使用 DHCP
– 不使用 BOOTP
– 不使用 RARP
– 直接采用写死的静态 IP 配置
所以这是一种:
> **静态 IP + NFS 挂载根文件系统** 的启动方式。
—
## 8. 把你的参数整体连起来理解
你的系统启动流程实际上是这样的。
### 在 U-Boot 阶段
1. 通过 TFTP 下载 `zImage`
2. 通过 TFTP 下载 `imx6ull-alientek-emmc.dtb`
3. 用 `bootz` 启动内核
### 在 Linux 内核阶段
1. 从 `ttymxc0` 串口输出启动日志
2. 使用静态 IP:`192.168.10.50`
3. 与服务器 `192.168.10.100` 通信
4. 通过 NFS 挂载服务器目录 `/home/eh/linux/nfs/xuexi/rootfs`
5. 把这个目录作为 Linux 根文件系统 `/`
所以一句话总结你的配置就是:
> **通过 TFTP 启动内核和设备树,通过 NFS 挂载根文件系统。**
### 8.1 为什么内核和设备树常用 TFTP,而根文件系统常用 NFS
这不是“必须这样”,而是因为 **TFTP 和 NFS 更适合不同阶段的任务**。
#### 1)为什么启动阶段常用 TFTP
U-Boot 处在系统刚上电后的早期阶段,这时它的能力比较有限,通常只有一个比较简化的网络协议栈。
TFTP 的特点是:
– 协议简单
– 实现开销小
– 很适合按文件名下载少量文件
– 非常适合传输 `zImage`、`uImage`、`.dtb` 这类单个启动文件
所以在 bootloader 阶段,TFTP 非常适合用来做:
– 下载内核镜像
– 下载设备树
– 有时也下载 initramfs
简单说就是:
> **U-Boot 阶段更需要“把几个启动文件快速搬到内存里”,TFTP 正适合做这件事。**
#### 2)为什么根文件系统常用 NFS
根文件系统不是一个单独文件,而是一整套目录树,里面包含:
– `/bin`
– `/sbin`
– `/lib`
– `/etc`
– `/dev`
– `/usr`
– 各种配置、脚本、库文件、可执行程序
Linux 启动后,需要把这些内容当成一个真正的文件系统来访问,要求支持:
– 目录层级
– 按路径查找文件
– 权限管理
– 符号链接
– 动态库加载
– 按需读写文件
而 NFS 的本质就是:
> **把服务器上的一个目录,通过网络挂载成 Linux 可直接使用的文件系统。**
所以它非常适合做 rootfs。
#### 3)为什么不能把 rootfs 也直接用 TFTP
因为 TFTP 本质上只是一个 **文件传输协议**,不是一个真正的“可挂载文件系统”。
它适合做的是:
– 下载一个文件
– 再下载另一个文件
但它不适合做的是:
– 让 Linux 把它当 `/` 根目录来长期运行
– 支持成千上万个文件的按需访问
– 提供完整文件系统语义
所以:
> **TFTP 适合传文件,不适合直接充当运行中的根文件系统。**
当然,也有一种特殊情况:
– 先通过 TFTP 下载一个 `initramfs` 镜像
– 再由内核把这个镜像当临时根文件系统使用
但这本质上是“**TFTP 下载一个 rootfs 镜像到内存**”,不是“**把 TFTP 本身当 rootfs 文件系统**”。
#### 4)那能不能都用 NFS
理论上可以,前提是你的 U-Boot 编译时支持 `nfs` 命令。
也就是说,在某些环境里,U-Boot 也可以通过 NFS 去取:
– 内核镜像
– 设备树文件
但是实践中更常见的还是:
– **U-Boot 用 TFTP 拉内核和 dtb**
– **Linux 用 NFS 挂载 rootfs**
原因是:
– TFTP 在 bootloader 阶段更简单直接
– 配置和调试成本更低
– 兼容性通常更好
– 传少量启动文件已经足够
#### 5)一句话理解分工
你可以这样记:
– **TFTP**:适合启动阶段“搬运几个文件到内存”
– **NFS**:适合系统运行阶段“把整个目录树当文件系统来使用”
所以你现在这种方案本质上是:
> **启动文件传输用 TFTP,运行时根文件系统访问用 NFS。**
这是一种按协议特点做的分工,而不是随意搭配。
—
## 9. 为什么这种方式特别适合驱动开发
这种启动方式对驱动开发非常方便,原因包括:
### 9.1 改内核方便
你重新编好 `zImage` 后,只要更新服务器上的内核镜像,开发板下次启动就能用新内核。
### 9.2 改设备树方便
你修改并重新编译 `.dtb` 后,只要更新 TFTP 服务器上的 dtb 文件即可。
### 9.3 改根文件系统方便
因为根文件系统走 NFS,所以:
– 改应用程序
– 改脚本
– 改库文件
– 改测试程序
都不需要反复烧写 eMMC/SD 卡,只要直接改服务器目录内容即可。
### 9.4 调试效率高
非常适合:
– 内核开发
– 设备树调试
– 驱动开发
– 根文件系统调试
—
## 10. 一个容易误解的地方
你的设备树文件名是:
“`bash
imx6ull-alientek-emmc.dtb
“`
而你的根文件系统却是:
“`bash
root=/dev/nfs
“`
这两者并不矛盾。
因为:
– `imx6ull-alientek-emmc.dtb` 描述的是板级硬件平台
– `root=/dev/nfs` 决定的是 Linux 最终从哪里挂载根文件系统
也就是说:
> **你这块板是 eMMC 版本硬件,但当前调试时根文件系统走的是 NFS。**
这是完全正常的。
—
## 11. 在实际开发中如何查看和修改
### 查看当前值
“`bash
printenv bootcmd
printenv bootargs
“`
### 修改当前值
“`bash
setenv bootcmd ‘tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 – 83000000’
setenv bootargs ‘console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.100:/home/eh/linux/nfs/xuexi/rootfs,proto=tcp rw ip=192.168.10.50:192.168.10.100:192.168.10.1:255.255.255.0::eth0:off’
“`
### 保存环境变量
“`bash
saveenv
“`
如果不执行 `saveenv`,很多情况下修改只在本次上电有效,重启后可能丢失。
—
## 12. 一句话区分
你可以这样记:
– **`bootcmd`**:U-Boot 执行的“启动脚本”
– **`bootargs`**:传给 Linux 内核的“启动参数”
—
## 13. 总结
在 Linux 驱动开发中:
– **怀疑启动的不是最新内核或最新 dtb,看 `bootcmd`**
– **怀疑日志、根文件系统、网络启动、调试参数有问题,看 `bootargs`**
对于你当前这套参数,完整含义是:
> **U-Boot 通过 TFTP 下载 `zImage` 和 `dtb` 到内存中启动 Linux;Linux 内核通过静态 IP 与 NFS 服务器通信,并将服务器上的 rootfs 目录挂载为根文件系统。**
这是一种非常典型、非常适合内核和驱动开发的启动方式。