u-boot分析与使用
u-boot分析与使用
零、U-boot使用
uboot 命令中输入的数字均为十六进制,而非十进制,注意!!!
0.1信息查询命令
buinfo:查看板子信息
printenv:查看环境变量(直接输入print也行)
version:查看uboot版本号
0.2环境变量相关操作
-
setenv:新建、删除、设置或者修改环境变量的值
1
setenv [变量名] [变量值]
如果变量值中有空格,可以用单引号
'
括起来:1
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
-
saveenv:保存修改后的环境变量
0.3内存操作
md
内存查看命令,用于显示内存值
1 | md[.b, .w, .l] address [# of objects] |
命令中的[.b .w .l]对应 byte、 word 和 long,即分别以 1 个字节、 2 个字节、 4 个字节来显示内存值。 address 就是要查看的内存起始地址, [# of objects]表示要查看的数据长度,这个长度和你所选择的显示格式有关。
nm
修改指定地址的内存值
1 | nm[.b, .w, .l] address |
同样以.b、 .w 和.l 来指定操作格式。 address 就是要修改的内存起始地址。 值得注意的是该命令修改完后地址不会自增
mm
修改指定地址的内存值
1 | mm[.b, .w, .l] address |
与nm命令类似,区别就是修改完后地址会自增
mw
使用一个指定的数据填充一段内存
1 | mw[.b, .w, .l] address value [count] |
以.b、 .w 和.l 来指定操作格式。 address 表示要填充的内存起始地址,value为要填充的数据, count是填充的长度。
cp
数据拷贝命令,用于将 DRAM 中的数据从一段内存拷贝到另一段内存中,或者把 Nor Flash 中的数据拷贝到 DRAM 中
1 | cp[.b, .w, .l] source target count |
以.b、 .w 和.l 来指定操作格式。source 为源地址, target 为目的地址, count 为拷贝的长度
cmp
比较命令,用于比较两段内存的数据是否相等
1 | cmp[.b, .w, .l] addr1 addr2 count |
以.b、 .w 和.l 来指定操作格式。addr1 为第一段内存首地址, addr2 为第二段内存首地址, count 为要比较的长度。
0.4网络操作
该部分命令需要有以下环境变量的支持:
1 | #板卡ip地址 |
ping
验证网络是否正常,不做过多解释
注意!只能在 uboot 中 ping 其他的机器,其他机器不能 ping uboot(uboot 没有对 ping命令做处理,如果用其他的机器 ping uboot 会失败)
dhcp
dhcp会自动获取 IP 地址,同时还会通过 TFTP 来启动 linux 内核,输入? dhcp
可查看uboot中 dhcp 命令详细的信息,这里一般用于自动获取ip地址
nfs
通过nfs(Network File System,网络文件系统)使得板卡能够直接从服务器(服务器需要提前布置好NFS服务与NFS文件目录)上下载相关资源到DRAM中(如zImage、文件系统、设备树等)
1 | nfs [loadAddress] [[hostIPaddr:]bootfilename] |
其中 loadAddress 是要保存的 DRAM 地址, [[hostIPaddr:]bootfilename]是要下载的文件地址(需要输入服务器中的完整路径)
tftp
与nfs类似,均用于通过网络下载文件到 DRAM 中(服务器需要提前布置好TFTP服务与TFTP文件目录)
1 | tftp [loadAddress] [[hostIPaddr:]bootfilename] |
loadAddress 是 文 件 在 DRAM 中 的存 放 地 址 ,[[hostIPaddr:]bootfilename]是要从 Ubuntu 中下载的文件(这里不需要输入服务器中的完整路径,直接从TFTP文件目录中获取)
0.5EMMC和SD卡操作
mmc info
mmc info
命令和mmcinfo
命令相同,均为输出当前选中的设备信息,不做过多解释
mmc rescan
用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡
mmc list
查看当前开发板一共有几个 MMC 设备,通过该命令可以查看当前选中的设备(一般来说0为SD卡,1为EMMC)
mmc dev
切换当前 MMC 设备
1 | mmc dev [dev] [part] |
[dev]用来设置要切换的 MMC 设备号,[part]是分区号(可以不写,默认为分区 0)
mmc part
查看选中的设备对应的分区信息
mmc read
读取当前选中 mmc 设备的数据
1 | mmc read addr blk# cnt |
addr 是数据读取到 DRAM 中的地址, blk# 是要读取的块起始地址(十六进制),一个块是 512字节,这里的块和扇区是一个意思,在 MMC 设备中我们通常说扇区, cnt 是要读取的块数量(十六进制)。
mmc write
将数据写入到当前选中 mmc 设备中
1 | mmc write addr blk# cnt |
addr 是数据写入到 DRAM 中的地址, blk# 是要写入的块起始地址(十六进制),一个块是 512字节,这里的块和扇区是一个意思,在 MMC 设备中我们通常说扇区, cnt 是要写入的块数量(十六进制)。
mmc erase
擦除 MMC 设备的指定块,最好别用
1 | mmc erase blk# cnt |
blk# 为要擦除的起始块, cnt 是要擦除的数量。
0.6FAT文件系统操作
需要在 uboot 中对 SD 卡或者 EMMC 中存储的文件进行操作时需要用到该部分操作命令(只支持FAT格式)
fatinfo
查询指定 MMC 设置指定分区的文件系统信息
1 | fatinfo <interface> [<dev[:part]>] |
interface 表示接口,比如 mmc。dev 是查询的设备号, part 是要查询的分区。
fatls
查询 FAT 格式设备的目录和文件信息
1 | fatls <interface> [<dev[:part]>] [directory] |
interface 是要查询的接口,比如 mmc。dev 是要查询的设备号,part 是要查询的分区,directory是要查询的目录(默认为根目录/
)
fstype
查看 MMC 设备某个分区的文件系统格式
1 | fstype <interface> <dev>:<part> |
interface 是要查询的接口,比如 mmc。dev 是要查询的设备号,part 是要查询的分区
fatload
将指定的文件读取到 DRAM 中
1 | fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]] |
interface 为接口,比如 mmc。 dev 是设备号, part 是分区, addr 是保存在 DRAM 中的起始地址, filename 是要读取的文件名字。 bytes 表示读取多少字节的数据(0或者省略表示读取整个文件)。pos 是要读的文件相对于文件首地址的偏移(0或者省略表示从文件首地址开始读取)
fatwrite
uboot 默认没有使能 fatwrite 命令,需要修改板子配置头文件来使能宏CONFIG_FAT_WRITE
。该命令用于将 DRAM 中的数据写入到 MMC 设备中
1 | fatwrite <interface> <dev[:part]> <addr> <filename> <bytes> |
interface 为接口,比如 mmc, dev 是设备号, part 是分区, addr 是要写入的数据在 DRAM中的起始地址, filename 是写入的数据文件名字, bytes 表示要写入多少字节的数据
0.7EXT文件系统操作
uboot 有 ext2 和 ext4 这两种格式的文件系统的操作命令,常用的就四个命令,分别为:ext2load
、 ext2ls
、 ext4load
、 ext4ls
和 ext4write
。 这些命令与FAT文件系统的类似,在此不做过多叙述。
0.8BOOT操作
bootz
用于启动zImage镜像文件
1 | bootz [addr [initrd[:size]] [fdt]] |
addr 是 Linux 镜像文件在 DRAM 中的位置, initrd 是 initrd 文件在DRAM 中的地址(不使用该参数可以用-
代替), fdt 就是设备树文件在 DRAM 中的地址。
bootm
与上述命令类似,但是是启动uImage镜像文件,如果不使用设备树,则命令为:
1 | bootm addr |
addr 是 uImage 镜像在 DRAM 中的首地址。如果要使用设备树则和bootz命令一致
boot
通过读取环境变量 bootcmd 来启动 Linux 系统
例子:
1 | setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000' |
0.9其他命令
reset
复位命令,不做过多解释
go
用于运行裸机程序
1 | go addr [arg ...] |
addr 是应用在 DRAM 中的首地址,arg是传入的参数
run
用于运行环境变量中定义的命令
1 | setenv mybootemmc 'fatload mmc 1:1 80800000 zImage; fatload mmc 1:1 83000000 imx6ullalientek-emmc.dtb;bootz 80800000 - 83000000' |
一、Makefile结构分析
编译u-boot时,一般需要采取以下命令:
1 | make V=1 xxx_config # V=1输出全部内容 |
下面依次介绍各命令的作用:
其中会用到以下命令,提前熟悉下:
1
2 #搜索文件内的变量名
grep -nR "变量名"
1、make xxx_config
该命令会执行顶层Makefile中的目标%config
,具体如下所示:
1 | %config: scripts_basic outputmakefile FORCE |
其中一些变量在其他地方定义过,总结如下:
-
相关参数值:
1
2
3
4
5
6
7#使用参数V=1时
Q = # 输出详细信息
#不使用参数V=1时
Q = @ # 此时输出信息为精简版
MAKE = make
build = -f ./scripts/Makefile.bulid obj -
outputmakefile为空,scripts_basic:
1
2
3scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount展开后:
1
2
3scripts_basic:
make -f ./scripts/Makefile.bulid obj=scripts/basic
rm -f .tmp_quiet_recordmcount -
FORCE
表示会让该依赖每次都执行
最后实际上运行的命令为:
1 | #依赖中: |
通过上述命令,可以看出都是调用./scripts/Makefile.bulid
,该文件也可以视为Makefile,下面分析上述两条命令:
1、命令make -f ./scripts/Makefile.bulid obj=scripts/basic
:
未指定目标,即采取./scripts/Makefile.bulid
文件中PHONY
指定的默认目标:__build
:
1 | __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \ |
利用echo
输出各变量,有:
1 | #顶层Makefile中: |
综上,有:
1 | __build: /scripts/basic/fixdep |
所以这条命令只是编译可执行文件/scripts/basic/fixdep
2、命令make -f ./scripts/Makefile.bulid obj=scripts/kconfig xxx_defconfig
:
在Makefile.bulid中通过include $(kbuild-file)
,即调用include ./scripts/kconfig/Makefile
,在这里边指定目标:
1 | %_defconfig: $(obj)/conf |
这里替换掉各个变量后有:
1 | %_defconfig: scripts/kconfig/conf |
即有:
- 编译生成软件scripts/kconfig/conf
- 调用编译好的软件scripts/kconfig/conf生成配置输出文件
.config
2、make
未指定目标,即采取Makefile文件中PHONY
指定的默认目标,这里PHONY
依赖有点多,但是重心为以下依赖:
PHONY
->_all
->$(ALL-y)
其中$(ALL-y)
中的依赖也很多,但是重点是u-boot.bin
依赖,且u-boot.bin
依赖的文件也比较多,只需关注以下依赖即可:
1 | u-boot.bin: u-boot-nodtb.bin FORCE |
-
libs-y
会包括很多,但是基本上均为各种源码所对应的.o
文件1
2
3
4
5
6
7
8
9libs-y += lib/
libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
libs-$(CONFIG_OF_EMBED) += dts/
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
...
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))- 最后的
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
调用了patsubst
函数,将libs-y
中的/
替换为/built-in.o
,即将原来的各个目录替换为了对应目录下的built-in.o
文件 built-in.o
文件是通过相同文件夹下.built-in.o.cmd
文件生成的
- 最后的
综上,最后简化后的makefile如下:
1 | u-boot: arch/arm/cpu/armv7/start.o lib/built-in.o ...(大量的built-in.o文件) u-boot.lds FORCE |
- u-boot.lds为编译出的链接脚本(真正的链接脚本来自
arch/arm/cpu/u-boot.lds
)
3、链接脚本
链接地址
通过make V=1 -j8
编译U-boot时,最后会输出Entry Point: xxxxxxxx
,此地址即为链接地址
该地址可以通过编译输出时的log信息发现,在最后链接时,会加入-Ttext xxxxxxxx
,即在链接过程中即指定了链接地址
通过grep -nR "xxxxxxxx"
可以发现最后的链接地址来自于include/configs/mx6_common.h
中的**CONFIG_SYS_TEXT_BASE
**宏
脚本分析
编译成功后会生成一个u-boot.map
文件,其中包括了各段的具体地址,结合该.lds
文件分析可以得到各段的地体地址
1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") |
二、初始化流程
详见思维导图:U-Boot启动流程.xmind
三、u-boot命令实现
-
所有命令通过结构体
cmd_tbl_t
进行封装(在include/command.h
中定义)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
char *usage; /* Usage message (short) */
char *help; /* Help message (long) */
/* do auto completion on the arguments */
int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
};
typedef struct cmd_tbl_s cmd_tbl_t; -
所有命令通过结构体中的
name
成员进行查找 -
这些结构体存在一个uboot自定义的段内:
1
2
3
4
5
6
7
8SECTIONS
{
...
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*))); //uboot命令存放段
}
...
} -
通过宏
U_BOOT_CMD
可以快速定义这些命令结构体:
1 | U_BOOT_CMD( |
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
);
四、一般的移植思路
- 芯片厂商或者uboot官网找到uboot的源码
- 直接在
configs/
下复制类似芯片或板卡的xxx_defconfig
默认配置文件 - 在
include/configs
下找到类似芯片或板卡的配置头文件,直接复制 - 在
board/
下找到类似芯片或板卡的板级文件夹,直接复制