U-Boot学习(1):简介及命令行指令详解

2024-01-10 07:35:17

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内核时所执行的主要步骤:

  1. 启动阶段(Boot ROM):
    • 当嵌入式设备上电或者复位时,处理器会从固定地址处的启动ROM(Boot ROM)中开始执行代码。
    • 启动ROM的主要任务是初始化系统的基本硬件,如时钟、内存控制器等,并加载U-Boot引导加载程序到RAM中。
  2. U-Boot初始化:
    • 一旦U-Boot加载到RAM中,它就开始执行。U-Boot首先进行硬件初始化,包括处理器、内存、外设等的配置。
    • U-Boot提供了一个命令行界面,允许用户进行配置和交互式操作。用户可以通过串口、网络或其他方式与U-Boot进行通信。
  3. 加载Linux内核:
    • U-Boot负责从存储介质(如Flash、SD卡、网络等)中加载Linux内核镜像到设备的内存中。这通常涉及到文件系统的读取和解析。
    • 用户可以在U-Boot命令行中手动输入加载内核的命令,也可以通过配置文件自动加载。
  4. 设备树加载:
    • 对于许多嵌入式系统,U-Boot还会加载设备树(Device Tree)。设备树是一种描述硬件设备和系统拓扑结构的数据结构,它允许Linux内核动态适应各种硬件配置。
  5. 传递控制权给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为设备树的地址。我们可以使用TFTPNFS等命令下载镜像到RAM后,使用bootz来启动内核:
tftp 80800000 zImage
tftp 83000000 imx6ull-alientek-emmc.dtb
bootz 80800000 – 83000000
  • bootm:从内存中启动uImage镜像。
    指令格式同bootzbootm [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,将输出两个内存里的值是否相等。
  • cp:内存复制。

    • 类似C语言的memcpy,格式为:cp [.b, .w, .l] source target count
  • 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使用以太网相关的指令时,比如初始化时,会从环境变量中取这些值进行配置。

环境变量描述
ipaddrIP地址,若不指定,可使用dhcp命令自动获取
ethaddrMAC地址
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看一下是否在线。

下面来介绍一下nfstftpboot两个指令,NFSTFTP的用处和环境搭建可以参考这篇文章:环境搭建之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来实现。首先使用前面所说的tftpbootnfs将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:什么也不做,返回成功。

文章来源:https://blog.csdn.net/tilblackout/article/details/135179482
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。