U-Boot使用实验
U-Boot简介
Linux启动简介以及Bootloader
linux系统从软件角度看,可以分为以下四个层次。
- 引导加载程序,包括固化固件的boot代码和Bootloader两个部分。大多数是没有固件的,Bootloader是上电后执行后的第一个程序。
- Linux内核,特定于嵌入式板子的定制内核以及内核的启动参数,启动参数可以是内核默认的或者Bootloader传递的。
- 文件系统,博爱阔根文件系统和建立于flash内存上的文件系统,包含了Linux系统能过运行所需要的应用程序、库等。
- 用户应用程序,存储于文件系统中,又是在用户应用程序和内核层之间可能包含一个嵌入式图形用户界面,常用的GUI有:Qtopia和Mini GUI等。
Bootloader存放参数如串口,IP地址等,正常启动Bootloader运行,它将kernel(内核)复制到内存中,并且在内存的固定地址设置好传递的内核参数,运行内核,内核启动后挂在根文件系统。
Bootloader启动要分两过程,第一个使用汇编,利用其完成CPU体系结构的初始化(硬件设备初始化,未加载第二过程准备RAM空间,复制Bootloader第二过程代码到RAM中,设置好栈,跳转到C的入口),并调第二过程代码,第二过程使用C语言来实现(初始化使用的硬件设备,检测系统内存映射,将内核映像和根文件系统映像从Flash读到RAM中,为内核设置启动参数,调用内核)。
调用内核前必须满足:
CPU 寄存器的设置
R0=0
R1=机器类型的ID
R2=启动参数标记列表在RAM中的起始基地址
CPU工作模式
必须禁止中断
CPU必须为SVC 模式
Cache和MMU的设置
MMU必须关闭
指令Cache可开可关
数据Cache必须关
Uboot是通用Bootloader,可以引导多种架构的CPU,具有开源代码,支持多种嵌入式操作系统内核,支持多种处理器系列,有较高的可靠性和稳定性,高度灵活的功能设置,有丰富的设备驱动源码,有调试文档和技术支持,支持NFS挂载,支持目标板环境变量多种存储方式,上电自检。
使用Uboot
将Uboot压缩包在Ubuntu上解压后使用以下命令来编译
1 | make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean |
这三条命令中 ARCH=arm 设置目标为 arm 架构,CROSS_COMPILE 指定所使用的交叉编译器。第一条命令相当于“make distclean”,目的是清除工程,一般在第一次编译的时候最好清理一下工程。第二条指令相当于“make mx6ull_14x14_ddr512_emmc_defconfig”,用于配置 uboot,配置文件为 mx6ull_14x14_ddr512_emmc_defconfig。最后一条指令相当于 “make -j12”也就是使用 12 核来编译 uboot。
之后使用imxdownload将uboot.bin下载进入sd卡,之后将板子的uart1接上ttl转usb,之后打开MobaXterm。设置好参数后,复位linux板子,之后按下回车键,进入uboot命令行。
boot指令使用
信息查询命令
1 | bdinfo //用于查看板子信息 |
环境变量操作命令
1 | saveenv //保存修改后的环境变量、 |
一般环境变量是存放在外部 flash 中的,uboot 启动的时候会将环境变量从 flash 读取到 DRAM 中。所以使用命令 setenv 修改的是 DRAM中的环境变量值,修改以后要使用 saveenv 命令将修改后的环境变量保存到 flash 中,否则的话uboot 下一次重启会继续使用以前的环境变量值。
也可以使用setenv来删除环境变量
1 | set author |
上面命令中通过 setenv 给 author 赋空值,也就是什么都不写来删除环境变量 author。重启uboot 就会发现环境变量 author 没有了。
内存操作命令
md 命令用于显示内存值,格式如下:
1 | md[.b, .w, .l] address [# of objects] |
命令中的[.b .w .l]对应 byte、word 和 long,也就是分别以 1个字节、2 个字节、4 个字节来显示内存值。address 就是要查看的内存起始地址,[# of objects]表示要查看的数据长度。
Uboot命令中的数字是十六进制的。
1 | md.b 80000000 14 以 0X80000000 开始的 20 个字节的内存值 |
nm 命令用于修改指定地址的内存值,命令格式如下:
1 | nm [.b, .w, .l] address |
nm 命令同样可以以.b、.w 和.l 来指定操作格式,比如现在以.l格式修改 0x80000000 地址的数据为 0x12345678。输入命令:
1 | nm.l 80000000 |
80000000 表示现在要修改的内存地址,0500e031 表示地址0x80000000 现在的数据,?后面就可以输入要修改后的数据 0x12345678,输入完成以后按下回车,然后再输入‘q’即可退出
mm 命令也是修改指定地址内存值的,使用 mm 修改内存值的时候地址会自增。比如以.l 格式修改从地址 0x80000000 开始的连续 3 个内存块(3*4=12个字节)的数据为 0X05050505。
命令 mw 用于使用一个指定的数据填充一段内存,命令格式如下:
1 | mw [.b, .w, .l] address value [count] |
mw 命令同样可以以.b、.w 和.l 来指定操作格式,address 表示要填充的内存起始地址,value为要填充的数据,count 是填充的长度。比如使用.l 格式将以 0X80000000 为起始地址的 0x10 个内存块(0x10 * 4=64 字节)填充为 0X0A0A0A0A
1 | mw.l 80000000 0A0A0A0A 10 |
cp 是数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 Nor Flash 中的数据拷贝到 DRAM 中。
1 | cp [.b, .w, .l] source target count |
cp 命令同样可以以.b、.w 和.l 来指定操作格式,source 为源地址,target 为目的地址,count为拷贝的长度。我们使用.l 格式将 0x80000000 处的地址拷贝到 0X80000100 处,长度为0x10 个内存块(0x10 * 4=64 个字节),
1 | cp.l 80000000 80000100 10 |
cmp 是比较命令,用于比较两段内存的数据是否相等
1 | cmp [.b, .w, .l] addr1 addr2 count |
addr1 为第一段内存首地址,addr2 为第二段内存首地址,count 为要比较的长度。
网络操作命令
将开发板的enet2接口和wifi或者电脑连接,使用setenv和saveenv命令来设置网络的ip相关参数。
ping 命令
开发板的网络能否使用,是否可以和服务器(Ubuntu 主机)进行通信,通过 ping 命令就可以验证,直接 ping 服务器的 IP 地址即可。
1 | ping 192.168.1.115 |
dhcp 命令
dhcp 用于从路由器获取 IP 地址,前提得开发板连接到路由器上的,如果开发板是和电脑直连的,那么 dhcp 命令就会失效。直接输入 dhcp 命令即可通过路由器获取到 IP 地址.
nfs 命令
nfs(Network File System)网络文件系统,通过 nfs 可以在计算机之间通过网络来分享资源,这样做的目的是为了方便调试 linux 镜像和设备树,也就是网络调试,通过网络调试是 Linux 开发中最常用的调试方法。在 Linux 内核调试阶段,如果用这个烧写软件的话将会非常浪费时间,而这个时候网络调试的优势就显现出来了,可以通过网络将编译好的 linux 镜像和设备树文件下载到 DRAM 中,然后就可以直接运行。
一般使用 uboot 中的 nfs 命令将 Ubuntu 中的文件下载到开发板的 DRAM 中,在使用之前需要开启 Ubuntu 主机的 NFS 服务,并且要新建一个 NFS 使用的目录,以后所有要通过NFS 访问的文件都需要放到这个 NFS 目录中。
1 | nfs [loadAddress] [[hostIPaddr:]bootfilename] |
loadAddress 是要保存的 DRAM 地址[[hostIPaddr:]bootfilename]是要下载的文件地址。
Ubuntu20.04具体参考以下方法:
(嵌入式Linux开发——解决uboot无法使用nfs服务从ubuntu中下载文件(TTT、cannot mount等错误)_
EMMC 和 SD 卡操作命令
如果要在 uboot 中更新 SD对应的 uboot,可以使用如下所示命令:
1 | mmc dev 0 0 //切换SD卡的0区 |
如果要在 uboot 中更新 EMMC 对应的 uboot,可以使用如下所示命令:
1 | mmc dev 1 0 //切换到 EMMC 分区 0 |
FAT 格式文件系统操作命令
fatinfo 命令
fatinfo 命令用于查询指定 MMC 设备分区的文件系统信息
1 | fatinfo <interface> [<dev[:part]>] |
interface 表示接口,比如 mmc,dev 是查询的设备号,part 是要查询的分区。比如我们要查询 EMMC 分区 1 的文件系统信息,命令如下:
1 | fatinfo mmc 1:1 |
fatls 命令
fatls 命令用于查询 FAT 格式设备的目录和文件信息,命令格式如下:
1 | fatls <interface> [<dev[:part]>] [directory] |
interface 是要查询的接口,比如 mmc,dev 是要查询的设备号,part 是要查询的分区,directory是要查询的目录。
fstype 命令
fstype 用于查看 MMC 设备某个分区的文件系统格式
1 | fstype <interface> <dev>:<part> |
fatload命令
1 | fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]] |
interface 为接口,比如 mmc,dev 是设备号,part 是分区,addr 是保存在 DRAM 中的起始地址,filename 是要读取的文件名字。bytes 表示读取多少字节的数据,如果 bytes 为 0 或者省略的话表示读取整个文件。pos 是要读的文件相对于文件首地址的偏移,如果为 0 或者省略的话表示从文件首地址开始读取。
fatwrite命令
uboot 默认没有使能 fatwrite 命令,需要修改板子配置头文件
1 | #define CONFIG_FAT_WRITE /* 使能 fatwrite 命令 */ |
fatwirte 命令用于将 DRAM 中的数据写入到 MMC 设备中,命令格式如下:
1 | fatwrite <interface> <dev[:part]> <addr> <filename> <bytes> |
interface 为接口,比如 mmc,dev 是设备号,part 是分区,addr 是要写入的数据在 DRAM中的起始地址,filename 是写入的数据文件名字,bytes 表示要写入多少字节的数据。我们可以通过 fatwrite 命令在 uboot 中更新 linux 镜像文件和设备树。
EXT 格式文件系统操作命令
常用的就四个命令,分别为:ext2load、ext2ls、ext4load、ext4ls 和 ext4write,这些命令的含义和使用与 fatload、fatls 和 fatwrite一样,只是 ext2 和 ext4 都是针对 ext 文件系统的。
Boot操作命令
bootz命令
要启动 Linux,需要先将 Linux 镜像文件拷贝到 DRAM 中,如果使用到设备树的话也需要将设备树拷贝到 DRAM 中。可以从 EMMC 或者 NAND 等存储设备中将 Linux 镜像和设备树文件拷贝到 DRAM,也可以通过 nfs 或者 tftp 将 Linux 镜像文件和设备树文件下载到 DRAM 中。
之后就可以使用bootz来启动zImage镜像文件:
1 | bootz [addr [initrd[:size]] [fdt]] |
命令 bootz 有三个参数,addr 是 Linux 镜像文件在 DRAM 中的位置,initrd 是 initrd 文件在DRAM 中的地址,如果不使用 initrd 的话使用‘-’代替即可,fdt 就是设备树文件在 DRAM 中的地址。
bootm 命令
bootm 和 bootz 功能类似,但是 bootm 用于启动 uImage 镜像文件。如果不使用设备树的话启动 Linux 内核的命令如下:
1 | bootm addr |
addr 是 uImage 镜像在 DRAM 中的首地址。
如果要使用设备树,那么 bootm 命令和 bootz 一样,命令格式如下:
1 | bootm [addr [initrd[:size]] [fdt]] |
其中 addr 是 uImage 在 DRAM 中的首地址,initrd 是 initrd 的地址,fdt 是设备树(.dtb)文件在 DRAM 中的首地址,如果 initrd 为空的话,同样是用“-”来替代。
boot 命令
boot 命令也是用来启动 Linux 系统的,只是 boot 会读取环境变量 bootcmd 来启动 Linux 系统,bootcmd 是一个很重要的环境变量!其名字分为“boot”和“cmd”,也就是“引导”和“命令”,说明这个环境变量保存着引导命令,其实就是启动的命令集合,具体的引导命令内容是可以修改的。比如我们要想使用 tftp 命令从网络启动 Linux 那么就可以设置 bootcmd 为“tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 -83000000”,然后使用 saveenv 将 bootcmd 保存起来。然后直接输入 boot 命令即可从网络启动Linux 系统,命令如下:
1 | setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb; bootz 80800000 - 83000000' |
其他常用命令
reset命令
输入reset即可实现复位重启。
go 命令
go 命令用于跳到指定的地址处执行应用
1 | go addr [arg ...] |
run 命令
run 命令用于运行环境变量中定义的命令,比如可以通过“run bootcmd”来运行 bootcmd 中的启动命令,但是 run 命令最大的作用在于运行我们自定义的环境变量。在后面调试 Linux 系统的时候常常要在网络启动和 EMMC/NAND 启动之间来回切换,而 bootcmd 只能保存一种启动方式,如果要换另外一种启动方式的话就得重写 bootcmd,会很麻烦。这里我们就可以通过自定义环境变量来实现不同的启动方式,比如定义环境变量 mybootemmc 表示从 emmc 启动,定义 mybootnet 表示从网络启动,定义 mybootnand 表示从 NAND 启动。如果要切换启动方式的话只需要运行“run mybootxxx(xxx 为 emmc、net 或 nand)”即可。
mtest 命令
mtest 命令是一个简单的内存读写测试命令,可以用来测试自己开发板上的 DDR,命令格式如下:
1 | mtest [start [end [pattern [iterations]]]] |