LV05-01-uboot-03-uboot相关命令

本文主要是uboot相关命令的相关笔记。若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows版本 windows11
Ubuntu版本 Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
Win32DiskImager Win32DiskImager v1.0
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
https://elixir.bootlin.com/linux/latest/source 在线阅读linux kernel源码
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)

这里使用的是NXP厂商提供的u-boot,u-boot的命令是可以自己定义和裁剪的。

一、几个常用命令

1. help

1.1 查看所有支持的命令

我们在 SecureCRT 终端输入 help ,便会打印出当前 uboot 所支持的所有命令。

image-20221013095607018
点击查看相关命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
?       - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
bmode - sd1|sd2|qspi1|normal|usb|sata|ecspi1:0|ecspi1:1|ecspi1:2|ecspi1:3|esdhc1|esdhc2|esdhc3|esdhc4 [noreset]
bmp - manipulate BMP image data
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootvx - Boot vxWorks from an ELF image
bootz - boot Linux zImage image from memory
clocks - display clocks
clrlogo - fill the boot logo area with black
cmp - memory compare
coninfo - print console devices and information
cp - memory copy
crc32 - checksum calculation
dcache - enable or disable data cache
dhcp - boot image via network using DHCP/TFTP protocol
dm - Driver model low level access
echo - echo args to console
editenv - edit environment variable
env - environment handling commands
erase - erase FLASH memory
exit - exit script
ext2load- load binary file from a Ext2 filesystem
ext2ls - list files in a directory (default /)
ext4load- load binary file from a Ext4 filesystem
ext4ls - list files in a directory (default /)
ext4size- determine a file's size
ext4write- create a file in the root directory
false - do nothing, unsuccessfully
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fatsize - determine a file's size
fdt - flattened device tree utility commands
flinfo - print FLASH memory information
fstype - Look up a filesystem type
fuse - Fuse sub-system
go - start application at address 'addr'
gpio - query and control gpio pins
help - print command description/usage
i2c - I2C sub-system
icache - enable or disable instruction cache
iminfo - print header information for application image
imxtract- extract a part of a multi-image
itest - return true/false on integer compare
load - load binary file from a filesystem
loadb - load binary file over serial line (kermit mode)
loads - load S-Record file over serial line
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
ls - list files in a directory (default /)
md - memory display
mdio - MDIO utility commands
mii - MII utility commands
mm - memory modify (auto-incrementing address)
mmc - MMC sub system
mmcinfo - display MMC info
mtest - simple RAM read/write test
mw - memory write (fill)
nfs - boot image via network using NFS protocol
nm - memory modify (constant address)
ping - send ICMP ECHO_REQUEST to network host
pmic - PMIC
printenv- print environment variables
protect - enable or disable FLASH write protection
reset - Perform RESET of the CPU
run - run commands in an environment variable
save - save file to a filesystem
saveenv - save environment variables to persistent storage
setenv - set environment variables
setexpr - set environment variable as the result of eval expression
sf - SPI flash sub-system
showvar - print local hushshell variables
size - determine a file's size
sleep - delay execution for some time
source - run script from memory
test - minimal test like /bin/sh
tftpboot- boot image via network using TFTP protocol
true - do nothing, successfully
usb - USB sub-system
usbboot - boot from USB device
version - print monitor, compiler and linker version

1.2 查看指定命令帮助信息

我们输入 help(或 ? )命令名 就可以查看指定命令的详细用法 ,即

1
2
? cmd_name
help cmd_name

1.3 使用实例

点击查看实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
=> help bootz
bootz - boot Linux zImage image from memory

Usage:
bootz [addr [initrd[:size]] [fdt]]
- boot Linux zImage stored in memory
The argument 'initrd' is optional and specifies the address
of the initrd in memory. The optional argument ':size' allows
specifying the size of RAW initrd.
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead

=> ? bootz
bootz - boot Linux zImage image from memory

Usage:
bootz [addr [initrd[:size]] [fdt]]
- boot Linux zImage stored in memory
The argument 'initrd' is optional and specifies the address
of the initrd in memory. The optional argument ':size' allows
specifying the size of RAW initrd.
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead

2. reset

2.1 使用格式

reset 命令可以用于重启开发板,我们直接在命令行输入 reset 即可:

1
reset   

2.2 使用实例

点击查看实例
1
2
=> reset
resetting ...

3. loadb

3.1 使用格式

loadb 命令通过串口进行数据的传输,我们一般使用该命令进行二进制文件的传输,它使用的是 kermit protocol 传输协议,一般使用格式如下:

1
loadb [ off ] [ baud ]

【参数说明】

  • off :其实就是数据要加载到内存中的起始地址。
  • baud :传输的波特率,可以不写,默认为 115200 。

【注意】我们要是使用此种方式传输裸机程序的话,需要将裸机可执行文件 xxx.bin下载到开发板 DRAM 的 0X87800000 地址处,因为裸机可执行程序的链接首地址就0x87800000,当然,我们也可以修改裸机工程中的链接文件,修改链接地址,这里与链接地址一致即可。

3.2 使用实例

点击查看详情
1
2
=> loadb 87800000  
## Ready for binary (kermit) download to 0x80800000 at 115200 bps...

然后按下 enter 按键便会等待数据的传输,我们可以直接将文件拖拽到 SecureCRT 终端界面中,然后选择发送 Kermmit 即可开启文件传输,将会有以下提示:

1
2
3
4
5
6
  kermit  trl+C ȡ
正在传输 device.h...
100% 41 KB 10 KB/ 00:00:04 0

## Total Size = 0x0000a77d = 42877 Bytes
## Start Addr = 0x80800000

4. go

4.1 使用格式

go 命令用于跳到指定的地址处执行应用,命令格式如下:

1
go addr [arg ...]

【参数说明】

  • addr :是应用在 DRAM 中的首地址。

4.2 使用实例

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
=> tftp 87800000 /imx6ull/lcd.bin
FEC1 Waiting for PHY auto negotiation to complete... done
Using FEC1 device
TFTP from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/imx6ull/lcd.bin'.
Load address: 0x87800000
Loading: ###
6.8 KiB/s
done
Bytes transferred = 35953 (8c71 hex)

=> go 87800000
## Starting application at 0x87800000 ...
LCD ID=0x4384

5. run

5.1 使用格式

run 命令用于运行环境变量中定义的命令,比如可以通过 run bootcmd 来运行 bootcmd 中的启动命令,但是 run 命令最大的作用在于运行我们自定义的环境变量。

在后面调试 Linux 系统的时候可能常常要在网络启动和 EMMC/NAND 启动之间来回切换,而 bootcmd 只能保存一种启动方式,如果要换另外一种启动方式的话就得重写 bootcmd ,会很麻烦。这里我们就可以通过自定义环境变量来实现不同的启动方式,比如定义环境变量 mybootemmc 表示从 emmc 启动,定义 mybootnet 表示从网络启动,定义 mybootnand 表示从 NAND 启动。如果要切换启动方式的话只需要运行 run mybootxxx ( xxx 为 emmc 、 net 或 nand )即可。一般命令格式如下:

1
run var [...]

【参数说明】

  • var :表示需要运行的定义了命令的环境变量名。

5.2 使用实例

点击查看详情
1
2
3
4
5
6
7
=> setenv bootcmd 'tftp 80800000 /imx6ull/zImage\;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb\;bootz 80800000 - 83000000'
=> printenv bootcmd
bootcmd=tftp 80800000 /imx6ull/zImage;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb;bootz 80800000 - 83000000
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
=> run bootcmd

6. mtest

mtest 命令是一个简单的内存读写测试命令,可以用来测试自己开发板上的 DDR ,一般命令格式如下:

1
mtest [start [end [pattern [iterations]]]]
  • start :要测试的 DRAM 开始地址。
  • end :是结束地址。

【注意】如果要结束测试就按下键盘上的 Ctrl + C 键。

二、信息查询

常用的和信息查询有关的命令有 3 个: printenv、bdinfo 和 version。

1. printenv

1.1 使用格式

该命令主要用于打印出所有的环境变量信息,我们在 SecureCRT 终端中直接执行下边的命令,便可以得到该命令的使用格式:

1
2
3
4
5
6
7
8
=> ? printenv
printenv - print environment variables

Usage:
printenv [-a]
- print [all] values of all environment variables
printenv name ...
- print value of environment variable 'name'

该命令使用方式很简单,我们直接在命令行输入下边的命令即可。

1
2
3
printenv [env_name]
# 或者
print [env_name]

【参数说明】

  • env_name :环境变量名,它是可选的,若要查看所有环境变量,省略该参数即可。

1.2 使用实例

1.2.1 查看所有环境变量

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
=> print
baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootargs=console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/imx_rootfs,proto=tcp rw ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off
bootcmd=tftp 80800000 /imx6ull/zImage;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb;bootz 80800000 - 83000000
# 中间部分省略 ... ...
script=boot.scr
serverip=192.168.10.101

Environment size: 2673/8188 bytes

1.2.2 查看指定环境变量

点击查看详情
1
2
=> print bootcmd
bootcmd=tftp 80800000 /imx6ull/zImage;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb;bootz 80800000 - 83000000

2. bdinfo

2.1 使用格式

bdinfo 命令,此命令用于查看板子信息,直接在命令行输入该命令即可,一般使用格式如下:

1
bdinfo

通过该命令,我们可以看到 DRAM 的起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。

2.2 使用实例

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
=> bdinfo
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
eth0name = FEC1
ethaddr = b8:ae:1d:01:00:00
current eth = FEC1
ip_addr = 192.168.10.102
baudrate = 115200 bps
TLB addr = 0x9FFF0000
relocaddr = 0x9FF47000
reloc off = 0x18747000
irq_sp = 0x9EF44EA0
sp start = 0x9EF44E90
FB base = 0x00000000

3. version

3.1 使用格式

version 用于查看 uboot 的版本号,直接输入 version 即可:

1
version

3.2 使用实例

点击查看详情
1
2
3
4
5
=> version 

U-Boot 2016.03-g9e33bde-dirty (Sep 25 2022 - 21:24:46 +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

三、环境变量

上边我们通过 printenv 打印出来的信息中有很多的环境变量,比如 baudrate 、 bootcmd 等等。 uboot 中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和变量一样。比如 bootdelay 这个环境变量就表示 uboot 启动延时时间,默认 bootdelay=3 ,也就默认延时 3 秒。前面说的 3 秒倒计时就是由 bootdelay 定 义的,如果将 bootdelay 改为 5 的话就会倒计时 5s 了。

1. setenv

1.1 使用格式

该命令主要用于操作环境变量,我们在 SecureCRT 终端中查看一下该命令使用格式:

1
2
3
4
5
6
7
8
=> ? setenv
setenv - set environment variables

Usage:
setenv [-f] name value ...
- [forcibly] set environment variable 'name' to 'value ...'
setenv [-f] name
- [forcibly] delete environment variable 'name'

该命令可以用于创建、修改、删除环境变量,一般使用格式如下:

1
setenv env_name env_value

【参数说明】

  • env_name :表示要修改的环境变量名称。
  • env_value : 表示环境变量的值,此参数省略的话可以用于删除相应的环境变量。

【注意】

(1)此命令修改完后,开发板若重启,那么所有环境变量将会复原。

(2)有时候我们修改的环境变量值可能会有空格, 比如 bootcmd、 bootargs 等, 这个时候环境变量值就得用单引号括起来(后便会介绍这两个环境变量)。

1.2 使用实例

1.2.1 修改环境变量

点击查看详情

我们想修改一下 uboot 启动 linux 倒计时,这个时间是由 bootdelay 环境变量来控制的,我们上边是 5 秒,我们可以通过以下命令更改秒数

1
2
3
4
5
=> printenv bootdelay 
bootdelay=3
=> setenv bootdelay 5
=> printenv bootdelay
bootdelay=5

1.2.2 创建环境变量

点击查看详情
1
2
3
4
5
=> printenv aaa
## Error: "aaa" not defined
=> setenv aaa 1234567890
=> printenv aaa
aaa=1234567890

1.2.3 删除环境变量

点击查看详情

我们创建了不想要的环境变量怎么办呢?有创建肯定有删除,该命令同样也可以删除环境变量,去掉后边的值,就可以将一个已经存在的环境变量删除:

1
2
3
4
5
=> printenv aaa
aaa=1234567890
=> setenv aaa
=> printenv aaa
## Error: "aaa" not defined

删除后,我们打印一个不存在测环境变量便会出错。

2. saveenv

2.1 使用格式

前边的环境变量我们修改后,只在此次使用有效,当开发板关机时,修改过的环境变量全部会失效,那如何保存下来,让这些修改过的环境变量后边也依然可以生效呢?我们可以使用 saveenv 命令,我们直接在终端输入命令即可,一般格式如下:

1
saveenv

2.2 保存到了哪里?

我们使用这个命令后,环境变量被保存到哪里了呢?这个应该是在uboot源码中进行了定义,我没有去追踪,但是后来在一些资料中了解到,开发板若是从SD卡启动就保存到SD卡,EMMC启动就保存到EMMC,从后边学习mmc read命令的时候,可以看到环境变量存储的起始地址是从相应的mmc的0x600开始的。

2.3 使用实例

点击查看详情
1
2
3
=> saveenv 
Saving Environment to MMC...
Writing to MMC(0)... done

这样我们修改过的环境变量就会被保存到 eMMC ,下一次开发板重新启动,环境变量还会是我们修改后的值。

四、内存操作

内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、 nm、mm、 mw、 cp 和 cmp。

1. md

1.1 使用格式

md 命令用于显示内存值,格式如下:

1
md[.b, .w, .l] address [# of objects]

【参数说明】

  • [.b .w .l] :对应 byte 、 word 和 long ,也就是分别以 1 个字节 、 2 个字节、 4 个字节来显示内存值。
  • address :要查看的内存起始地址。
  • [# of objects] :表示要查看的数据长度,这个数据长度单位不是字节,而是跟我们所选择的显示格式有关。比如我们设置要查看的内存长度 为 20 (十六进制为 0x14 ),如果显示格式为 .b 的话那就表示 20 个字节;如果显示格式为 .w 的话就表示 20 个 word ,也就是 20*2=40 个字节;如果显示格式为 .l 的话就表示 20 个 long ,也就是 20*4=80 个字节。

【注意】

(1)uboot 命令中的数字都是十六进制的,不是十进制的!命令里面的数字都是十六进制的,所以可以不用写 0x 前缀,十进制的 20 其十六进制为 0x14 ,所以命令 md 后面的个数应该是 14 ,如果写成 20 的话就表示查看 32 (十六进制为 0x20 )个字节的数据。

(2)md与[.b, .w, .l]之间没有空格,紧挨着写。

1.2 使用实例

点击查看详情

我们若想查看以 0x80000000 开始的 20 个字节的内存值,显示格式为 .b 的话,应该使用如下所示命令:

1
2
3
=> md.b 80000000 14
80000000: ff ff f7 ff f7 ff ff ff ff ff ff ff ff ff ff ff ................
80000010: ff ff ff ff ....

1.3 b、w、l

这三者是指定显示内存值的基本单位,我们可以实际看一下这三个参数的区别:

1
2
3
4
5
6
7
8
9
10
=> md.b 80000000 10 
80000000: ff ff f7 ff f7 ff ff ff ff ff ff ff ff ff ff ff ................
=> md.w 80000000 10
80000000: ffff fff7 fff7 ffff ffff ffff ffff ffff ................
80000010: ffff ffff ffff ffff ffff ffff ffff ffff ................
=> md.l 80000000 10
80000000: fff7ffff fffffff7 ffffffff ffffffff ................
80000010: ffffffff ffffffff ffffffff ffffffff ................
80000020: fffffffd bfffffff ffffffff ffffffff ................
80000030: ffffffff ffffffff ffffffff ffffffff ................

上面这三个命令都是查看以 0x80000000 为起始地址的内存数据,第一个命令以 .b 格式显示,长度为 0x10,也就是 16 个字节;第二个命令以 .w 格式显示,长度为 0x10,也就是 16*2=32 个字节;最后一个命令以 .l 格式显示,长度也是 0x10,也就是 16*4=64 个字节。

2. nm

2.1 使用格式

nm 命令用于修改指定地址的内存值,命令格式如下:

1
nm[.b, .w, .l] address

nm 命令同样可以以 .b 、 .w 和 .l 来指定操作格式。需要注意的是,修改内存需要两步,而且需要退出内存修改,后边的实例会有体现。

【注意】

(1)nm与[.b, .w, .l]之间没有空格,紧挨着写。

(2)使用 q 退出内存的修改。

2.2 使用实例

例如,我们现在要以 .l 格式修改 0x80000000 地址处数据为 12345678 ,我们输入以下命令:

1
2
=> nm.l 80000000 
80000000: fff7ffff ?

其中 80000000 表示现在要修改的内存地址, fff7ffff 表示地址 40008000 现在的数据, ? 后面就可以输入要修改后的数据 0x12345678 ,输入完成以后按下回车:

1
2
3
=> nm.l 80000000 
80000000: fff7ffff ? 12345678
80000000: 12345678 ?

这样我们便修改了相应的数据,然后输入 q 再按下回车就可以退出了

1
2
3
=> nm.l 80000000 
80000000: fff7ffff ? 12345678
80000000: 12345678 ? q

3. mm

3.1 使用格式

mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增,而使用命令 nm 的话地址不会自增。使用格式如下:

1
mm[.b, .w, .l] address

使用方式与 nm 一致,只不过它的地址会自己增加,

【注意】

(1)mm与[.b, .w, .l]之间没有空格,紧挨着写。

3.2 使用实例

点击查看详情

此处,我们以以 .l 格式修改从地址 0x80000000 开始的连续 3 个内存块(3*4=12个字节)的数据为 0x87654321 :

1
2
3
4
5
6
7
=> mm.l 80000000
80000000: 12345678 ? 87654321
80000004: fffffff7 ? 87654321
80000008: ffffffff ? 87654321
8000000c: ffffffff ? q
=> md.l 80000000 4
80000000: 87654321 87654321 87654321 ffffffff !Ce.!Ce.!Ce.....

4. mw

4.1 使用格式

mw 用于使用一个指定的数据填充一段内存,命令格式如下:

1
mw[.b, .w, .l] address value [count]

【参数说明】

  • [.b .w .l] :对应 byte 、 word 和 long ,也就是分别以 1 个字节 、 2 个字节、 4 个字节填充指定的内存。
  • address :要查看的内存起始地址。
  • value :为要填充的数据。
  • count :填充的长度。

4.2 使用实例

点击查看详情

我们使用.l 格式将以 0x80000000 为起始地址的 0x10 个内存块(0x10 * 4=64 字节)填充为 0x50505050,

1
2
3
4
5
6
=> mw.l 80000000 50505050 10
=> md.l 80000000 10
80000000: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000010: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000020: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000030: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP

5. cp

5.1 使用格式

cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 Nor Flash 中的数据拷贝到 DRAM 中。命令格式如下:

1
cp[.b, .w, .l] source target count

【参数说明】

  • [.b .w .l] :对应 byte 、 word 和 long ,也就是分别以 1 个字节 、 2 个字节、 4 个字节拷贝数据。
  • source :源地址。
  • target :目的地址。
  • count :为拷贝的长度。

5.2 使用实例

点击查看详情

我们使用 .l 格式将 0x80000000 处的地址拷贝到 0X80000100 处,长度为 0x10 个内存块(0x10 * 4=64 个字节),命令如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
=> md.l 80000000 10 
80000000: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000010: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000020: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000030: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
=> md.l 80000100 10
80000100: ffffffff fbffffff ffffffff ffffffff ................
80000110: ffffffff ffffffff fffff7ff ffffffff ................
80000120: ffff7fff ffffffff ffffffff ffffffff ................
80000130: ffffffff fffbffff ffffffff ffffffff ................
=> cp.l 80000000 80000100 10
=> md.l 80000100 10
80000100: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000110: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000120: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000130: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP

6. cmp

6.1 使用格式

cmp 是比较命令,用于比较两段内存的数据是否相等,命令格式如下:

1
cmp[.b, .w, .l] addr1 addr2 count

【参数说明】

  • [.b .w .l] :对应 byte 、 word 和 long ,也就是对应 1 个字节 、 2 个字节、 4 个字节。
  • addr1 :第一段内存首地址。
  • addr1 :第二段内存首地址。
  • count :为要比较的长度。

6.2 使用实例

点击查看详情

我们使用 .l 格式来比较 0x80000000 和 0x80000100 这两个地址数据是否相等,比较长度为 0x10 个内存块(16 * 4=64 个字节)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=> md.l 80000000 10         
80000000: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000010: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000020: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000030: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
=> md.l 80000100 10
80000100: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000110: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000120: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
80000130: 50505050 50505050 50505050 50505050 PPPPPPPPPPPPPPPP
=> cmp.l 80000000 80000100 10
Total of 16 word(s) were the same
=> cmp.l 80000000 80000100 11
word at 0x80000040 (0xfffff7ff) != word at 0x80000140 (0xdff7ffff)
Total of 16 word(s) were the same
=> cmp.l 80000000 80000100 13
word at 0x80000040 (0xfffff7ff) != word at 0x80000140 (0xdff7ffff)
Total of 16 word(s) were the same

五、存储器访问

uboot 支持 EMMC 和 SD 卡,因此也会提供 EMMC 和 SD 卡的操作命令。 关于存储器的访问主要是 mmc 命令,我们使用 help mmc 来查看它的帮助信息:

1
2
3
4
5
6
7
=> help mmc
mmc - MMC sub system

Usage:
mmc info - display info of the current MMC device
mmc read addr blk# cnt
# 后边的省略 ... ...

需要注意的是不同版本的uboot对命令的支持也不尽相同,关于目前使用的uboot2016.03版本中关于mmc的命令总结如下:

命令 描述
mmc info 输出 MMC 设备信息
mmc read 读取 MMC 中的数据。
mmc wirte 向 MMC 设备写入数据。
mmc rescan 扫描 MMC 设备。
mmc part 列出 MMC 设备的分区。
mmc dev 切换 MMC 设备。
mmc list 列出当前有效的所有 MMC 设备。
mmc hwpartition 设置 MMC 设备的分区。
mmc bootbus…… 设置指定 MMC 设备的 BOOT_BUS_WIDTH 域的值。
mmc bootpart…… 设置指定 MMC 设备的 boot 和 RPMB 分区的大小。
mmc partconf…… 设置指定 MMC 设备的 PARTITION_CONFG 域的值。
mmc rst 复位 MMC 设备
mmc setdsr 设置 DSR 寄存器的值。

1. mmc info

1.1 使用格式

该命令用于查看当前设备的 mmc 信息,直接输入命令即可:

1
mmc info

还有一个与 mmc info 命令相同功能的命令: mmcinfo,mmc和info之间没有空格。

1.2 使用实例

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
=> mmc info
Device: FSL_SDHC
Manufacturer ID: 3
OEM: 5344
Name: SU04G
Tran Speed: 50000000
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 3.7 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes

这里我们采用的是从SD卡启动,所以默认的设备是SD卡,所以这里显示的是SD卡的相关信息。

2. mmc rescan

mmc rescan 命令用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡,直接输入 mmc rescan 即可。

1
mmc rescan

3. mmc list

3.1 使用格式

mmc list 命令用于来查看当前开发板一共有几个 MMC 设备,直接输入 mmc list 即可:

1
mmc list

3.2 使用实例

点击查看详情
1
2
3
=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1

可以看出当前开发板有两个 MMC 设备: FSL_SDHC:0 和 FSL_SDHC:1 (eMMC),这是因为现在用的是 EMMC 版本的核心板,加上 SD 卡一共有两个 MMC 设备, FSL_SDHC:0 是 SD卡, FSL_SDHC:1是 EMMC。由于我们启动方式设置的是从SD卡启动,所以默认会将 SD 卡设置为当前 MMC 设备,这就是为什么输入“mmc info”查询到的是 SD卡 设备信息,而不是 EMMC 。要想查看 EMMC 信息,就要使
用命令“mmc dev”来将 EMMC 设置为当前的 MMC 设备。

4. mmc dev

4.1 使用格式

mmc dev 命令用于切换当前 MMC 设备,一般使用格式如下:

1
mmc dev [dev] [part]

【参数说明】

  • dev :设置要切换的 MMC 设备号。
  • part :分区号,如果不写分区号的话默认为分区 0 。

4.2 使用实例

点击查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1

=> mmc info
Device: FSL_SDHC
Manufacturer ID: 3
OEM: 5344
Name: SU04G
Tran Speed: 50000000
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 3.7 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes

=> mmc dev 1
switch to partitions #0, OK
mmc1(part 0) is current device

=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1 (eMMC)

=> mmc info
Device: FSL_SDHC
Manufacturer ID: 15
OEM: 100
Name: 8GTF4
Tran Speed: 52000000
Rd Block Len: 512
MMC version 4.0
High Capacity: Yes
Capacity: 7.3 GiB
Bus Width: 8-bit
Erase Group Size: 512 KiB

5. mmc part

5.1 使用格式

有时候 SD 卡或者 EMMC 会有多个分区,可以使用命令 mmc part 来查看其分区,直接输入命令即可:

1
mmc part

5.2 使用实例

点击查看详情

我们来查看一下开发板中 emmc 的分区情况:

1
2
3
4
5
6
7
8
9
10
=> mmc dev 1
switch to partitions #0, OK
mmc1(part 0) is current device
=> mmc part

Partition Map for MMC device 1 -- Partition Type: DOS

Part Start Sector Num Sectors UUID Type
1 20480 262144 7f764a7b-01 0c
2 282624 14987264 7f764a7b-02 83

此时 EMMC 有两个分区, 第一个分区起始扇区为 20480,长度为 262144 个扇区; 第二个分区起始扇区为 282624,长度为 14594048 个扇区。

如果 EMMC 里面烧写了 Linux 系统的话, EMMC 是有 3 个分区的,第 0 个分区存放 uboot,第 1 个分区存放 Linux 镜像文件和设备树,第 2 个分区存放根文件系统。但是在上边只有两个分区,那是因为第 0 个分区没有格式化,所以识别不出来,实际上第 0 个分区是存在的。

一个新的 SD 卡默认只有一个分区,那就是分区 0,所以前面讲解的 uboot 烧写到 SD 卡,其实就是将 u-boot.bin 烧写到了 SD 卡的分区 0 里面。

6. mmc read

6.1 使用格式

mmc read 命令用于将 MMC 中指定扇区中的内容读取到内存中指定的地址,命令格式如下:

1
mmc read <dev_num> addr blk# cnt

【参数说明】

  • dev_num : mmc 的设备号,可以通过 mmc list 查询到,这个参数可以不写,不写的话默认为当前 mmc 设备。
  • addr :数据读取到内存中的起始地址。
  • blk# :要读取的起始地址 (十六进制 ),一个块是 512 字节,这里的块和扇区是一个意思,在 MMC 设备中我们通常说扇区。
  • cnt :要读取的数量 (十六进制 )。

【注意】一个块是 512 字节。

6.2 使用实例

点击查看详情

我们这里使用的mmc设备为SD卡,我们从 SD卡的第 1536(0x600)个块开始,读取 16(0x10)个块的数据到 DRAM 的0x80800000 地址处,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
=> md.l 80800000 20        
80800000: 6c696146 20657275 6e616863 676e6967 Failure changing
80800010: 73756220 65707320 28206465 0a296425 bus speed (%d).
80800020: 6c615600 63206469 20706968 72646461 .Valid chip addr
80800030: 65737365 20003a73 58323025 72754300 esses:. %02X.Cur
80800040: 746e6572 73756220 20736920 000a6425 rent bus is %d..
80800050: 61766e49 2064696c 20737562 000a6425 Invalid bus %d..
80800060: 74746553 20676e69 20737562 25206f74 Setting bus to %
80800070: 46000a64 756c6961 63206572 676e6168 d..Failure chang
=> mmc dev 0
switch to partitions #0, OK
mmc0 is current device
=> mmc read 80800000 600 10

MMC read: dev # 0, block # 1536, count 16 ... 16 blocks read: OK
=> md.l 80800000 20
80800000: a288750f 64756162 65746172 3531313d .u..baudrate=115
80800010: 00303032 72616f62 616e5f64 453d656d 200.board_name=E
80800020: 62004b56 6472616f 7665725f 5834313d VK.board_rev=14X
80800030: 62003431 5f746f6f 3d746466 00797274 14.boot_fdt=try.
80800040: 746f6f62 73677261 6e6f633d 656c6f73 bootargs=console
80800050: 7974743d 3063786d 3531312c 20303032 =ttymxc0,115200
80800060: 746f6f72 65642f3d 666e2f76 666e2073 root=/dev/nfs nf
80800070: 6f6f7273 39313d74 36312e32 30312e38 sroot=192.168.10

从这里我们可以看到,uboot的环境变量是保存在了SD卡中,并且存储起始地址就是1536*512=786432。

7. mmc write

7.1 使用格式

mmc write 命令用于将数据写到 MMC 设备里面。我们可以使用命令 mmc write命令来升级 uboot ,也就是在 uboot 中更新uboot 。这里会要用到 nfs 或者 tftp 命令,通过 nfs 或者 tftp 命令将新的 u-boot.bin 下载到开发板的 DRAM 中,然后再使用命令 mmc write 将其写入到 MMC 设备中。一般使用格式如下:

1
mmc write <dev_num> addr blk# cnt

【参数说明】

  • dev_num : mmc 的设备号,可以通过 mmc list 查询到,这个参数可以不写,不写的话默认为当前 mmc 设备。
  • addr :数据将要写入到内存中的起始地址。
  • blk# :要写入的块起始地址 (十六进制 ),一个块是 512 字节,这里的块和扇区是一个意思,在 MMC 设备中我们通常说扇区。
  • cnt :要写入的块数量 (十六进制 )。

【注意】千万不要写 SD 卡或者 EMMC 的前两个块 (扇区 ),里面保存着分区表。

7.2 使用实例

我们可以使用命令 mmc write 来升级 uboot,也就是在 uboot 中更新 uboot。这里要用到 nfs 或者 tftp 命令,通过 nfs 或者 tftp 命令将新的 u-boot.bin 下载到开发板的 DRAM 中,然后再使用命令 mmc write 将其写入到 MMC设备中。

7.2.1 更新SD卡中的uboot

  • 查看原来的uboot信息
1
2
3
4
5
6
7
8
=> mmc dev 0
switch to partitions #0, OK
mmc0 is current device
=> version

U-Boot 2016.03-g9e33bde-dirty (Sep 25 2022 - 21:24:46 +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

我们切换到SD卡设备,然后查看一下当前的uboot信息,可以看到目前的uboot编译时间为 Sep 25 2022 - 21:24:46。

  • 重新编译uboot,并生成load.imx文件

重编编译uboot后,我们会得到一个 u-boot.bin 文件,这个文件不能直接使用,我们还需要通过烧写工具加上一些信息,得到一个 .imx 文件,这个可以修改一下源码,将 imxdownload.c 文件中的这句注释掉:

1
system(cmdbuf);

这样就不会真的烧写到设备节点啦,所以这个情况下,设备节点随便写就是了,然后我们就会得到一个 load.imx文件,然后我们将这个文件放到tftp目录下:

1
/home/hk/3tftp/imx6ull/load.imx
点击查看直接方式

其实,uboot源码中带有一个工具,我们编译好uboot后,会自动生成一个 u-boot.imx 文件,我们可以直接使用的。

  • 通过tftp下载uboot
1
2
3
4
5
6
7
8
9
10
=> tftp 80800000 /imx6ull/load.imx
FEC1 Waiting for PHY auto negotiation to complete... done
Using FEC1 device
TFTP from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/imx6ull/load.imx'.
Load address: 0x80800000
Loading: #############################
75.2 KiB/s
done
Bytes transferred = 421464 (66e58 hex)

可以看出,load.imx 大小为 421464 字节, 421464/512=824,所以我们要向 SD 卡中写入824(0x338)个块(如果有小数的话就要加 1 个块 )。

  • 写入uboot到SD卡中
1
2
3
4
5
6
7
=> mmc dev 0 0
switch to partitions #0, OK
mmc0 is current device

=> mmc write 80800000 2 338

MMC write: dev # 0, block # 2, count 824 ... 824 blocks written: OK
  • 重启开发板,查看编译时间
1
2
3
4
5
=> version

U-Boot 2016.03-g9e33bde-dirty (Oct 13 2022 - 17:11:27 +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

可以看到此时的uboot编译时间变成了 Oct 13 2022 - 17:11:27,这也就意味着我们完成了SD卡中uboot的更新。

7.2.2 更新EMMC中uboot

其实跟更新SD卡中 uboot 过程类似,这里只写需要的命令(在开发板的终端执行):

1
2
3
4
mmc dev 1 0                      # 切换到 EMMC 分区 0
tftp 80800000 /imx6ull/load.imx # 下载 load.imx 到 DRAM
mmc write 80800000 2 338 # 烧写 load.imx 到 EMMC 中
mmc partconf 1 1 0 0 # 分区配置, EMMC 需要这一步

8. mmc erase

mmc erase 命令用于擦除 MMC 设备的指定块,一般使用格式如下:

1
mmc erase blk# cnt

【参数说明】

  • blk# :要擦除的起始块。
  • cnt :要擦除的数量。

【注意】一般不要用 mmc erase 来擦除 MMC 设备。

六、网络操作

在 uboot 中,至少支持的外设就是串口和网口,因为我们需要通过这两个外设进行文件的传输,下边我们先来学习一下网络操作相关命令吧。这里需要注意I.MX6U-ALPHA 开发板有两个网口: ENET1 和 ENET2,一定要连接 ENET2,当然也可以修改uboot源码,使其改为 ENET1 ,默认没有修改的是使用的 ENET2。

1. 网络环境变量

要使用网络的话,还是需要先设置一下网络的相关环境变量。相关的环境变量如下:

环境变量描述
ipaddr开发板 IP 地址,可以不设置,使用 dhcp 命令来从路由器获取 IP 地址。
ethaddr开发板的 MAC 地址,一定要设置。
gatewayip网关地址。
netmask子网掩码。
serverip服务器IP地址,也就是 Ubuntu主机 IP 地址,用于调试代码。

【注意】

(1)开发板的 IP 一定要与 ubuntu 主机的 IP 处于网段,这样才能实现网络通信。

(2)如果在同一个网段内有多个开发板的话一定要保证每个开发板的 ethaddr 是不同的,否则通信会有问题。

我的一般设置如下:

1
2
3
4
5
6
7
8
9
=> setenv ipaddr 192.168.10.102
=> setenv ethaddr b8:ae:1d:01:00:00
=> setenv gatewayip 192.168.10.1
=> setenv netmask 255.255.255.0
=> setenv serverip 192.168.10.101
# 最后再保存一下环境变量到 mmc
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done

2. ping

2.1 使用格式

我们的开发板的网络能否使用,是否可以和服务器 ( Ubuntu 主机 )进行通信,可以通过 ping 命令验证,直接 ping 服务器的 IP 地址即可,一般格式如下:

1
ping ip_addr

【参数说明】

  • ip_addr :表示我们要测试是否可联通的的 ip 地址。

【注意】

(1)只能在 uboot 中 ping 其他的机器,其他机器不能 ping uboot ,因为 uboot 没有对 ping 命令做处理,如果用其他的机器 ping uboot 的话会失败。

2.2 使用实例

点击查看详情

比如我的服务器 IP 地址为 192.168.10.100 ,那么命令格式如下:

1
ping 192.168.10.100

如果连接成功的话,会有以下信息提示:

1
2
3
4
=> ping 192.168.10.100
FEC1 Waiting for PHY auto negotiation to complete... done
Using FEC1 device
host 192.168.10.100 is alive

如果连接不成功(服务器没开启或者说 IP 不在同一网段的话),可能会有以下提示:

1
2
3
4
5
=> ping 192.168.10.102
Using FEC1 device

ARP Retry count exceeded; starting again
ping failed; host 192.168.10.102 is not alive

3. dhcp

dhcp 用于从路由器获取 IP 地址,前提得开发板连接到路由器上的,如果开发板是和电脑直连的,那么 dhcp 命令就会失效。直接输入 dhcp 命令即可通过路由器获取到 IP 地址。

4. tftp

4.1 使用格式

tftp 命令用于通过网络下载数据到指定的内存地址中, tftp 命令使用的是 TFTP 协议, ubuntu 主机作为 TFTP 服务器。因此需要在 ubuntu 上搭建 TFTP 服务器,之前的时候我们已经搭建并本地验证过了(Linux开发中的《LV10-01-TFTP和NFS》这篇笔记),在 uboot 中使用的格式如下:

1
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]

【参数说明】

  • tftpboot :命令的全称,但是其实我们输入一个 tftp 就足够了。
  • loadAddress :是要下载的文件在内存( DRAM )中的存放地址。
  • hostIPaddr :服务器的 IP 地址,实际就是 ubuntu 主机的 IP 地址,如果说前边我们设置了 serverip 环境变量的话,这里是可以省略的。
  • bootfilename :是要从 ubuntu 中下载的文件这个文件是以服务器中 tftp 目录为根目录的。

4.2 使用实例

点击查看详情

例如,我们前边设置的 tftp 目录为 /home/hk/3tftp ,然后我们需要下载这个文件:

1
/home/hk/3tftp/imx6ull/lcd.bin

这个时候我们使用的命令如下:

1
2
3
=> tftp 80000000 /imx6ull/lcd.bin
# 或者
=> tftp 80000000 192.168.10.101:/imx6ull/lcd.bin

按下 enter 按键之后,我们会得到以下提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
=> tftp 80000000 /imx6ull/lcd.bin
Using FEC1 device
TFTP from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/imx6ull/lcd.bin'.
Load address: 0x80000000
Loading: ###
731.4 KiB/s
done
Bytes transferred = 35953 (8c71 hex)
=> tftp 80000000 192.168.10.101:/imx6ull/lcd.bin
Using FEC1 device
TFTP from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/imx6ull/lcd.bin'.
Load address: 0x80000000
Loading: ###
899.4 KiB/s
done
Bytes transferred = 35953 (8c71 hex)

5. nfs

5.1 命令简介

nfs(Network File System)网络文件系统,通过 nfs 可以在计算机之间通过网络来分享资源,比如我们将 linux 镜像和设备树文件放到 ubuntu 中,然后在 uboot 中使用 nfs 命令将 ubuntu 中的 linux 镜像和设备树下载到开发板的 DRAM 中。这样做的目的是为了方便调试 linux 镜像和设备树,也就是网络调试,通过网络调试是 Linux 开发中最常用的调试方法。

原因是嵌入式 linux开发不像单片机开发,可以直接通过 JLINK 或 STLink 等仿真器将代码直接烧写到单片机内部的 flash 中,嵌入式 Linux 通常是烧写到 EMMC、 NAND Flash、 SPI Flash 等外置 flash 中,但是嵌入式 Linux 开发也没有 MDK, IAR 这样的 IDE,更没有烧写算法,因此不可能通过点击一个“download”按钮就将固件烧写到外部 flash 中。虽然半导体厂商一般都会提供一个烧写固件的软件,但是这个软件使用起来比较复杂,这个烧写软件一般用于量产的。其远没有 MDK、 IAR的一键下载方便,在 Linux 内核调试阶段,如果用这个烧写软件的话将会非常浪费时间,而这个时候网络调试的优势就显现出来了,可以通过网络将编译好的 linux 镜像和设备树文件下载到 DRAM 中,然后就可以直接运行。

5.2 使用格式

首先是需要搭建nfs的环境,可以看这篇笔记:Linux开发中的《LV10-01-TFTP和NFS》。一般使用格式如下:

1
nfs [loadAddress] [[hostIPaddr:]bootfilename]

【参数说明】

  • nfs :命令的全称。
  • loadAddress :是要下载的文件在内存( DRAM )中的保存的地址。
  • hostIPaddr :服务器的 IP 地址,实际就是 ubuntu 主机的 IP 地址,如果说前边我们设置了 serverip 环境变量的话,这里是可以省略的。
  • bootfilename :是要从 ubuntu 中下载的文件,这里似乎是需要一个相对于ubuntu根目录的绝对路径才可以。

5.3 使用实例

点击查看详情

我们在ubuntu的nfs目录中有这样一个文件:

1
/home/hk/4nfs/imx_rootfs/linuxrc

我们现在通过nfs命令将这个文件下载到开发板内存的 0x80000000,可使用如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
=> nfs 80000000 192.168.10.101:/home/hk/4nfs/imx_rootfs/linuxrc 
Using FEC1 device
File transfer via NFS from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/home/hk/4nfs/imx_rootfs/linuxrc'.
Load address: 0x80000000
Loading: #################################################################
#################################################################
########
done
Bytes transferred = 703904 (abda0 hex)
=> nfs 80000000 /home/hk/4nfs/imx_rootfs/linuxrc
Using FEC1 device
File transfer via NFS from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/home/hk/4nfs/imx_rootfs/linuxrc'.
Load address: 0x80000000
Loading: #################################################################
#################################################################
########
done
Bytes transferred = 703904 (abda0 hex)

这里关于后边文件路径问题,我尝试了使用 nfs 目录作为根目录,这样就无法找到相应的文件了,会有如下情况:

1
2
3
4
5
6
7
8
=> nfs 80000000 192.168.10.101:/imx_rootfs/linuxrc
FEC1 Waiting for PHY auto negotiation to complete... done
Using FEC1 device
File transfer via NFS from server 192.168.10.101; our IP address is 192.168.10.102
Filename '/imx_rootfs/linuxrc'.
Load address: 0x80000000
Loading: *** ERROR: Cannot mount
T T

七、文件系统操作

有时候需要在 uboot 中对 SD 卡或者 EMMC 中存储的文件进行操作,这时候就要用到文件操作命令。

1. FAT 格式文件系统

1.1 fatinfo

1.1.1 使用格式

fatinfo 命令用于查询指定 MMC 设备分区的文件系统信息,一般格式如下:

1
fatinfo <interface> [<dev[:part]>]

【参数说明】

  • interface :表示接口,比如 mmc。
  • dev :查询的设备号。
  • part :要查询的分区。

1.1.2 使用实例

点击查看详情

我们要查询 EMMC 分区 1 的文件系统信息,命令如下:

1
2
3
4
5
6
=> fatinfo mmc 1:1
Interface: MMC
Device 1: Vendor: Man 000015 Snr c0fffe2a Rev: 0.6 Prod: 8GTF4R
Type: Removable Hard Disk
Capacity: 7456.0 MB = 7.2 GB (15269888 x 512)
Filesystem: FAT32 "NO NAME "

1.2 fatls

1.2.1 使用格式

fatls 命令用于查询 FAT 格式设备的目录和文件信息,一般格式如下:

1
fatls <interface> [<dev[:part]>] [directory]

【参数说明】

  • interface :表示接口,比如 mmc 。
  • dev :查询的设备号。
  • part :要查询的分区。
  • directory :是要查询的目录。

1.2.2 使用实例

点击查看详情

我们要查询 EMMC 分区 1 的所有目录和文件(正点原子开发板出厂的时候EMMC中有相应的一些文件),命令如下:

1
2
3
4
5
6
7
8
9
10
11
=> fatls mmc 1:1
6785360 zimage
39323 imx6ull-14x14-emmc-4.3-480x272-c.dtb
39323 imx6ull-14x14-emmc-4.3-800x480-c.dtb
39323 imx6ull-14x14-emmc-7-800x480-c.dtb
39323 imx6ull-14x14-emmc-7-1024x600-c.dtb
39323 imx6ull-14x14-emmc-10.1-1280x800-c.dtb
40159 imx6ull-14x14-emmc-hdmi.dtb
40067 imx6ull-14x14-emmc-vga.dtb

8 file(s), 0 dir(s)

1.3 fstype

1.3.1 使用格式

fstype 命令用于查看 MMC 设备某个分区的文件系统格式 ,一般格式如下:

1
fstype <interface> <dev>:<part>

【参数说明】

  • interface :表示接口,比如 mmc 。
  • dev :查询的设备号。
  • part :要查询的分区。

1.3.2 使用实例

点击查看详情

在正点原子 EMMC 核心板上的 EMMC 在出厂的时候默认有 3 个分区,我们来查看一下这三个分区的文件系统格式 ,命令如下:

1
2
3
4
5
6
7
=> fstype mmc 1:0
Failed to mount ext2 filesystem...
** Unrecognized filesystem type **
=> fstype mmc 1:1
fat
=> fstype mmc 1:2
ext4
  • 分区 0 格式未知,因为分区 0 存放的 uboot,并且分区 0 没有格式化,所以文件系统格式未知。

  • 分区 1 的格式为 fat,分区 1 用于存放 linux 镜像和设备树。

  • 分区 2 的格式为 ext4,用于存放 Linux 的根文件系统(rootfs)。

1.4 fatload

1.4.1 使用格式

fatload 命令用于将指定的文件读取到内存中,命令格式如下:,格式如下:

1
fatload <interface> [<dev[:part]>] <addr> <filename> [bytes [pos]]

【参数说明】

  • interface :表示接口,比如 mmc 。
  • dev :设备号。
  • part :分区。
  • addr :要保存到内存中的起始地址。
  • filename :要读取的文件名字。
  • bytes :表示读取多少字节的数据,如果 bytes 为 0 或者省略的话表示读取整个文件。
  • pos :是要读的文件相对于文件首地址的偏移,如果为 0 或者省略的话表示从文件首地址开始读取。

1.4.2 使用实例

点击查看详情

我们将 EMMC 分区 1 中的 zImage 文件读取到 DRAM 中的0x80800000 地址处,命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
=> fatls mmc 1:1 
6785360 zimage
39323 imx6ull-14x14-emmc-4.3-480x272-c.dtb
39323 imx6ull-14x14-emmc-4.3-800x480-c.dtb
39323 imx6ull-14x14-emmc-7-800x480-c.dtb
39323 imx6ull-14x14-emmc-7-1024x600-c.dtb
39323 imx6ull-14x14-emmc-10.1-1280x800-c.dtb
40159 imx6ull-14x14-emmc-hdmi.dtb
40067 imx6ull-14x14-emmc-vga.dtb

8 file(s), 0 dir(s)

=> fatload mmc 1:1 80800000 zImage
reading zImage
6785360 bytes read in 223 ms (29 MiB/s)

可以看出在 223ms 内读取了 6785360 个字节的数据,速度为 29MiB/s,速度是非常快的,因为这是从 EMMC 里面读取的,而 EMMC 是 8 位的,速度就很快了。

1.5 fatwrite

1.5.1 使用格式

fatwrite 命令用于将 DRAM 中的数据写入到 MMC 设备中,命令格式如下:,格式如下:

1
fatwrite <interface> <dev[:part]> <addr> <filename> <bytes>

【参数说明】

  • interface :表示接口,比如 mmc 。
  • dev :设备号。
  • part :分区。
  • addr :要保存到内存中的起始地址。
  • filename :要写入的文件名字。
  • bytes :表示读取多少字节的数据。

【注意】uboot 默认没有使能 fatwrite 命令,需要修改板子配置头文件,例如,mx6ullevk.h、mx6ull_alientek_emmc.h 等等,板子不同,其配置头文件也不同。找到所用的开发板对应的配置头文件然后添加如下一行宏定义来使能 fatwrite 命令:

1
#define CONFIG_FAT_WRITE /* 使能 fatwrite 命令 */

1.5.2 使用实例

点击查看详情

这里没有实际的去测试,使用的命令格式如下:

1
2
tftp 80800000 zImage
fatwrite mmc 1:1 80800000 zImage 6788f8

zImage 大小为 6785272(0X6788f8)个字节(注意,由于开发板系统在不断的更新中,因此zImage 大小不是固定的,一切以实际大小为准)

2. EXT 格式文件系统

uboot 有 ext2 和 ext4 这两种格式的文件系统的操作命令,常用的就四个命令,分别为:ext2load、 ext2ls、 ext4load、 ext4ls 和ext4write。这些命令的含义和使用与 fatload、 fatls 和 fatwrite一样,只是 ext2 和 ext4 都是针对 ext 文件系统的。

八、boot 操作

1.两个环境变量

1.1 boodcmd

bootcmd 环境变量中保存着 uboot 的默认命令,该环境变量其实也叫做自启动环境变量。在我们重启开发板的时候,会有这么一个提示信息:

1
Hit any key to stop autoboot : 3

这里就是 uboot 倒计时,这里的这个时间由环境变量 bootdelay 控制,当倒计时结束之前按下任意按键,就会进入 uboot 的一个命令行的模式,着大盖就类似于我们重装 windows 系统时的 BIOS 系统一样。

当我们在倒计时期间没有按下任何按键的话,倒计时结束以后就会执行 bootcmd 中的命令。这些命令一般用来启动 linu 内核,比如读取 EMMC 或者 NAND Flash 中的 linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 linux 内核。可以在 uboot 启动以后进入命令行设置 bootcmd 环境变量的值。如果 EMMC 或者 NAND 中没有保存 bootcmd 的值,那么 uboot 就会使用默认的值,板子第一次运行 uboot 的时候都会使用默认值来设置 bootcmd 环境变量。

【注意】该环境变量中可以有多条命令,多条命令之间用 \; 分隔开,修改完后我们需要将环境变量进行保存,否则下次重启,我们设置的环境变量就会失效。

【设置实例】

另外,我们可以通过 run 命令来运行这个环境变量中的命令。例如,

1
2
3
4
5
6
7
=> setenv bootcmd 'tftp 80800000 /imx6ull/zImage\;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb\;bootz 80800000 - 83000000'
=> printenv bootcmd
bootcmd=tftp 80800000 /imx6ull/zImage;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb;bootz 80800000 - 83000000
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
=> run bootcmd

1.2 bootargs

bootargs 保存着 uboot 传递给 Linux 内核的参数,也被叫做自启动参数, uboot 不需要使用。在 Linux 内核源码里面有相应的文档讲解如何设置,文档为 Documentation/filesystems/nfs/nfsroot.txt ,后边可以看一下,常见的格式如下:

1
root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
点击查看参数说明

【参数说明】

  • server-ip :服务器 IP 地址,也就是存放根文件系统主机的 IP 地址,那就是 ubuntu 的 IP地址,比如我的 ubuntu 主机 IP 地址为 192.168.10.101。
  • root-dir : 根文件系统的存放路径,比如我的就是 /home/hk/4nfs。
  • nfs-options : NFS 的其他可选选项,一般不设置。
  • client-ip : 客户端 IP 地址,也就是开发板的 IP 地址, Linux 内核启动以后就会使用此 IP 地址来配置开发板。注意此地址一定要和 ubuntu 主机在同一个网段内,并且没有被其他的设备使用,在 ubuntu 中使用 ping 命令 ping 一下就知道要设置的 IP 地址有没有被使用,如果不能 ping 通就说明没有被使用,那么就可以设置为开发板的 IP 地址,比如我的开发板设置为192.168.10.102。
  • server-ip : 服务器 IP 地址,就是ubuntu的IP地址,我的是192.168.10.101。
  • gw-ip : 网关地址,我的是 192.168.10.1。
  • netmask :子网掩码,我的是 255.255.255.0。
  • hostname :客户机的名字,一般不设置,此值可以空着。
  • device : 设备名,也就是网卡名,一般是 eth0, eth1….,正点原子的 I.MX6U-ALPHA 开发板的 ENET2 为 eth0, ENET1 为 eth1。如果电脑只有一个网卡,那么基本只能是 eth0,这里我们使用 ENET2,所以网卡名就是 eth0。
  • autoconf : 自动配置,一般不使用,所以设置为 off。
  • dns0-ip : DNS0 服务器 IP 地址,不使用。
  • dns1-ip : DNS1 服务器 IP 地址,不使用。

根据上边这个格式,我们的bootargs参数可以设置为:

1
root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/imx_rootfs,proto=tcp rw ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off

其中 proto=tcp 表示使用 TCP 协议, rw 表示 nfs 挂载的根文件系统为可读可写 。

【设置实例】

最终我们设置的bootargs参数为:

1
2
3
4
5
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/imx_rootfs,proto=tcp rw ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc'
=> printenv bootargs bootargs=console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/imx_rootfs,proto=tcp rw ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off
=> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done

【命令说明】

  • console
    console 用来设置 linux 终端(或者叫控制台),也就是开发板安装好 linux 后,我们通过什么设备来和 Linux 进行交互,是串口还是 LCD 屏幕?如果是串口的话串口号是多少等。这里设置 console 为 ttySAC2 。 ttySAC2 后面有个 ,115200 ,这是设置串口的波特率, console=ttySAC2,115200 综合起来就是设置 ttySAC2 作为 Linux 的终端,并且串口波特率设置为 115200 。

  • init

init 用来指定 init 进程的程序路径,一般 init=/linuxrc , 或者 init=/etc/preinit , preinit 的内容一般是创建 console 、 null 设备节点,运行 init 程序,挂载一些文件系统等操作。我们可能以为 init=/linuxrc 是固定写法,其实不然, /linuxrc 指的是 / 目录下面的 linuxrc 脚本,一般指向 busybox 。

2. bootz

要启动 Linux,需要先将 Linux 镜像文件拷贝到 DRAM 中,如果使用到设备树的话也需要将设备树拷贝到 DRAM 中。可以从 EMMC 或者 NAND 等存储设备中将 Linux 镜像和设备树文件拷贝到 DRAM,也可以通过 nfs 或者 tftp 将 Linux 镜像文件和设备树文件下载到 DRAM 中。不管用那种方法,只要能将 Linux 镜像和设备树文件存到 DRAM 中就行,然后使用 bootz 命令来启动, bootz 命令用于启动 zImage 镜像文件

2.1 使用格式

一般使用格式如下:

1
bootz [addr [initrd[:size]] [fdt]]

【参数说明】

  • addr :Linux 镜像文件在 DRAM 中的位置
  • initrd :是 initrd 文件在DRAM 中的地址,如果不使用 initrd 的话使用‘ - ’代替即可。
点击查看什么是 initrd

Linux初始RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个 initrd RAM磁盘卸载,并释放内存。在很多嵌入式Linux系统中,initrd 就是最终的根文件系统。

  • fdt :就是设备树文件在 DRAM 中的地址。

2.2 使用实例

2.2.1 通过网络下载文件

点击查看详情

首先我们在ubuntu中准备好所需文件:

1
2
/home/hk/3tftp/imx6ull/zImage                # linux镜像文件
/home/hk/3tftp/imx6ull/imx6ull-14x14-evk.dtb # 设备树文件

然后我们下载相应文件到开发板指定位置:

1
2
3
=> tftp 80800000 /imx6ull/zImage
=> tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb
=> bootz 80800000 - 83000000

所有命令运行结果如下:

image-20221013193827596

2.2.2 通过EMMC启动

我们要从 EMMC 中启动 Linux 系统的话只需要使用命令 fatload 将 zImage 和 imx6ull-14x14-evk.dtb 从EMMC 的分区 1 中拷贝到 DRAM 中,然后使用命令 bootz 启动即可。

先使用命令 fatls 查看要下 EMMC 的分区 1 中有没有 Linux 镜像文件和设备树文件,如果没有的话就使用 fatwrite 命令将服务器tftp目录下的中的 zImage 和 imx6ull-14x14-evk.dtb 文件下载到内存,然后烧写到 EMMC 的分区 1 中。

EMMC中有相应的linux镜像文件和设备树文件后,我们就可以使用命令 fatload 将 zImage 和 imx6ull-14x14-evk.dtb文件拷贝到 DRAM 中,地址分别为 0x80800000 和 0x83000000,最后使用 bootz 启动。

1
2
3
fatload mmc 1:1 80800000 zImage
fatload mmc 1:1 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000

3. bootm

3.1 使用格式

boom 命令也是用于启动linux系统的,不使用设备树时,一般使用格式如下:

1
bootm [addr [arg ...]]

【参数说明】

  • addr 是 linux 镜像在 DRAM 中的首地址。
  • arg :可以是一个 initrd 映像的地址,当引导一个需要设备树的 Linux 内核时,需要第三个参数,它是设备树的地址。要在不使用 initrd 镜像的情况下引导内核,我们需要使用 - 作为第二个参数。

其实有的资料中,该命令格式是这样的,我感觉更加详细一点:

1
bootm [addr [initrd[:size]] [fdt]]

【参数说明】

  • addr :是 Linux 镜像文件在 DRAM 中的位置,
  • initrd :是 initrd 文件在 DRAM 中的地址,如果不使用 initrd 的话使用 - 代替即可 。
  • fdt :就是设备树文件在 DRAM 中的地址。

【注意】若不使用相应的地址,对应的位置写 - 。

或者直接这样写可能更清晰一点:

1
bootm kernel-addr ramdisk-addr dtb-addr

【参数说明】

  • kernel-addr : 内核的下载地址 。
  • ramdisk-addr : 根文件系统的下载地址。
  • dtb-addr : 设备树的下载地址。

【注意】若不使用相应的地址,对应的位置写 - 。

3.2 使用实例

与bootz一样。

4. boot

4.1 使用格式

boot 命令也是用来启动 Linux 系统的,只是 boot 会读取环境变量 bootcmd 来启动 Linux 系统,一般使用格式如下:

1
boot

【注意】它启动linux的时候需要借助 bootcmd 环境变量。

4.2 使用实例

点击查看详情

使用该命令的时候,我们需要输入的命令有:

1
2
3
setenv bootcmd 'tftp 80800000 /imx6ull/zImage\;tftp 83000000 /imx6ull/imx6ull-14x14-evk.dtb\;bootz 80800000 - 83000000'
saveenv
boot