U-Boot学习(1):简介及命令行指令详解
Bootloader的主要任务是引导加载并运行应用程序,对于MCU中的BootLoader,我之前写过一篇详细的文章单片机中BootLoader的严谨实现详解介绍它实现的整体流程。对于Linux来说,在运行Linux内核之前也需要BootLoader进行引导,这个BootLoader不需要我们写,因为有很多开源代码供我们选择,其中最常用的就是U-Boot。这篇文章就来简单介绍以下U-Boot,来看一下它是怎么使用的。
文章目录
1 U-Boot简介
U-Boot(Universal Bootloader
)是一个开源的引导加载程序,主要用于嵌入式系统和嵌入式设备。它被设计成通用的,能够在多种处理器架构上运行,如ARM、MIPS、x86等。U-Boot的主要功能是加载并启动操作系统内核,最常见的是Linux内核。
以下是U-Boot引导Linux内核时所执行的主要步骤:
- 启动阶段(Boot ROM):
- 当嵌入式设备上电或者复位时,处理器会从固定地址处的启动ROM(Boot ROM)中开始执行代码。
- 启动ROM的主要任务是初始化系统的基本硬件,如时钟、内存控制器等,并加载U-Boot引导加载程序到RAM中。
- U-Boot初始化:
- 一旦U-Boot加载到RAM中,它就开始执行。U-Boot首先进行硬件初始化,包括处理器、内存、外设等的配置。
- U-Boot提供了一个命令行界面,允许用户进行配置和交互式操作。用户可以通过串口、网络或其他方式与U-Boot进行通信。
- 加载Linux内核:
- U-Boot负责从存储介质(如Flash、SD卡、网络等)中加载Linux内核镜像到设备的内存中。这通常涉及到文件系统的读取和解析。
- 用户可以在U-Boot命令行中手动输入加载内核的命令,也可以通过配置文件自动加载。
- 设备树加载:
- 对于许多嵌入式系统,U-Boot还会加载设备树(
Device Tree
)。设备树是一种描述硬件设备和系统拓扑结构的数据结构,它允许Linux内核动态适应各种硬件配置。
- 对于许多嵌入式系统,U-Boot还会加载设备树(
- 传递控制权给Linux内核:
- 一旦Linux内核被成功加载到内存中,U-Boot会设置一些必要的参数,如内核命令行参数、设备树的地址等。
- U-Boot然后将控制权传递给Linux内核,使其开始执行。此时,Linux内核接管系统的控制权。
总的来说,U-Boot在嵌入式系统中的角色是引导加载程序,负责初始化硬件、加载操作系统内核、传递必要的参数,并最终将控制权交给内核。这使得U-Boot成为嵌入式系统中一个关键的组件,它的配置和功能对系统的启动和运行起着重要作用。
2 U-Boot命令行指令详解
在U-Boot运行后,会有一个倒计时,如果倒计时内我们按下键盘的任何按键,就可以进入U-Boot的命令行模式,如果不按下,则U-Boot会根据我们默认的启动参数来启动内核。这里我们就来介绍一下常用的U-Boot的命令行的指令。我们可以输入help
查看支持的指令:
对于具体的指令,我们可以输入help 指令名
来查看这个指令的用法,比如这里的base
指令:
下面就具体来看一下这些指令的使用方法,对于不常用的指令,就不详细介绍或举例说明了。
2.1 信息查询指令
-
bdinfo
:打印板级信息结构(Board Info structure
),包括板级名称、序列号、CPU类型、时钟频率等。
-
printenv
:打印环境变量的值。
这里面有很多环境变量的值,比如串口的波特率,还有U-Boot上电后倒计时的值bootdelay
。修改环境变量的指令参考下面的3.2 环境变量相关指令
。 -
version
:打印U-Boot的版本信息、编译器和链接器的版本。
2.2 环境变量相关指令
-
editenv
:编辑环境变量。
以修改bootdelay
参数为例,输入editenv bootdelay
后,等于进入一个文本模式,这里显示了当前的初始值,我们只需要修改然后回车即可,这条指令修改环境变量很方便。 -
env
:环境变量处理命令。
-
saveenv
:将环境变量保存到存储中。前面设置的变量默认是保存在RAM中,如果需要下次上电使用新设置的环境变量,需要保存到非易失存储中。 -
setenv
:设置/新建/删除环境变量的值。- 我们可以调用
set bootdelay 5
修改环境变量的值,如果环境变量不存在,则会自动创建。如果参数为空,则可以删除环境变量,如set bootdelay
。 - 有的U-Boot不支持识别参数中的空格,可能环境变量就设置为第一个空格前的值了,这个时候需要用单引号把参数括起来,如
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2'
。
- 我们可以调用
-
setexpr
:将环境变量设置为表达式的结果。- 举个例子,假设
bootdelay
为5,setexpr bootdelay1 $bootdelay + 2
将设置bootdelay1
为7。
- 举个例子,假设
-
showvar
:打印本地hushshell变量。
2.3 启动相关指令
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
中的启动命令。
假设我们想每次都从TFTP下载镜像启动,我们就可以设置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镜像。
2.4 文件系统相关指令
fatinfo
:打印FAT文件系统的信息。- 格式为:
fatinfo <interface> [<dev[:part]>]
上面查看了MMC(我这里MMC的dev为1)的第一个分区的文件系统信息
- 格式为:
fatls
:列出目录中的文件(默认为根目录)。
我们在MMC的第一个分区里面的文件系统里存放了zimage
和设备树。
fstype
:查看文件系统类型
fatload
:从文件系统中加载二进制文件到RAM。
下面代码加载zImage到0x80000000处
fatload mmc 1:1 80000000 zImage
fatsize
:确定文件的大小。fatwrite
:将文件写入DOS文件系统。ls
:列出目录中的文件(默认为根目录)。load
:从文件系统加载二进制文件。save
:将文件保存到文件系统。ext2load
:从Ext2文件系统加载二进制文件。ext2ls
:列出目录中的文件(默认为根目录)。ext4load
:从Ext4文件系统加载二进制文件。ext4ls
:列出目录中的文件(默认为根目录)。ext4size
:确定文件的大小。ext4write
:在根目录下创建文件。
2.5 内存操作指令
-
cmp
:对比内存内容。- 类似C语言的
memcmp
,格式为:cmp [.b, .w, .l] addr1 addr2 count
,将输出两个内存里的值是否相等。
- 类似C语言的
-
cp
:内存复制。- 类似C语言的
memcpy
,格式为:cp [.b, .w, .l] source target count
- 类似C语言的
-
md
(Memory Display
):显示内存内容。- 格式为:
md [.b, .w, .l] address [# of objects]
,其中b
/w
/l
分别表示字节、半字和字
上图为显示0x80000000内存处的前0x10(16)个字节。
- 格式为:
-
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退出。
- 格式为:
2.6 网络相关指令
在介绍网络相关的指令之前,先来介绍一下与网络有关的环境变量。在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请求。
我们可以ping Ubuntu主机的IP看一下是否在线。
下面来介绍一下nfs
和tftpboot
两个指令,NFS
和TFTP
的用处和环境搭建可以参考这篇文章:环境搭建之TFTP、NFS、SSH和FTP的安装和使用
nfs
:通过网络文件系统下载镜像- 格式为:
nfs [loadAddress] [[hostIPaddr:]bootfilename]
现在假设我们的Ubuntu NFS服务器上有一个1.txt
,文件内容为“123456\n”,它的十六进制如下:
执行nfs 80000000 192.168.31.120:/var/nfs/general/1.txt
:
- 如果确定两个板子ping通,但是板子使用
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
。
假设Ubuntu TFTP目录中有一个1.txt,文件内容为“654321\n”,它的十六进制如下:
执行tftp 80000000 192.168.31.120:1.txt
,这里不用指定tftp的文件目录,因为服务端的配置文件中有tftp的目录。
- 格式为:
2.7 设备操作指令
mmc
:MMC子系统命令,可以用来读写EMMC、SD等与MMC协议兼容的设备,命令如下:
这里我的板子使用的EMMC,首先使用mmc list
查看一下可用的MMC设备,然后使用mmc dev 1
(后面还能跟一个分区参数,不写则默认第一个分区,假设要切换到分区2,则输入mmc dev 1 2
)切换到EMMC,最后使用mmc part
来查看一下MMC的分区。
如果EMMC里有Linux的话,它是有3个分区的,第0个存放U-Boot,第1个存放Linux镜像和设备树,第2个存放根文件系统(下图中没有显示出0分区,但实际是存在的)。
下面我们从分区0中读取数据到RAM中,mmc read
是按块读取的,这里MMC的一个块是512字节,下面我们从分区0的第0块开始读4个块到0x80000000处。
如果我们想烧录U-Boot,我们就可以使用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
- 格式为:
2.8 其他指令
?
:帮助命令的别名。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
:什么也不做,返回成功。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!