编译 u-boot
u-boot,全称 Universal Boot Loader,是遵循 GPL 条款的开放源码项目。u-boot 的作用是系统引导。u-boot 的工作模式有启动加载模式和下载模式。启动加载模式是 Bootloader 的正常工作模式,嵌入式产品发布时,Bootloader 必须工作在这种模式下,Bootloader 将嵌入式操作系统从 FLASH 中加载到 SDRAM 中运行,整个过程是自动的。下载模式就是 Bootloader 通过某些通信手段将内核映像或根文件系统映像等从 PC 机中下载到目标板的 FLASH 中。用户可以利用 Bootloader 提供的一些命令接口来完成自己想要的操作。
下载与编译
git clone "https://gitee.com/thead-linux/u-boot.git" -b master
make ice_evb_c910_defconfig
make -j ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu-
# 配置文件保存在 configs/ 目录下,根据开发板选择相应的配置文件
编译过程如下:
......
LD u-boot
OBJCOPY u-boot.srec
OBJCOPY u-boot-nodtb.bin
SYM u-boot.sym
COPY u-boot.bin
CC spl/lib/display_options.o
LD spl/lib/built-in.o
LD spl/u-boot-spl
OBJCOPY spl/u-boot-spl-nodtb.bin
COPY spl/u-boot-spl.bin
CAT u-boot-with-spl.bin
编译完成后,生成 u-boot-with-spl.bin
文件,接下来将u-boot-with-spl.bin
烧写到开发板中。
烧写
方法一:使用 CCT 烧写,方法参见: Linux环境下烧写镜像
方法二:在开发板的 Linux 环境下直接烧写:
# 将 u-boot-with-spl.bin 传到开发板
scp u-boot-with-spl.bin root@192.168.1.100:~/
# 打开 boot 分区的写权限
ssh root@192.168.1.100 "echo 0 > /sys/block/mmcblk0boot0/force_ro"
# 将 u-boot-with-spl.bin 写入boot分区
ssh root@192.168.1.100 "dd if=u-boot-with-spl.bin of=/dev/mmcblk0boot0"
# 重启开发板
ssh root@192.168.1.100 "reboot"
u-boot命令使用
u-boot 提供众多命令,输入 help 或 ? 可以查看 u-boot 有哪些命令,本文介绍一些 u-boot 在日常工作中常用的一些命令:
C910 # help
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
bmode - sd1|sd2|qspi1|normal|usb|sata|ecspi1:0|ecspi1:1|ecspi1:2|ecspi1:3|esdhc1|esdhc2|esdhc3|esdhc4 [noreset]
bmp - manipulate BMP image data
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootelf - Boot from an ELF image in memory
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootvx - Boot vxWorks from an ELF image
bootz - boot Linux zImage image from memory
clocks - display clocks
clrlogo - fill the boot logo area with black
cmp - memory compare
...
信息查询相关命令
bdinfo
该命令用来查询当前板子的相关信息:
C910 # bdinfo
boot_params = 0x0000000000000000
DRAM bank = 0x0000000000000000
-> start = 0x0000000000000000
-> size = 0x0000000100000000
relocaddr = 0x00000000fff96000
reloc off = 0x000000003ff96000
ethaddr = 6a:af:e8:c6:23:ff
IP addr = 192.168.1.100
baudrate = 115200 bps
printenv
查询当前板子的一些相关环境变量:
C910 # print
arch=riscv
avail_addr=0x10000000
baudrate=115200
board=ice-c910
board_name=ice-c910
bootargs=console=ttyS0,115200 rdinit=/sbin/init rootwait rw earlyprintk root=PARTUUID=80a5a8e9-c744-491a-93c1-4f4194fd690b rootfstype=ext4 clk_ignore_unused loglevel=7 crashkernel=256M-:128M c910_mmu_v1 eth=6a:af:e8:c6:23:ff
bootcmd=ext4load mmc 0:2 0x0 fw_jump.bin; ext4load mmc 0:2 0x01f00000 hw.dtb; ext4load mmc 0:2 0x02000000 uImage; bootm 0x02000000 - 0x01f00000
bootdelay=2
cpu=c9xx
dtb_addr=0x01f00000
dtb_emmc_size=0x100
dtb_emmc_start=0x1000
ethact=ethernet@3fffc0000
ethaddr=6a:af:e8:c6:23:ff
fdtcontroladdr=fffe4630
ipaddr=192.168.1.100
kernel_addr=0x02000000
kernel_emmc_size=0xa000
kernel_emmc_start=0x2000
opensbi_addr=0x0
opensbi_emmc_size=0x280
opensbi_emmc_start=0x800
partitions=name=table,size=2031KB;name=boot,size=60MiB,type=bc13c2ff-59e6-4262-a352-b275fd6f7172;name=root,uuid=80a5a8e9-c744-491a-93c1-4f4194fd690b,size=-,type=linux
stderr=serial@3fff73000
stdin=serial@3fff73000
stdout=serial@3fff73000
uuid_rootfs=80a5a8e9-c744-491a-93c1-4f4194fd690b
vendor=thead
Environment size: 1072/131068 bytes
version
该命令用于查询uboot版本和交叉编译工具的相关信息
C910 # version
U-Boot 2020.01-g6cc5d59b0d (Dec 20 2020 - 08:37:37 +0000)
riscv64-unknown-linux-gnu-gcc (T-HEAD RISCV Tools V1.10.2 B20201104) 8.4.0
GNU ld (GNU Binutils) 2.32
环境变量相关命令
环境变量常用的命令主要有两个,分别是setenv和saveenv,setenv命令用来设置或者修改当前环境变量的值,saveenv用来保存环境变量的值,一般环境变量是存放在外部的Flash中的,例如Nand Flash中,当uboot启动的时候,会将环境变量读取的DRAM中,当我们使用setenv修改了环境变量的值后,需要使用saveenv命令将修改后的环境变量的值保存到Flash中,否则修改无效。setenv命令的格式如下:
setenv env value
查看当前的bootdelay环境变量值为:
print cpu
当前的bootdelay为3,我们要将bootdelay环境变量的值设置为5,可以使用下面的命令:
setenv bootdelay 5
saveenv
清除环境变量:
# 恢复所有变更的默认值
env default -a
内存操作相关命令
内存操作的相关命令用于对DRAM内存进行读写操作,常用的内存操作命令有md、nm、mm、mw、cp、cmp。
md 命令
用于显示内存值,命令格式如下:
C910 # ? md
md - memory display
Usage:
md [.b, .w, .l, .q] address [# of objects]
命令用法中的[.b, .w, .l]对应着byte、word、long,分别以1个字节、2个字节、4个字节来进行内存值显示,address表示要查看的内存起始地址,[# of objects]表示要查看的数据长度,和显示的数据格式有关,并且需要注意的是uboot命令中的数字都是十六进制的。
例如,当我们想要查看从地址0x1000000开始的48个字节的内存值,可以使用下面的命令显示:
md.b 10000000 30
或者:
md.b 0x10000000 0x30
显示如下:
C910 # md.b 0x10000000 0x30
80000000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
nm 命令
用于修改指定地址的内存值,命令格式如下:
C910 # ? nm
nm - memory modify (constant address)
Usage:
nm [.b, .w, .l, .q] address
nm命令同样是使用[.b, .w, .l]来指定内存的操作格式,例如,想要使用修改0x10000000地址的数据为0x77,可使用下面的命令:
C910 # nm.b 10000000
10000000: ff ? 11
10000000: 11 ? q
再通过 md 0x10000000
查看数据是否更改:
C910 # md.b 0x10000000
10000000: 11 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
10000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
10000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
mm命令
mm命令也是可以用来修改内存值,但是使用mm命令修改内存值的时候,地址值将会自增,使用nm命令时,地址值将不会自增,命令格式如下:
C910 # ? mm
mm - memory modify (auto-incrementing address)
Usage:
mm [.b, .w, .l, .q] address
例如,使用.b格式修改0x10000000开始的连续4个字节数据为0x12345678,使用命令如下:
C910 # mn.b 0x10000000
Unknown command 'mn.b' - try 'help'
C910 # mm.b 0x10000000
10000000: 11 ? 1
10000001: ff ? 2
10000002: ff ? 3
10000003: ff ? 4
10000004: ff ? 5
10000005: ff ? 6
10000006: ff ? q
C910 # md.b 0x10000000
10000000: 01 02 03 04 05 06 ff ff ff ff ff ff ff ff ff ff ................
10000010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
10000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
mw命令
用来使用一个指定的数据填充一段内存,命令的使用方法如下:
C910 # ? mw
mw - memory write (fill)
Usage:
mw [.b, .w, .l, .q] address value [count]
该命令同样使用[.b, .w, .l]来指定操作格式,address表示要填充的内存起始地址,value表示要填充的数据,count是要填充的长度。例如,使用.b格式将以0x10000000为起始地址的0x30个内存块填充为0xAA,命令如下:
C910 # mw.b 10000000 aa 30
C910 # md.b 0x10000000
10000000: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
10000010: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
10000020: aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ................
cp命令
数据拷贝命令,用于将DRAM中的数据从一段内存中拷贝到另一段内存中,命令的使用格式如下所示:
C910 # ? cp
cp - memory copy
Usage:
cp [.b, .w, .l, .q] source target count
该命令同样使用[.b, .w, .l]来指定操作格式,source表示为内存源地址,target表示为目标地址,count为拷贝的长度。例如,使用.b格式将0x10000000开始地址处的0x30个字节拷贝到0x10000800地址处,命令如下所示:
cp.b 10000000 10000800 30
或者:
cp.b 0x10000000 0x10000800 30
cmp命令
用于比较两段内存的数据是否相等,命令的使用格式如下所示:
C910 # ? cmp
cmp - memory compare
Usage:
cmp [.b, .w, .l, .q] addr1 addr2 count
该命令同样使用[.b, .w, .l]来指定操作格式,addr1为第一段内存首地址,addr2为第二段内存首地址,count表示要比较的长度。
例如,使用.b格式来比较0x80000000和0x80000100两个地址的数据是否相等,比较的长度为0x14个字节,命令如下:
C910 # cmp.b 0x10000000 0x10000800 30
byte at 0x0000000010000000 (0xaa) != byte at 0x0000000010000800 (0xff)
Total of 0 byte(s) were the same
emmc和sd卡相关操作命令
对于uboot来说是支持emmc或者sd卡,因此也需要提供给用户emmc和sd卡的相关操作命令,uboot中常用于操作mmc设备的命令为"mmc",mmc是一系列的命令,它的后面可以跟不同的参数,在uboot命令行中,输入下面的命令可以查看详细的用法:
C910 # ? mmc
mmc - MMC sub system
Usage:
mmc info - display info of the current MMC device
mmc read addr blk# cnt
mmc write addr blk# cnt
mmc erase blk# cnt
mmc rescan
mmc part - lists available partition on current mmc device
mmc dev [dev] [part] - show or set current mmc device [partition]
mmc list - lists available devices
mmc hwpartition [args...] - does hardware partitioning
arguments (sizes in 512-byte blocks):
[user [enh start cnt] [wrrel {on|off}]] - sets user data area attributes
[gp1|gp2|gp3|gp4 cnt [enh] [wrrel {on|off}]] - general purpose partition
[check|set|complete] - mode, complete set partitioning completed
WARNING: Partitioning is a write-once setting once it is set to complete.
Power cycling is required to initialize partitions after set to complete.
mmc setdsr <value> - set DSR register value
mmc后跟不同的参数可以实现不同的功能,如下:
命令 | 功能 |
---|---|
mmc info | 输出mmc设备的信息 |
mmc read | 读取mmc设备中的数据 |
mmc write | 向mmc设备中写入数据 |
mmc rescan | 扫描当前存在的mmc设备 |
mmc part | 列出mmc设备的分区 |
mmc dev | 切换mmc设备 |
mmc list | 列出当前的mmc设备 |
mmc hwpartition | 设置mmc设备的分区 |
mmc bootbus | 设置指定mmc设备的BOOT_BUS_WIDTH的值 |
mmc bootpart | 设置指定mmc设备的boot和RPMB分区大小 |
mmc partconf | 设置指定mmc设备的PARTITION_CONFIG的值 |
mmc rst | mmc设备复位 |
mmc setdsr | 设置mmc设备DSR寄存器的值 |
mmc info
mmc info命令用来显示当前选中的mmc设备的信息:
C910 # mmc info
Device: mmc0@3fffb0000
Manufacturer ID: 11
OEM: 100
Name: 004GA
Bus Speed: 25000000
Mode: MMC High Speed (52MHz)
Rd Block Len: 512
MMC version 5.0
High Capacity: Yes
Capacity: 3.7 GiB
Bus Width: 8-bit
Erase Group Size: 512 KiB
HC WP Group Size: 4 MiB
User Capacity: 3.7 GiB WRREL
Boot Capacity: 2 MiB ENH
RPMB Capacity: 512 KiB ENH
mmc rescan
mmc rescan命令用于扫描当前目标板上所有的mmc设备,包括eMMC和SD卡,命令输入如下:
C910 # mmc rescan
mmc list
mmc list命令可用于查看当前目标板共有多少个mmc设备,输入命令如下:
C910 # mmc list
mmc0@3fffb0000: 0 (eMMC)
mmc dev
mmc dev命令可以用来切换当前的mmc设备,命令格式如下:
mmc dev [dev] [part]
其中[dev]表示要切换的mmc设备号,[part]是mmc设备的分区号,如果不写分区号,则默认为分区0。
例如,使用命令切换到eMMC设备:
#当0为SD卡,1为eMMC时
=> mmc dev 1
mmc part
mmc part命令可以用来查看当前mmc设备的分区,例如查看当前sd卡中的分区:
C910 # mmc part
Partition Map for MMC device 0 -- Partition Type: EFI
Part Start LBA End LBA Name
Attributes
Type GUID
Partition GUID
1 0x00000022 0x00000fff "table"
attrs: 0x0000000000000000
type: ebd0a0a2-b9e5-4433-87c0-68b6b72699c7
type: data
guid: 72fd13fc-2c69-42f4-9f02-d19dd22ecd0e
2 0x00001000 0x0001efff "boot"
attrs: 0x0000000000000000
type: bc13c2ff-59e6-4262-a352-b275fd6f7172
guid: 02382dbd-94d7-435f-ace0-598b4a5acb5e
3 0x0001f000 0x0075ffde "root"
attrs: 0x0000000000000000
type: 0fc63daf-8483-4772-8e79-3d69d8477de4
type: linux
guid: 80a5a8e9-c744-491a-93c1-4f4194fd690b
mmc read
mmc read命令可用于读取mmc设备的数据,它的使用格式如下所示:
mmc read addr blk# cnt
其中addr是将数据读取到DRAM中的地址,blk是要读取的块起始地址,一块为512字节,cnt则是要读取的块的数量。
例如,从当前的SD卡设备的第2048块开始,读取20个块数据到DRAM的0x10000100地址处,该命令如下:
C910 # mmc list
C910 # mmc dev 0
C910 # mmc read 10000100 800 14
读取成功后,如下所示:
MMC read: dev # 0, block # 2048, count 20 ... 20 blocks read: OK
mmc write
mmc write命令可以将DRAM中的数据写入到mmc设备里面,其命令的格式如下所示:
mmc write addr blk# cnt
其中addr是要写入到mmc设备中的数据在DRAM中的起始地址,blk是要写入mmc的块起始地址,cnt是要写入的块数量,一个块的大小为512字节。
例如,将DRAM地址0x10000100开始的数据,从mmc设备的2048个块开始烧写,烧写20个块,命令如下:
C910 # mmc read 10000100 800 14
写入成功后,如下所示:
MMC write: dev # 0, block # 8264, count 20 ... 20 blocks written: OK
mmc erase
mmc erase可以用来擦除mmc设备中指定的块,其使用命令格式如下:
mmc erase blk# cnt
其中blk是要擦除的起始块,cnt是要擦除的块数量。
例如,可以使用下面的命令擦除mmc设备从2048个块开始的20个块:
C910 # mmc list
C910 # mmc part
C910 # mmc erase 800 14
文件系统相关操作
当我们需要在uboot中对SD卡或者eMMC设备中存储的文件进行操作时,这个时候就需要用到uboot中文件系统的操作命令,u-boot 支持文件系统,包括 fat、ext2/ext3/ext4 等。对于fat 文件系统,u-boot 提供:fatinfo、fatls、fstype、fatload、fatwrite
等命令,对于 ext2/ext3/ext4
文件,u-boot 出提供相对的操作命令:ext2load ext2ls ext4load ext4ls ext4size
等。下文以 ext4 为参考,介绍 文件系统操作。
fstype 命令
fstype命令可以用于查看mmc设备中某个分区的文件系统格式,该命令的用法如下所示:
C910 # ? fstype
fstype - Look up a filesystem type
Usage:
fstype <interface> <dev>:<part>
- print filesystem type
fstype <interface> <dev>:<part> <varname>
- set environment variable to filesystem type
从上面的输出可以看到,fstyp命令具有两个用法,第一个用来查看mmc设备分区中的文件系统类型,第二个则是用来设置文件系统类型的环境变量,对于第一个命令用法,
例如,查看我当前目标板中sd设备的第一个分区的文件系统类型,可以使用下面命令:
=> fstype mmc 0:2
命令输入后回车,输出如下所示:
C910 # fstype mmc 0:2
ext4
ext4ls 命令
ext4ls 命令可以用于查询FAT格式文件系统的目录和文件信息,该命令的用法如下:
C910 # ? ext4ls
ext4ls - list files in a directory (default /)
Usage:
ext4ls <interface> <dev[:part]> [directory]
- list files from 'dev' on 'interface' in a 'directory'
在该命令的用法中,
例如,查询我当前sd卡中分区2中的目录和文件,可以输入下面命令:
=> ext4ls mmc 0:2
命令输入回车后,将会列举根目录下所有的目录和文件,输出如下:
C910 # ext4ls mmc 0:3
<DIR> 1024 .
<DIR> 1024 ..
<DIR> 12288 lost+found
<DIR> 4096 bin
<DIR> 1024 boot
<DIR> 1024 dev
<DIR> 5120 etc
<DIR> 1024 home
<DIR> 1024 lib
<DIR> 1024 media
<DIR> 1024 mnt
<DIR> 1024 opt
<DIR> 1024 proc
<DIR> 1024 root
<DIR> 1024 run
<DIR> 4096 sbin
<DIR> 1024 srv
<DIR> 1024 sys
<DIR> 1024 tmp
<DIR> 1024 usr
<DIR> 1024 var
<DIR> 1024 resources
当我们再想查看 etc 目录下的目录或者文件时,可以使用下面的命令:
C910 # ext4ls mmc 0:3 /etc
ext4load 命令
ext4load 命令用来将指定的文件读取到DRAM内存中,该命令的使用格式如下:
C910 # ? ext4load
ext4load - load binary file from a Ext4 filesystem
Usage:
ext4load <interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]
- load binary file 'filename' from 'dev' on 'interface'
to address 'addr' from ext4 filesystem
该命令的用法中,
例如,在我当前的目标帮中,将mmc卡中第二个分区中的 uImage 文件读取到DRAM中0x10000000起始地址中,可以使用下面的命令:
C910 # ext4ls mmc 0:2
C910 # ext4load mmc 0:2 0x10000000 uImage
命令输入后回车,输出如下:
14058560 bytes read in 610 ms (22 MiB/s)
BOOT启动相关操作命令
u-boot最主要的工作就是引导启动Linux系统,因此uboot中肯定是有相关的boot启动命令的,和boot启动常用相关的命令有boot、bootm和bootz,接下来,我们了解一下这些命令的使用。
boot 命令
boot命令是用来启动Linux系统的,该命令的用法如下:
C910 # ? boot
boot - boot default, i.e., run 'bootcmd'
Usage:
boot
可以看到到,该命令将会运行bootcmd,也就是boot命令将会读取bootcmd这个环境变量,并运行这个环境变量中的命令,来看一下当前我目标板中的bootcmd环境变量内容,使用下面命令查看:
C910 # print bootcmd
显示如下:
bootcmd=ext4load mmc 0:2 0x0 fw_jump.bin; ext4load mmc 0:2 0x01f00000 hw.dtb; ext4load mmc 0:2 0x02000000 uImage; bootm 0x02000000 - 0x01f00000
可以看到,该环境变量就是定义了一些启动引导的命令集合,先使用ext4load命令从eMMC 第个分区读取fw_jump.bin、hw.dtb、uImage 到DRAM内存地址中,然后使用bootm命令启动Linux系统。
bootm 命令
bootm命令用于从内存中启动uImage镜像,该命令的用法如下:
C910 # ? bootm
bootm - boot application image from memory
Usage:
bootm [addr [arg ...]]
- boot application image stored in memory
passing arguments 'arg ...'; when booting a Linux kernel,
'arg' can be the address of an initrd image
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead
For the new multi component uImage format (FIT) addresses
must be extended to include component or configuration unit name:
addr:<subimg_uname> - direct component image specification
addr#<conf_uname> - configuration specification
Use iminfo command to get the list of existing component
images and configurations.
Sub-commands to do part of the bootm sequence. The sub-commands must be
issued in the order below (it's ok to not issue all sub-commands):
start [addr [arg ...]]
loados - load OS image
ramdisk - relocate initrd, set env initrd_start/initrd_end
fdt - relocate flat device tree
cmdline - OS specific command line processing/setup
bdt - OS specific bd_t processing
prep - OS specific prep before relocation or go
go - start OS
主要环境变量介绍
环境变量 | 描述 |
---|---|
bootdelay | 执行自动启动的等候秒数 |
baudrate | 串口控制台的波特率 |
netmask | 以太网接口的掩码 |
ethaddr | 以太网卡的网卡物理地址 |
bootfile | 缺省的下载文件 |
bootargs | 传递给内核的启动参数 |
bootcmd | 自动启动时执行的命令 |
serverip | 服务器端的ip地址 |
ipaddr | 本地ip 地址 |
U-boot的环境变量值得注意的有两个: bootcmd 和bootargs。
bootcmd:前面有说过bootcmd是自动启动时默认执行的一些命令,因此你可以在当前环境中定义各种不同配置,不同环境的参数设置,然后设置bootcmd为你经常使用的那种参数。
# 配置 bootcmd setenv bootcmd "ext4load mmc 0:2 $opensbi_addr fw_jump.bin" setenv bootcmd "$bootcmd;ext4load mmc 0:2 $opensbi_addr fw_jump.bin" setenv bootcmd "$bootcmd;ext4load mmc 0:2 $dtb_addr hw.dtb" setenv bootcmd "$bootcmd;ext4load mmc 0:2 $kernel_addr uImage" setenv bootcmd "$bootcmd;bootm $kernel_addr - $dtb_addr"
bootargs:bootargs是环境变量中的重中之重,甚至可以说整个环境变量都是围绕着bootargs来设置的。
# 配置内核启动参数 bootargs setenv bootargs "console=ttyS0,115200" setenv bootargs "$bootargs root=PARTUUID=$uuid_rootfs rootfstype=ext4" setenv bootargs "$bootargs rdinit=/sbin/init rootwait rw earlyprintk" setenv bootargs "$bootargs clk_ignore_unused loglevel=7 c910_mmu_v1" setenv bootargs "$bootargs eth=$ethaddr"
bootargs的种类非常的多,主要参考网址: