《uboot基础命令记录①》
之前学习了
uboot的一些基础命令,这里想写一篇博客记录一下参考文档:<正电原子驱动开发指南>
1.信息查询命令
-
bdinfo这里会打印一些开发板的基本信息,如:
DRAM 的起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。
bash=> bdinfo arch_number = 0x00000000 boot_params = 0x80000100 DRAM bank = 0x00000000 -> start = 0x80000000 -> size = 0x20000000 eth0name = FEC1 ethaddr = (not set) current eth = FEC1 ip_addr = <NULL> baudrate = 115200 bps TLB addr = 0x9FFF0000 relocaddr = 0x9FF55000 reloc off = 0x18755000 irq_sp = 0x9EF52EA0 sp start = 0x9EF52E90 -
printenv打印开发板的一些环境变量
bash=> printenv author=Yunes baudrate=115200 board_name=EVK board_rev=14X14 boot_fdt=try bootcmd=run findfdt;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr}; bootdelay=5 bootscript=echo Running bootscript from mmc ...; source console=ttymxc0 ethact=FEC1 ethprime=FEC fdt_addr=0x83000000 fdt_file=imx6ull-14x14-emmc-4.3-480x272-c.dtb fdt_high=0xffffffff findfdt=if test $fdt_file = undefined; then if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then echo WARNING: Could not determine dtb to use; fi; fi; image=zImage initrd_addr=0x83800000 initrd_high=0xffffffff ip_dyn=yes loadaddr=0x80800000 loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}; loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file} loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image} logo_file=alientek.bmp mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unused mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot} mmcautodetect=yes mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; mmcdev=0 mmcpart=1 mmcroot=/dev/mmcblk0p2 rootwait rw netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; panel=ATK-LCD-4.3-480x272 script=boot.scr splashimage=0x88000000 splashpos=m,m stderr=serial stdin=serial stdout=serial Environment size: 2598/8188 bytes我们可以看到有两个信息:
- author=Yunes
- bootdelay=5
这两个信息都是我之前添加过的,一个是添加作者名字,一个是启动延迟,也就是开发板reset之后倒计时。
-
version打印uoot版本号
bash=> version U-Boot 2016.03 (Dec 27 2025 - 21:01:51 +0800) arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4 GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git
2.环境变量操作命令
环境变量操作涉及到两个命令setenv 和saveenv,这两个很好理解吧,一个是设置一个是保存
setenv是修改DRAM 中的环境变量值,而saveenv是将修改后的环境变量存放到flash中,否则下次重启还是之前的变量
2.1添加or修改环境变量
回想第一小节的version下两个变量,具体操作如下
bash
setenv bootdelay 5
saveenv
serenv aothor Yunes
saveenv
遇到修改环境变量出现空格的情况,需要增加引号
bash
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
2.2删除环境变量
bash
setenv author
saveenv
#后面加空值即可
3.内存操作命令
内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有
md、nm、mm、mw、cp 和 cmp
3.1、md
md 命令用于显示内存值,格式如下:
bash
md[.b , .w, .l] address [# of objects]
b(byte)、w(word)、l(long) 分别对应1、2、4个字节,address 就是要查看的内存起始地址,[# of objects]表示要查看的数据长度
Ps:uboot命令中的数字都是十六进制的!
例如:
bash
=> md.b 80000000 10
80000000: 9f 77 ff fb df bf bf b2 ff fb fa ff ff bf be 7f .w..............
=> md.w 80000000 10
80000000: 779f fbff bfdf b2bf fbff fffa bfff 7fbe .w..............
80000010: 7ffe 5fff eab6 fff4 fbdd f5fc faef eafd ..._............
=> md.l 80000000 10
80000000: fbff779f b2bfbfdf fffafbff 7fbebfff .w..............
80000010: 5fff7ffe fff4eab6 f5fcfbdd eafdfaef ..._............
80000020: 3fbff47e aabcbebf fef17dce e8eaffde ~..?.....}......
80000030: fd7efdf3 bf8b6eab df6d7f5f eabedaee ..~..n.._.m.....
分析一下这三个命令
bash
=> md.b 80000000 10 #起始地址0x80000000 ,大小16*1=16字节
=> md.w 80000000 10 #起始地址0x80000000 ,大小16*2=32字节
=> md.l 80000000 10 #起始地址0x80000000 ,大小16*4=64字节
3.2、nm
nm 命令用于修改指定地址的内存值,格式如下:
bash
nm[.b , .w, .l] address
例如:
bash
=> nm.l 80000000
80000000: fbff779f ?
?后面写你要改成的内容,我们就写0x123456678,再q退出
bash
=> nm.l 80000000
80000000: fbff779f ? 12345678
80000000: 12345678 ? q
修改完成之后,我们在使用md命令查看一下,一共四个字节
bash
=> md.l 80000000 1
80000000: 12345678 xV4.
3.3 、mm
mm命令会自增
bash
=> mm.l 80000000
80000000: 12345678 ? 01010101
80000004: b2bfbfdf ? 02020202
80000008: fffafbff ? 03030303
8000000c: 7fbebfff ? q
=> md.l 80000000 3
80000000: 01010101 02020202 03030303 ............
3.4、mw
命令 mw 用于使用一个指定的数据填充一段内存,命令格式如下:
bash
mw[.b , .w, .l] address value [count]
例如:
bash
=> mw.l 80000000 0a0a0a0a 10
=> md.l 80000000 10
80000000: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000010: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000020: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000030: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
3.5、cp
cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中命令格式如下:
bash
cp [.b, .w, .l] source target count
例如:
bash
=> cp.l 80000000 80000100 10
=> md.l 80000100 10
80000100: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000110: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000120: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
80000130: 0a0a0a0a 0a0a0a0a 0a0a0a0a 0a0a0a0a ................
3.6、cmp
cmp 是比较命令,用于比较两段内存的数据是否相等,命令格式如下:
bash
cmp [.b, .w, .l] addr1 addr2 count
例如:
bash
=> cmp.l 80000000 80000100 10
Total of 16 word(s) were the same
4.总结
这些基础命令简单了解掌握,本来还有一个网络操作命令的,由于缺少路由器,uboot下的网络比较难搞,打算在后续linux篇章再介绍
4.网络操作命令
uboot 是支持网络的,我们在移植 uboot 的时候一般都要调通网络功能,因为在移植 linuxkernel 的时候需要使用到 uboot 的网络功能做调试。uboot 支持大量的网络相关命令,比如 dhcp、ping、nfs 和 tftpboot,我们接下来依次学习一下这几个和网络有关的命令。
记得进行以下步骤
- 查看主机网卡(网线),再去修改虚拟机网络适配器桥接模式下的网卡
- 修改ubuntu下的IP,将其设置为静态,这样不会因为后续开机dhcp导致不必要的麻烦
| 环境变量 | 描述 |
|---|---|
| ipaddr | 开发板 ip 地址,可以不设置,使用 dhcp 命令来从路由器获取 IP 地址。 |
| ethaddr | 开发板的 MAC 地址,一定要设置。 |
| gatewayip | 网关地址。 |
| netmask | 子网掩码。 |
| serverip | 服务器 IP 地址,也就是 Ubuntu 主机 IP 地址,用于调试代码。 |
bash
=> setenv ipaddr 10.135.25.24
=> setenv ethaddr b8:ae:1d:01:00:00
=> setenv gatewayip 10.135.25.1
=> setenv netmask 255.255.255.0
=> setenv serverip 10.135.25.24
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
修改完我们就重启,来测试一个最基本命令
4.1、ping
bash
=> ping 10.135.25.24
Using FEC1 device
host 10.135.25.24 is alive
4.2、nfs
这里直接对原文总结并修改:
NFS (网络文件系统)是一种通过网络在不同计算机之间共享文件的机制。
在嵌入式 Linux 开发中,常利用 NFS 在 Ubuntu 主机 与 开发板 之间共享资源。
例如:
我们可以将 Linux 内核镜像(zImage/uImage) 和 设备树文件(.dtb) 放在 Ubuntu 主机中,然后在 U-Boot 中通过 nfs 命令,把这些文件直接下载到开发板的 DRAM 中并运行。
这种方式的主要目的在于:
方便调试 Linux 内核和设备树文件**,也称为 网络调试。
为什么嵌入式 Linux 要用网络调试?
嵌入式 Linux 的开发方式与单片机开发有很大不同:
- 单片机开发
- 可以通过 J-Link、ST-Link 等仿真器
- 使用 MDK、IAR 等 IDE
- 一键下载程序到 MCU 内部 Flash
- 嵌入式 Linux 开发
- 系统通常存放在 eMMC / NAND / SPI Flash 等外部存储中
- 没有类似 MDK、IAR 这种"一键下载"的 IDE
- 固件烧写一般依赖厂家提供的烧写工具
而厂家提供的烧写工具通常:
- 操作步骤复杂
- 更适合 量产
- 在内核调试阶段频繁使用会 极大浪费时间
因此,在 Linux 内核调试阶段,直接反复烧写 Flash 并不高效。
网络调试的优势
网络调试正是为了解决上述问题而出现的:
- 内核或设备树修改后
- 只需在 Ubuntu 上重新编译
- 通过 U-Boot + NFS
- 直接将文件下载到 DRAM 中运行
无需烧写 Flash,大大提高调试效率。
这也是 嵌入式 Linux 开发中最常用的调试方式之一。
U-Boot 中使用 NFS
通常流程如下:
- 在 Ubuntu 主机上:
- 开启 NFS 服务
- 创建一个 NFS 共享目录
- 所有需要通过网络加载的文件(内核、设备树等)都放在该目录中
- 在 U-Boot 中:
- 使用
nfs命令 - 将 Ubuntu 主机 NFS 目录中的文件下载到开发板的 DRAM 中运行
- 使用
但是nfs不稳定还要改很多东西,我直接上tftp
4.3、tftp
安装环境
bash
sudo apt-get install tftp-hpa tftpd-hpa
sudo apt-get install xinetd
依旧创建文件夹
bash
mkdir /home/yunes/linux/tftpboot && chmod 777 /home/yunes/linux/tftpboot
bash
vi /etc/xinetd.d/tftp
service tftp
{
socket_type = dgram
protocol = udp
wait = yes
user = root
server = /usr/sbin/in.tftpd
server_args = -s /home/yunes/linux/tftpboot/
disable = no
per_source = 11
cps = 100 2
flags = IPv4
}
启动tftp
bash
sudo service tftpd-hpa start
再编写修改
bash
vi /etc/default/tftpd-hpa
# /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/yunes/linux/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-l -c -s"
重启:
bash
sudo service tftpd-hpa restart
将zImage放到tftp文件夹里
下载
bash
tftp 80800000 zImage
下载完毕
bash
=> tftp 80800000 zImage
Using FEC1 device
TFTP from server 10.135.25.24; our IP address is 10.135.25.25
Filename 'zImage'.
Load address: 0x80800000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
########
1.9 MiB/s
done
Bytes transferred = 6785480 (6789c8 hex)
5.总结
目前还只总结了一部分,后续还要再补充,但是这个网络真的麻烦啊,没路由器只能再虚拟机网络桥接网线和无线来回摆动,浪费了很多时间,尤其是这个nfs,版本兼容问题,气死我了!!!不过好在加深了印象,继续努力😀