一、Uboot是什么?
Linux要启动必须要有bootloader程序。芯片在上电之后先运行bootloader程序,bootloader程序初始化DDR等外设。然后将内核从存储介质(SD,NAND,NOR FLASH,MMC等)中拷贝到DDR中。然后启动linux内核。Uboot属于bootloader程序的一种。
Uboot有三种:(1)uboot 官方的源码;(2)半导体厂商根据uboot官方的源码自己维护的代码;(3)采用半导体厂商芯片的开发板厂商,根据半导体厂商的uboot代码,自己维护的代码。三者是递进迭代的。
二、uboot编译和命令使用
(1)Uboot的编译
我使用的是正点原子的阿尔法开发板。几天来终于把uboot的使用按照教程过了一遍。初学linux,跌跌撞撞踩了很多坑。现在回顾一下。作为记录。
首先进行的是U-BOOT的初次编译。在这第一步我就卡了很久很久。我这里使用的是512MB(DDR3)+8GB(EMMC)核心板。
我按照要求把uboot源码的压缩包放到指定的文件夹,并解压之后。执行三条指令uboot源码进行编译。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
这一行ARCH=arm为指定ARM架构。CROSS_COMPILE=arm-linux-gnueabihf指定交叉编译器为gnueabihf。 distclean为清除工程。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- (加空格) mx6ull_14x14_ddr512_emmc_defconfig
这里面有两个坑。第一是指出来的空格。我在自行操作的时候发现了这个问题。改正过来了;第二是14x14的“x”号,是英文字母x。我之前 一直输入算数×号,导致系统一直提示找不到这个文件。最后去问了正点原子的客服,技术客服远程操作,自己打完文件名称,没有问题。后来我才发现问题出现在这里。是个大坑啊。
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
第三行是-j12是指用12核来编译uboot。
这三条指令执行完毕之后,uboot就算编译成功了。
此外正点原子还提供了使用shell文件来进行编译的方法。为了图省事,我在windows系统下面编写了shell文件中的内容,通过TFP传送到ubuntu系统。在执行shell文件的时候总是出错。
后来去网上查了原因。我在windows系统中建立的文件是dos格式的。dos格式下每一行的结尾是\r\n, 而unix格式下行的结尾是\n。所以执行会报错。文件需要转换成unix格式的。使用dos2unix filename函数转换之后。shell就可以执行了。
接着使用正点原子的imxdownload来下载到SD卡,将SD卡插入到开发板上,并且设置开发板从SD卡启动。
(2)U-Boot 命令使用
输入”help”或者“?”可以查看当前Uboot支持的命令。
输入“help(或?) 命令名”既可以查看命令的详细用法。
1.信息查询命令
bdinfo
这一命令用于查看板子的信息。
printenv
这是我用到最多的一个命令,用于查看环境信息。环境信息里面有很多需要配置的变量。尤其是在网络命令的时候,需要配置本地IP地址,主机IP地址,MAC地址,子网掩码。
上图是环境变量的部分信息
version
这一命令用于查看ubuntu的版本号。
我的ubuntu的版本信息
可以看到版本日期是2016.03,编译日期是2020年5月3日12:35:27 东八时区。这还包含了编译器信息。
2.环境变量操作命令
修改环境变量用到setenv和saveenv命令。
setenv 环境变量 变量值.
系统开启的时候会将环境变量从flash中读取到SDRAM中,使用setenv命令修改的环境变量是在SDRAM中的环境变量。使用saveenv命令可以保存修改后的环境变量到flash中去。下次开机的时候,就会使用修改后的环境变量。
要修改的环境变量值有空格的时候要用单引号括起来‘’。
新建环境变量值和删除环境变量也使用setenv命令。
新建环境变量格式为:
setenv 新的环境变量 环境变量值
删除环境变量格式为:
setenv 要删除的环境变量
3.内存操作命令
md
该命令用于显示内存的数值。格式如下。
md[.b, .w, .l] address [# of objects]。b,w,l三选一,表示的是数值显示的单位,b是指以一个字节为单位显示,w是以一个字(两个字节为显示单位),l是以四个字节为显示单位。address为要查看的内存起始地址。[# of objects]指的是显示变量的数量。表示多少个指定格式的数据。如果数据格式指定为b,数量为字节的数量;如果数据格式指定为w,数量为字的数量;如果数据格式指定为l,数量为long的数量;
ubuntu中的数字都是十六进制的。不是十进制的。
比如你想查看以 0X80000000 开始的 20 个字节的内存值,显示格式为.b 的话使用:
md.b 80000000 14
比如你想查看以 0X80000000 开始的 40 个字节的内存值,显示格式为.w 的话使用:
md.w 80000000 14
比如你想查看以 0X80000000 开始的 80 个字节的内存值,显示格式为.l 的话使用:
md.l 80000000 14
nm
该命令用于修改指定地址的内存值。
nm [.b, .w, .l] address
[.b, .w, .l]用于指定操作格式。address用于指定要修改的地址。
按照格式输入后,会显示地址和地址现有的数据值,最后是一个?号。直接输入修改后的值,按回车即可修改。这种修改方式地址不自增,按输入q,按回车退出修改;
mm
使用该命令修改内存值时,地址会自增。其他的格式同nm命令。
mw
该命令为使用一个指定的数据,重复填充一段指定的内存。
mw [.b, .w, .l] address value [count]
[.b, .w, .l]和address的作用和上面几个命令是一样的。value是要填充的值, [count]是要填充的数量。
实例:mw.l 80000000 0A0A0A0A 10为以long的格式从80000000地址处开始,填充10个0a0a0a0a数值。
cp
cp是数据拷贝命令。从指定地址以指定格式拷贝指定数量的数据到另一个地址。
cp [.b, .w, .l] source target count
[.b, .w, .l]指定数据格式;source指定源地址;target指定目标地址;count指定要拷贝的数据的数量。
示例:cp.l 80000000 80000100 10为 从80000000地址处以long的格式拷贝16个数据到80000100地址处。
cmp
该命令用于比较两段内存值是否相等。
cmp [.b, .w, .l] addr1 addr2 count
[.b, .w, .l]为指定数据格式;addr1第一个地址;addr2第二个地址;count要比较的数据的数量。
4.网络命令
网络调试命令有ping,dhcp,nfs,tftp.
进行网络调试之前首先要配置环境变量。
setenv ipaddr 开发板ip地址。这个地址要与电脑上的ubuntu主机在同一个网段内。
setenv ethaddr 00:04:9f:04:d2:35 这个是MAC地址。如果同一个网段内有多个板子,要保证MAC地址是不一样的。
setenv serverip 服务器ip地址。这个地址是主机ubuntu的ip地址。
setenv gatewayip 网关ip
setenv netmask 子网掩码
配置完成之后保存环境变量即可。
ping命令
这是我碰到的第一个网络命令的难点。我在一开始自己照着教程ping的时候,完全没有ping通过,即使是在开发板和电脑都使用网线连接路由器的情况下。
ping不通的结果
后来还是找了技术客服来解决。首先是因为我的开发板ip地址和ubuntu的地址不在同一个网段内。然后主机ubuntu的网络使用桥接模式。解决了这两点,就可以ping通了。
如果要使用TFP传输文件的时候,主机ubuntu的网络模式要切换到NAT格式。IPV4的设置修改为自动DHCP模式。
dhcp
该命令是开发板向路由器请求分配ip地址。
nfs
nfs(Network File System)网络文件系统。通过nfs可以在计算机之间通过网络在分享资源。
nfs [loadAddress] [[hostIPaddr:]bootfilename]
[loadAddress]为要保存的DRAM地址,[[hostIPaddr:]bootfilename]是要下载的文件地址。
tftp
走到这里又是一个坑。在教程里先下载并搭建tftp服务器,建立tftp文件夹。然后配置tftp。这个文档里面说“安装完成以后新建文件/etc/xinetd.d/tftp,如果没有/etc/xinetd.d 目录的话自行创建。”我以为是自己建立一个etc文件夹。就自己建立了etc/xinetd.d/tftp文件。并在文件中添加配置内容。启动tftp服务,修改/etc/default/tftpd-hpa文件。将建立的tftpboot文件路径添加进去。
然后将镜像文件复制到tftpboot文件夹中,给予文件和文件夹相应的权限。执行tftp下载命令。
是不通的!
又检查了好几遍,确认是不通的。
我自己实在不知道是怎么回事。完全按照教程上来的。为什么会有这样的结果?
无奈之下,再一次去找客服。客服远程鼓捣了一通之后,发现我建立的配置文件是不对的。etc文件夹是在根目录中已有的,只是没有显示出来。自己去建立一个etc文件夹,然后添加配置文件,肯定是不能用的了。
5.emmc和SD卡操作命令
mmc info
该命令用于输出当前选中的 mmc info 设备的信息
mmc rescan
扫描开发板上的所有mmc设备。
mmc list
mmc list 命令用于来查看当前开发板一共有几个 MMC 设备。
mmc dev
mmc dev 命令用于切换当前 MMC 设备。
mmc dev [dev] [part]
[dev]指设备号,0为SD卡,1为emmc,[part]指分区号,默认为0。
mmc part
查看当前磁盘分区。
mmc read 命令
mmc read 命令用于读取 mmc 设备的数据,命令格式如下:
mmc read addr blk# cnt
addr是读取到DRAM中的地址,blk 是要读取的块起始地址(十六进制),一个块是512字节。cnt为要读取的块的数量。
mmc write 命令
将数据从DRAM写入到mmc设备里面 。
mmc write addr blk# cnt
addr为要写入的数据在DRAM中的地址。blk为要写入mmc的块起始地址,一个块为512个字节。
mmc write 可以通过uboot来更新uboot。先使用tftp命令将uboot下载到DRAM中去。然后使用mmc write 命令将uboot写入到SD卡。
mmc erase
mmc擦除命令。mmc erase blk# cnt
blk为要擦除的块起始地址,cnt为要擦除的块的数量。
6.FAT格式文件系统操作命令
Fatinfo
fatinfo
interface表示接口,dev表示要查询的设备号,part为要查询的分区
fatls
用于查询 FAT 格式设备的目录和文件信息
fatls
比如fatls mmc 1:1是要查询emmc分区1的目录和文件信息。
fstype
fstype 用于查看 MMC 设备某个分区的文件系统格式。
fstype
fatload 命令用于将指定的文件读取到 DRAM 中,命令格式如下:
fatload
Interface为设备号。dev[:part]设备号和分区;addr为保存在DRAM中的起始地址;filename为要读取的文件的名字;bytes表示读取多少个字节的数据,byte为0表示读取整个文件。Pos为读取地址相对于文件首地址的偏移量,如果为0的情况下表示从首地址开始读取。
7. EXT 格式文件系统操作命令
uboot 有 ext2 和 ext4 这两种格式的文件系统的操作命令,常用的就四个命令,分别为:
ext2load、ext2ls、ext4load、ext4ls 和 ext4write。这些命令的含义和使用与 fatload、fatls 和 fatwrit一样,只是 ext2 和 ext4 都是针对 ext 文件系统的。
比如EMMC分区2是EXT4格式的。所以可以使用ext4ls来查看文件系统
ext4ls mmc 1:2
8.BOOT 操作命令
bootz
bootz [addr [initrd[:size]] [fdt]]
addr为镜像文件起始地址,initrd是initrd文件在DRAM中的起始地址,不用可以用“-”代替;fdt为设备树文件在DRAM中的起始地址。
使用网络启动linux
tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dt
bootz 80800000 – 83000000
从EMMC中启动linux
fatload mmc 1:1 80800000 zImage
fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dt
bootz 80800000 – 83000000
在这里文档又出现了一个错误(1.3版本的),文档中说设备树文件的名字是imx6ull-alientek-emmc.dtb但是我在网盘里翻了很久,也没有找到这个文件名对应的文件。后来去了B站,看视频发现操作的时候用的不是这个名字。是imx6ull-14x14-emmc-7-1024x600-c.dtb这个名字。
文档出错简直是要新手的命啊。
bootm
bootm用于启动uImage 镜像。。
boot
boot命令引用bootcmd变量来启动linux。将启动方式放在bootcmd中,直接使用boot命令即可使用该方式启动linux。bootcmd中可以存放任意一种启动方式。
9.其他命令
reset
重启uboot
go
跳到指定的地址处直接执行应用。
run
运行环境变量中定义的命令。
mtest
内存读写测试命令。
mtest [start [end [pattern [iterations]]]] start是起始地址,end为结束地址。