U-Boot介绍
1. U-Boot简介
Linux系统要启动就必须需要一个bootloader程序,也就是说芯片上电后先运行一段bootloader程序,这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND/NOR FLASH/SD/MMC等)拷贝到DDR中,最后启动Linux内核。bootloader和Linux内核的关系跟PC上的BIOS和Windows的关系一样,bootloader就相当于BIOS。目前有很多现成的bootloader软件可用,比如U-Boot、vivi、RedBoot等,其中以U-Boot使用最为广泛,接下来将主要介绍U-Boot(uboot)
uboot(Universal Boot Loader),是一个遵循GPL协议的开源软件,是一个裸机代码,可以看作是一个裸机综合例程。uboot官网为http://www.denx.de/wiki/oops/U-Boot/,可在“Source Code”中下载uboot源码
uboot官网上下载的是原汁原味的uboot源码,但是一般不会直接使用uboot官方源码。uboot官方源码是给半导体厂商准备的;半导体厂商会下载官方uboot源码,然后将自家相应的芯片移植进去,也就是说半导体厂商会自已维护一个版本的uboot,例如IMX6U芯片厂商NXP就会维护一个自已的uboot版本;芯片厂商维护的uboot版本主要针对自家评估板的,如果自已做的开发板就需要修改芯片厂商官方的uboot,使其支持开发板厂商生产的评估板
这里使用开发板厂商提供的uboot源码( uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2)来对uboot工程目录进行分析
以EMMC版的核心板为例,以下是uboot的目录,左侧为未编译的源码,右侧为编译后的源码(编译方法见本文第二小节)
编译后的uboot源码比未编译的源码多了许多文件,这些文件或文件夹的含义见下表:
上表中很多文件夹和文件我们都不需要去关心,需要关注的文件夹或文件如下:
- arch文件夹:存放与架构有关的文件,这里只关注arm文件夹即可
打开arch/arm文件夹,里面内容(部分)如下:
– mach开头的文件夹跟具体的设备有关,我们使用的IMX6U,需要关注“imx-common”
– cpu文件夹里是和cpu架构有关的
打开arm/cpu文件夹,里面内容如下:
– armv7文件夹,IMX6U是Cortex-A7内核属于armv7,因此需要关注armv7文件夹
– u-boot.lds是ARM芯片所使用的u-boot链接脚本文件
- board文件夹:和具体的板子有关的
打开board文件夹,里面内容如下:
– freescale文件夹,freescale芯片的板子都在此文件夹下
打开freescale文件夹,找到和mx6u相关的文件夹:
– mx6ul开头的,表示使用IMX6UL芯片的板子
– mx6ull开头的,表示使用IMX6ULL芯片的板子
– mx6ullevk是NCP官方的IMX6ULL开发板
- configs文件夹:存放uboot配置文件,一般半导体或开发板厂商会制作好一个配置文件,供用户在此基础上进行修改,配置文件统一命名为“xxx_defconfig”,xxx表示开发板名字
打开configs文件夹,里面的配置文件如下
– 下图框中的6个文件就是ALPHA开发板对应的uboot默认配置文件
使用"make xxx_defconfig"命令,即可配置uboot
- u-boot.xxx_cmd文件:是一系列的文件,都是编译生成的,是一些命令文件
比如.u-boot.bin.cmd文件,里面存放了一个变量cmd_u-boot.bin
#该变量的作用:拷贝一份u-boot-nodtb.bin文件,并重命名为u-boot.bin
cmd_u-boot.bin := cp u-boot-nodtb.bin u-boot.bin
那么u-boot-nodtb.bin是怎么来的呢?.u-boot-nodtb.bin.cmd就是用于生成u-boot-nodtb.bin的
#用到了arm-linux-gnueabihf-objcopy来将ELF格式的u-boot文件转换为二进制的u-boot-nodtb.bin
cmd_u-boot-nodtb.bin := arm-linux-gnueabihf-objcopy --gap-fill=0xff -j .text......-O binary u-boot u-boot-nodtb.bin
文件u-boot是ELF格式的文件,.u-boot.cmd就是用于生成u-boot的
#用到了arm-linux-gnueabihf-ld.bfd链接工具,将各个built-in.o文件链接在一起形成u-boot文件
cmd_u-boot := arm-linux-gnueabihf-ld.bfd ......-Map u-boot.map
文件u-boot.imx是在u-boot.bin文件的头部添加了IVT/DCD等信息,.u-boot.imx.cmd就是用于生成u-boot.imx的
#用到了工具tools/mkimage,IVT/DCD等数据保存在了文件mximage.cfg.cfgtmp中
#tools/mkimage工具就是读取IVT/DCD等数据并添加到u-boot.bin头部,并生成u-boot.imx
cmd_u-boot.imx := ./tools/mkimage -n board/freescale/mx6ull_alientek_emmc/imximage.cfg.cfgtmp -T imximage -e 0x87800000 -d u-boot.bin u-boot.imx
- Makefile文件:顶层Makefile文件,Makefile是支持嵌套的,也就是顶层Makefile可以调用子目录中的Makefile文件
Makefile嵌套在大项目中很常见,一般大项目里的所有源码都不会放到同一个目录下,各个功能模块的源代码都是分开存放在各自的目录中,每个功能模块目录下都有一个Makefile文件,这个Makefile只处理本模块的编译链接工作,这样所有的编译链接工作就不用全部放到有个Makefile文件中,可以使得Makefile变得简洁明了
- u-boot.xxx文件:也是一系列文件,这些文件的含义如下
– u-boot:编译出来的ELF格式的uboot镜像文件
– u-boot.bin:编译出来的二进制格式的uboot可执行镜像文件
– u-boot.cfg:uboot的另外一种配置文件
– u-boot.imx:u-boot.bin添加头部信息后的文件
– u-boot.lds:链接脚本
– u-boot.map:uboot映射文件,可查看某个函数被链接到哪个地址上了
– u-boot.srec:S-Record格式的镜像文件
– u-boot.sym:uboot符合文件
– u-boot-nodtb.bin:和u-boot.bin一样,u-boot.bin是u-boot-nodtb.bin的复制文件
- .config文件:uboot配置文件,使用命令"make xxx_defconfig"配置uboot后就会自动生成
- README:描述了uboot的详细信息,包括uboot该如何编译、uboot中各文件夹的含义、相应的命令等
2. U-Boot初次编译
- 在Ubuntu中安装ncurses库,以防止编译报错
sudo apt-get install libncurses5-dev
-
创建相应目录,将开发板厂商提供的uboot源码拷贝至此目录中
-
解压uboot源码
tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2
- 使用以下命令编译uboot,本例程使用的是512MB(DDR3)+8GB(EMMC)核心板
#ARCH=arm表示设置目标为arm架构,相当于make distclean,清除工程
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
#配置uboot,配置文件为mx6ull_14x14_ddr512_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
#V=1用于设置编译过程中的信息输出级别,相当于make -j12,即使用12核来编译uboot
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
#每次编译uboot都需要输入一长串命令
#为了简单起见,建立一个shell脚本文件,将这些命令写到shell脚本里
#执行shell脚本即可完成编译工作
#新建mx6ull_alientek_emmc.sh脚本文件
##########################################################################
#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
##########################################################################
#通过命令“./mx6ull_alientek_emmc.sh”编译uboot
3. U-Boot烧写与启动
uboot编译好后就可以烧写到开发板上使用了
- 将imxdownload烧写工具拷贝到文件夹中,并给与可执行权限
chmod 777 imxdownload
- 使用imxdownload软件将bin文件下载到SD卡中
./imxdownload u-boot.bin /dev/sdb
- 烧写成功后,插入SD卡,使用USB线将USB_TTL和电脑相连,打开串口工具,设置好串口参数并打开,最后复位开发板。在出现“Hit any key to stop autoboot”倒计时(3秒)时,按下键盘上的回车键,就会进入uboot命令行模式;否则uboot就会使用默认参数来启动Linux内核了
当进入到uboot命令行模式后,左侧会出现一个“=>”标志,如上图示;上图中uboot启动时会输出一些信息,其含义如下:
– uboot版本号和编译时间
– CPU信息:IMX6ULL芯片,运行在396MHz,工业级,结温为-40~105℃
– 复位原因:POR,是IMX6ULL上的POR_B引脚,拉低即可复位
– Board:开发板名字
– I2C:提示I2C准备就绪
– DRAM:内存为512MB
– MMC:当前有两个MMC/SD卡控制器
– Display:LCD型号,ATK-LCD-7-1024x600(1024x600)
– Video:分辨率1024x600,格式为RGB888(24位)
– In/Out/Err:标准输入/输出和标准错误所使用的中断,以(serial)串口作为终端
– 切换到emmc的第0个分区上,从emmc启动;这里为了方便烧写到SD卡上
– 内心还是emmc的,所以启动后会将emmc作为默认存储器
– Net:网口信息,提示当前使用的是FEC1这个网口
– Error:提示FEC1网卡地址没有设置
– Normal Boot:提示正常启动,uboot要从emmc里面读取环境变量和参数,启动内核了
– 倒计时提示,默认倒计时3秒
4. U-Boot命令
进入uboot命令行模式后输入“help”或“?”,回车后即可查看当前uboot所支持的命令;使用“help cmd”或“? cmd”命令还可以查看命令的具体用法
4.1 信息查询命令
常用的信息查询命令有三个:bdinfo、printenv、version
-
bdinfo:用于查看板子信息
-
printenv:用于输出环境变量信息
-
version:用于查看uboot版本号
4.2 环境变量操作命令
- 修改环境变量:setenv(设置或修改环境变量的值)、saveenv(保存修改后的环境变量)
=> setenv bootdelay 5 #将环境变量bootdelay改为5
=> saveenv #保存修改后,uboot倒计时变为5秒
Saving Environment to MMC... #保存过程提示...
Writing to MMC(0)...done #保存到MMC(0)中,即SD卡中
#有时修改的环境变量值可能会有空格,此时需要用单引号括起来
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
=> saveenv #保存修改
#上面命令相当于设置了"console=ttymxc0,115200","root=/dev/mmcblk1p2","rootwait","rw"四组值
- 新建环境变量:setenv也可用于新建环境变量
=> setenv author andyxi #新建环境变量author,其值为andyxi
=> saveenv #保存修改
- 删除环境变量:使用setenv赋空值,即可删除环境变量
=> setenv author #给环境变量author赋空值,即删除该环境变量
=> saveenv #保存修改
4.3 内存操作命令
内存操作命令就是用于直接对DRAM进行读写操作的,常用的内存操作命令有:md、nm、mm、mw、cp和cmp
- md:用于显示内存值
#md命令格式如下:
md[.b,.w,.l] address [num of objects]
#[.b,.w,.l],对应byte,word和long,即以1/2/4个字节来显示内存值
#address,要查看的内存起始地址
#[num of objects],要查看的数据长度
#例如:查看0X80000000开始的20个字节的内存值,显示格式为.b
=> md.b 80000000 14
#注意:uboot命令中的数字都是十六进制的!!!
- nm:用于修改指定地址的内存值
#nm命令格式如下:
nm[.b,.w,.l] address
#[.b,.w,.l],对应byte,word和long,即以1/2/4个字节为操作格式
#address,要操作的内存起始地址
#例如:以.l格式修改0x80000000地址的数据为0x12345678
=> nm.l 80000000 #输入要修改的地址
80000000: 0500e031 ? 12345678 #返回0x80000000地址现存的数值;? 后面输入新数据
80000000: 12345678 ? q #回车后显示新数据;? 后面输入q即可退出
=>
- mm:用于修改指定地址的内存值,与nm命令的区别是,mm修改内存值时地址会自增
#mm命令格式如下:
mm[.b,.w,.l] address
#[.b,.w,.l],对应byte,word和long,即以1/2/4个字节为操作格式
#address,要操作的内存起始地址
#例如:以.l格式修改0x80000000地址开始的3个内存块的数据为0x05050505
=> mm.l 80000000
80000000: 0500e031 ? 05050505
80000004: 15d84584 ? 05050505
80000008: 2b009906 ? 05050505
8000000c: ae78903f ? q
=>
- mw:用于使用一个指定的数据填充一段内存
#mw命令格式如下:
mw[.b,.w,.l] address value [count]
#[.b,.w,.l],对应byte,word和long,即以1/2/4个字节为操作格式
#address,要填充的内存起始地址
#value,要填充的数据
#count,填充的长度
#例如:以.l格式将以0x80000000为起始地址的0x10个内存块填充位为0x0a0a0a0a
=> mw.l 80000000 0a0a0a0a 10
=> md.l 80000000 10 #使用md命令来查看
- cp:拷贝命令,用于将DRAM中的数据从一段内存拷贝到另一段内存中
#cp命令格式如下:
cp[.b,.w,.l] source target count
#[.b,.w,.l],对应byte,word和long,即以1/2/4个字节为操作格式
#source,源地址
#target,目的地址
#count,拷贝的长度
#例如:以.l格式将0x80000000地址处的数据拷贝到0x80000100处,长度为0x10个内存块
=> cp.l 80000000 80000100 10
- cmp:比较命令,用于比较两段内存的数据是否相等
#cmp命令格式如下:
cmp[.b,.w,.l] addr1 addr2 count
#[.b,.w,.l],对应byte,word和long,即以1/2/4个字节为操作格式
#addr1,第一段内存首地址
#addr2,第二段内存首地址
#count,要比较的长度
#例如:以.l格式来比较0x80000000和0x80000100地址处数据是否相等,比较长度为0x10个内存块
=> cmp.l 80000000 80000100 10
Total of 16 word(s) were the same
4.4 网络操作命令
uboot是支持网络的,在移植uboot时一般都要调通网络功能,因为在移植Linux kernel时需要使用uboot的网络功能做调试。uboot支持大量的网络命令,如dhcp、ping、nfs和tftpboot等
在使用uboot的网络功能之前先用网线将开发板的ENET2接口和电脑或路由器连接起来,IMX6U-ALPHA开发板有两个网口:ENET1和ENET2,需要连接ENT2,连接后设置如下几个环境变量
=> setenv ipaddr 192.168.10.50
=> 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.100
=> saveenv
设置好网络相关的环境变量后就可以使用网络相关的命令了
- ping:开发板的网络是否能使用,是否可以和服务器(Ubuntu)进行通信
=> ping 192.168.10.100
Using FEC1 device
host 192.168.10.100 is alive
=>
-
dhcp:用于从路由器获取IP地址,前提是开发板与路由器连接
-
nfs:网络文件系统,可以在计算机之间通过网络来分享资源
#nfs命令使用的前提条件:开启Ubuntu主机的NFS服务,并创建一个NFS使用的目录
#nfs命令格式如下
nfs [loadAddress] [[hostIPaddr:]bootfilename]
#loadAddress,要保存的DRAM地址
#[hostIPaddr:]bootfilename,要下载的文件地址
#例如:将Ubuntu主机nfs文件夹中的zImage镜像文件下载到开发板DRAM的0x80800000地址处
nfs 80800000 192.168.1.253:/home/andyxi/linux/nfs/zImage
将Linux镜像和设备树文件放到Ubuntu中,然后在uboot中使用nfs命令将Ubuntu中的Linux镜像和设备树下载到开发板的DRAM中。网络调试是Linux开发中最常用的调试方法。
开启NFS服务的方法,详见开发环境搭建第二小节
- tftp:和nfs命令一样,都是通过网络下载东西到DRAM中,使用的TFTP协议,Ubuntu主机作为TFTP服务器
#tftp命令使用的前提条件:开启Ubuntu主机的tftp服务,并创建一个tftp使用的目录
#tftp命令格式如下
tftp [loadAddress] [[hostIPaddr:]bootfilename]
#loadAddress,要保存的DRAM地址
#[hostIPaddr:]bootfilename,要下载的文件地址
#例如:将Ubuntu主机tftp文件夹中的zImage镜像文件下载到开发板DRAM的0x80800000地址处
tftp 80800000 zImage
#注意:与nfs命令的区别是,不需要输入文件在Ubuntu中的完整路径,只需要输入文件名即可
注意:tftp目录和要下载的文件应给与相应的权限(777)
开启TFTP服务的方法,详见开发环境搭建第三小节
4.5 EMMC和SD卡操作命令
uboot支持EMMC和SD卡,因此也要提供EMMC和SD卡的操作命令,这里统一使用MMC来指代EMMC和SD卡,uboot中常用于操作MMC设备的命令为“mmc”,mmc是一系列的命令,其后可以跟不同的参数,以实现不同的功能
4.6 FAT格式文件系统操作命令
有时需要在uboot中对SD卡或EMMC中存储的文件进行操作,这时就要用到文件操作命令,跟文件操作相关的命令有:fatinfo、fatls、fstype、fatload和fatwrite,但是这些命令只能支持FAT格式的文件系统
- fatinfo:用于查询指定MMC设备分区的文件系统信息
fatinfo <interface> [dev[:part]]
#interface 表示接口,如mmc
#dev 表示要查询的设备号
#part 表示要查询的分区
#例如
=> fatinfo mmc 1:1
- fatls:用于查询FAT格式设备的目录和文件信息
fatls <interface> [dev[:part]] [directory]
#interface 表示接口,如mmc
#dev 表示要查询的设备号
#part 表示要查询的分区
#directory 表示要查询的目录
#例如
=> fatls mmc 1:1 #表示查询EMMC分区1中的所有目录和文件
- fstype:用于查看MMC设备某个分区的文件系统格式
fstype <interface> <dev>:<part>
#interface 表示接口,如mmc
#dev 表示要查询的设备号
#part 表示要查询的分区
#例如
=> fstype mmc 1:2 #表示查询EMMC分区2的文件系统格式
- fatload:用于将指定的文件读取到DRAM中
fatload <interface> [dev[:part]] [<addr>[<filename>[bytes[pos]]]]
#interface 表示接口,如mmc
#dev 表示设备号
#part 表示分区
#addr 是保存在DRAM中的起始地址
#filename 是要读取的文件名
#bytes 表示读取多少字节的数据,若为0或省略,表示读取整个文件
#pos 要读取的文件相对于文件首地址的偏移,若为0或省略,表示从首地址开始读取
#例如
=> fatload mmc 1:1 80800000 zImage
#表示将EMMC分区1的zImage文件读取到DRAM中的0x80800000处
- fatwrite:用于将DRAM中的数据写入到MMC设备中
fatwrite <interface> <dev[:part]> <addr> <filename> <bytes>
#interface 表示接口,如mmc
#dev 表示设备号
#part 表示分区
#addr 是要写入的数据在DRAM中的起始地址
#filename 是要写入的数据文件名
#bytes 表示要写入多少字节的数据
#例如
=> fatwrite mmc 1:1 80800000 zImage 6788f8
#表示将大小为6788f8个字节的zImage文件写入到EMMC的分区1的0x80800000起始地址处
4.7 EXT格式文件系统操作命令
uboot有ext2和ext4这两种格式的文件系统的操作命令,常用的四个命令为:ext2load、ext2ls、ext4load、ext4ls和ext4write,这些命令的含义和用到与上一节中的FAT命令一模一样,只是针对ext文件系统的
4.8 NAND操作命令
如果使用的是NAND核心板,uboot也支持NAND Flash,也有一些NAND Flash的操作命令
- nand info:用于打印NAND Flash信息
- nand device:用于切换NAND Flash
- nand erase:用于擦除NAND Flash
- nand write:用于向指定地址写入指定的数据
- nand read:用于从NAND Flash中指定地址读取指定大小的数据到DRAM中
4.9 BOOT操作命令
uboot的本质工作是引导Linux,所以uboot有相关的boot引导命令来启动Linux,常用的boot有关的命令有:bootz、bootm和boot
- bootz:用于启动zImage镜像文件
#bootz命令格式如下:
bootz [addr [initrd[:size]] [fdt]]
#addr Linux镜像文件在DRAM中的位置
#initrd 表示initrd文件在DRAM中的地址,若不使用使用‘-’代替即可
#fdt 设备树文件在DRAM中的地址
#例如:Linux镜像文件和设备树准备好后,使用bootz命令启动Linux
tftp 80800000 zImage #下载zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb #下载设备树
bootz 80800000 - 83000000 #启动Linux
Linux镜像文件或设备树文件拷贝到DRAM里面后,使用bootz命令,启动Linux
- bootm:和bootz功能类似,不过是用于启动uImage镜像文件
#bootm命令格式如下:
bootm [addr [initrd[:size]] [fdt]]
#addr Linux镜像文件在DRAM中的位置
#initrd 表示initrd文件在DRAM中的地址,若不使用使用‘-’代替即可
#fdt 设备树文件在DRAM中的地址
- boot:也是用于启动Linux系统的,只是boot命令会读取环境变量bootcmd来启动Linux
#例如使用tftp命令从网络启动Linux
tftp 80800000 zImage #下载zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb #下载设备树
bootz 80800000 - 83000000 #启动Linux
#
#以上实例可以通过设置bootcmd,保存后直接使用boot命令来实现
setenv bootcmd 'tftp 80800000 zImage;tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000'
saveenv
boot
bootcmd环境变量保存着引导命令(启动的命令集合),具体的引导命令式可以修改的
4.10 其他常用命令
uboot中还有一些其他常用的命令,如reset、go、run和mtest等
- reset:复位重启
=> reset
restting...
- go:用于跳到指定的地址处执行应用
#go命令格式如下:
go addr [arg...]
#addr 应用在DRAM中的首地址
#arg 应用文件
#例如,使用tftp命令将printf.bin下载到DRAM的0x87800000地址处
=> tftp 87800000 printf.bin
=> go 87800000
- run:用于运行环境变量中定义的命令,例如通过“run bootcmd”来运行bootcmd中的启动命令
- mtest:简单的内存读写测试命令,可以测试开发板上的DDR
#mtest命令格式如下:
mtest [start [end [pattern [iterations]]]]
#start 要测试的DRAM中开始地址
#end 要测试的DRAM中结束地址
#例如,测试0x80000000 ~ 0x80001000这段内存
=> mtest 80000000 80001000