Bootloader的主要任务是引导加载并运行应用程序,对于MCU中的BootLoader,我之前写过一篇详细的文章单片机中BootLoader的严谨实现详解介绍它实现的整体流程。对于Linux来说,在运行Linux内核之前也需要BootLoader进行引导,这个BootLoader不需要我们写,因为有很多开源代码供我们选择,其中最常用的就是U-Boot。这篇文章就来简单介绍以下U-Boot,来看一下它是怎么使用的。
U-Boot(Universal Bootloader
)是一个开源的引导加载程序,主要用于嵌入式系统和嵌入式设备。它被设计成通用的,能够在多种处理器架构上运行,如ARM、MIPS、x86等。U-Boot的主要功能是加载并启动操作系统内核,最常见的是Linux内核。
以下是U-Boot引导Linux内核时所执行的主要步骤:
Device Tree
)。设备树是一种描述硬件设备和系统拓扑结构的数据结构,它允许Linux内核动态适应各种硬件配置。总的来说,U-Boot在嵌入式系统中的角色是引导加载程序,负责初始化硬件、加载操作系统内核、传递必要的参数,并最终将控制权交给内核。这使得U-Boot成为嵌入式系统中一个关键的组件,它的配置和功能对系统的启动和运行起着重要作用。
在U-Boot运行后,会有一个倒计时,如果倒计时内我们按下键盘的任何按键,就可以进入U-Boot的命令行模式,如果不按下,则U-Boot会根据我们默认的启动参数来启动内核。这里我们就来介绍一下常用的U-Boot的命令行的指令。我们可以输入help
查看支持的指令:
对于具体的指令,我们可以输入help 指令名
来查看这个指令的用法,比如这里的base
指令:
下面就具体来看一下这些指令的使用方法,对于不常用的指令,就不详细介绍或举例说明了。
bdinfo
:打印板级信息结构(Board Info structure
),包括板级名称、序列号、CPU类型、时钟频率等。
printenv
:打印环境变量的值。
这里面有很多环境变量的值,比如串口的波特率,还有U-Boot上电后倒计时的值bootdelay
。修改环境变量的指令参考下面的3.2 环境变量相关指令
。
version
:打印U-Boot的版本信息、编译器和链接器的版本。
editenv
:编辑环境变量。
以修改bootdelay
参数为例,输入editenv bootdelay
后,等于进入一个文本模式,这里显示了当前的初始值,我们只需要修改然后回车即可,这条指令修改环境变量很方便。
env
:环境变量处理命令。
saveenv
:将环境变量保存到存储中。前面设置的变量默认是保存在RAM中,如果需要下次上电使用新设置的环境变量,需要保存到非易失存储中。
setenv
:设置/新建/删除环境变量的值。
set bootdelay 5
修改环境变量的值,如果环境变量不存在,则会自动创建。如果参数为空,则可以删除环境变量,如set bootdelay
。setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2'
。setexpr
:将环境变量设置为表达式的结果。
bootdelay
为5,setexpr bootdelay1 $bootdelay + 2
将设置bootdelay1
为7。showvar
:打印本地hushshell变量。
bootz
:从内存中启动Linux zImage镜像。
bootz [addr [initrd[:size]] [fdt]]
,其中addr
为zImage地址,initrd
为在系统引导过程中挂载的临时根文件系统,一般我们不使用,可以填-
略过这个参数,fdt
为设备树的地址。我们可以使用TFTP
、NFS
等命令下载镜像到RAM后,使用bootz
来启动内核:tftp 80800000 zImage
tftp 83000000 imx6ull-alientek-emmc.dtb
bootz 80800000 – 83000000
bootm
:从内存中启动uImage镜像。bootz
:bootm [addr [initrd[:size]] [fdt]]
,如果不使用设备树,则直接bootm addr
。boot
:执行环境变量bootcmd
中的启动命令。bootcmd
,以后就输入boot就会执行bootcmd
里的指令了。实际上前面所说的bootdelay
倒计时结束后,默认也是执行bootcmd
。
bootargs
也与启动有关,它可以传参给内核,在后面的文章我们会介绍,这里先了解一下。setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz
80800000 - 83000000'
saveenv
boot
bootd
:和boot
类似,执行bootcmd
的命令,但它提供了额外的参数用来设置initrd
,这里不介绍。bootp
:通过网络使用BOOTP/TFTP协议启动镜像。bootvx
:从内存中的ELF映像启动vxWorks操作系统。bootelf
:从内存中的ELF映像启动。ELF文件包含调试信息,我们仅在调试是使用这个ELF文件,最后能够运行的程序是bin程序,这个指令能从ELF中提取出原始的bin镜像。fatinfo
:打印FAT文件系统的信息。
fatinfo <interface> [<dev[:part]>]
fatls
:列出目录中的文件(默认为根目录)。zimage
和设备树。fstype
:查看文件系统类型fatload
:从文件系统中加载二进制文件到RAM。fatload mmc 1:1 80000000 zImage
fatsize
:确定文件的大小。fatwrite
:将文件写入DOS文件系统。ls
:列出目录中的文件(默认为根目录)。load
:从文件系统加载二进制文件。save
:将文件保存到文件系统。ext2load
:从Ext2文件系统加载二进制文件。ext2ls
:列出目录中的文件(默认为根目录)。ext4load
:从Ext4文件系统加载二进制文件。ext4ls
:列出目录中的文件(默认为根目录)。ext4size
:确定文件的大小。ext4write
:在根目录下创建文件。cmp
:对比内存内容。
memcmp
,格式为:cmp [.b, .w, .l] addr1 addr2 count
,将输出两个内存里的值是否相等。cp
:内存复制。
memcpy
,格式为:cp [.b, .w, .l] source target count
md
(Memory Display
):显示内存内容。
md [.b, .w, .l] address [# of objects]
,其中b
/w
/l
分别表示字节、半字和字mm
(memory modify
):修改内存内容(自动增加地址)。
mm [.b, .w, .l] address
,输入后会进入交互模式,如果要退出保存,则输入空格回车即可。mtest
:简单的RAM读写测试。
mw
:写内存(填充)。
mw [.b, .w, .l] address value [count]
0x80000000
开始的8字节为0xab。nm
:修改内存内容(固定地址)。
nm [.b, .w, .l] address
mm
类似,但这里不递增,就是修改一个固定的内存的内容。这里没指定数据长度默认就是l
,这里输入完后按回车退出不了,按输入q退出。在介绍网络相关的指令之前,先来介绍一下与网络有关的环境变量。在U-Boot使用以太网相关的指令时,比如初始化时,会从环境变量中取这些值进行配置。
环境变量 | 描述 |
---|---|
ipaddr | IP地址,若不指定,可使用dhcp命令自动获取 |
ethaddr | MAC地址 |
gatewayip | 网关 |
netmask | 子网 |
serverip | 服务器IP地址,一般为我们开发使用的Ubuntu的IP地址 |
如下图所示:
dhcp
:通过DHCP/TFTP协议,使用网络启动镜像。
dhcp
,就会通过路由器的DHCP服务分配一个IP地址给设备dhcp [loadAddress] [[hostIPaddr:]bootfilename]
dhcp
,路由器给设备分配了一个IP地址192.168.31.203
。ping
:向网络主机发送ICMP ECHO_REQUEST请求。下面来介绍一下nfs
和tftpboot
两个指令,NFS
和TFTP
的用处和环境搭建可以参考这篇文章:环境搭建之TFTP、NFS、SSH和FTP的安装和使用
nfs
:通过网络文件系统下载镜像
nfs [loadAddress] [[hostIPaddr:]bootfilename]
1.txt
,文件内容为“123456\n”,它的十六进制如下:nfs 80000000 192.168.31.120:/var/nfs/general/1.txt
:nfs
命令提示File lookup fail
,大概率为版本原因,解决方法如下:1.修改nfs-kernel-server文件
sudo vi /etc/default/nfs-kernel-server
更改RPCNFSDCOUNT="-V 2 8"和RPCMOUNTDOPTS="-V 2 --manage-gids"
2.重启NFS服务端
sudo /etc/init.d/nfs-kernel-server restart
tftpboot
:通过TFTP协议传输镜像。
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
,如果不指定服务端地址hostIPaddr
,则会使用环境变量serverip
。tftp 80000000 192.168.31.120:1.txt
,这里不用指定tftp的文件目录,因为服务端的配置文件中有tftp的目录。mmc
:MMC子系统命令,可以用来读写EMMC、SD等与MMC协议兼容的设备,命令如下:mmc list
查看一下可用的MMC设备,然后使用mmc dev 1
(后面还能跟一个分区参数,不写则默认第一个分区,假设要切换到分区2,则输入mmc dev 1 2
)切换到EMMC,最后使用mmc part
来查看一下MMC的分区。mmc read
是按块读取的,这里MMC的一个块是512字节,下面我们从分区0的第0块开始读4个块到0x80000000处。mmc write
来实现。首先使用前面所说的tftpboot
或nfs
将Ubuntu中的U-Boot镜像拷贝到我们的RAM中,假设拷贝到了0x80000000处。然后我们要查看镜像的大小,假设镜像的大小为1800,则1800/512=3.51,即占据4个块的大小。注意命令的参数都是16进制。我们就可以调用下面命令对镜像进行烧录:mmc dev 1 0
mmc write 80000000 2 4 # I.MX系列的启动头的前1024字节(前2个块)存放启动头信息,所以从第2块烧录
mmc partconf 1 1 0 0 # EMMC需要执行此指令来让分区1(参数1)可以被识别(参数2)
mmcinfo
:显示MMC信息。
i2c
:I2C子系统命令,U-Boot支持与I2C设备进行通信
sf
:SPI Flash子系统命令。
usb
:USB子系统命令。
usbboot
:从USB设备启动。
usbboot loadAddr dev:part
?
:帮助命令的别名。base
:打印或设置地址偏移量。bmp
:操作BMP图像数据。clocks
:显示时钟信息。coninfo
:打印控制台设备和信息。crc32
:计算校验和。dcache
:启用或禁用数据缓存。dm
:驱动模型低级访问。echo
:将参数打印到控制台。erase
:擦除FLASH存储器。exit
:退出脚本。false
:什么也不做,返回失败。flinfo
:打印FLASH存储器信息。fuse
:Fuse子系统。go
:从指定地址开始执行应用程序。类似汇编LDR PC, =地址
。gpio
:查询和控制GPIO引脚。help
:打印命令描述/用法。icache
:启用或禁用指令缓存。iminfo
:打印应用程序映像的头信息。imxtract
:提取多重映像的一部分。itest
:根据整数比较返回真或假。loadb
:通过串行线加载二进制文件(Kermit模式)。loads
:通过串行线加载S-Record文件。loadx
:通过串行线加载二进制文件(Xmodem模式)。loady
:通过串行线加载二进制文件(Ymodem模式)。loop
:在地址范围上进行无限循环。mdio
:MDIO实用命令。mii
:MII实用命令。nm
:内存修改(常量地址)。pmic
:电源管理IC(PMIC)。protect
:启用或禁用FLASH写保护。reset
:执行CPU复位。run
:在环境变量中运行命令。sleep
:延迟一段时间。source
:从内存中运行脚本。test
:类似于/bin/sh的最小测试。true
:什么也不做,返回成功。