uboot
本章感觉没有什么现场操作,但需要耐心观看,丰富自己的基础知识。
U-Boot 工程目录分析
U-Boot 工程还包括编译之后的相关文件,文件夹或文件的含义见表:
我们要关注一部分文件夹或文件即可:
1、 arch 文件夹
(1)arch 文件夹存放着和架构有关的文件,比如 arm、 avr32、 m68k 等。
(2)arm 文件夹中存放CPU 有关的文件, I.MX6ULL对应imx-common这个文件夹;cpu文件夹也是和 cpu 架
构有关的。
(3)cpu文件夹存放多种 ARM 架构相关的文件夹,I.MX6ULL 使用的 Cortex-A7 内核,Cortex-A7 属于 armv7;uboot.lds是uboot的链接脚本文件,需要重点关注。
2、 board 文件夹
(1)board 文件夹就是和具体的板子有关的,I.MX 系列以前属于 freescale,只是freescale 后来被 NXP 收购了。
(2)freescale 文件夹存放相关芯片的,mx6ul开头的表示使用I.MX6UL 芯片的板子,mx6ullevk 是 NXP官方的 I.MX6ULL开发板。
3、 configs 文件夹
configs 文件夹为 uboot 配置文件, uboot 是可配置的,官方做好的配置文件基础上可以来添加自己想要的功能,这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”,xxx 表示开发板名字。
在编译 uboot 之前一定要使用 defconfig 来配置 uboot!
4、 .u-boot.xxx_cmd 文件
.u-boot.xxx_cmd 是一系列的文件,这些文件都是编译生成的,都是一些命令文件,一般就是.u-boot.xxx.cmd生成u-boot.xxx。
基本步骤都是:编译.c文件—>.elf文件—>链接.o文件
u-boot.imx 是在 u-boot.bin 文件的头部添加了 IVT、 DCD 等信息(IVT、 DCD 等数据保存在了文件
board/freescale/mx6ullevk/imximage-ddr512.cfg.cfgtmp )。MFGTools 工具将u-boot.imx 烧写到开发板。
5*、 Makefile 文件
顶层 Makefile 文件, Makefile 是支持嵌套的,也就是顶层 Makefile 可以调用子目录中的 Makefile 文件。(俗称套娃)
uboot 源码根目录下的 Makefile 是顶层 Makefile,他会调用其它的模块的 Makefile 文件,比如 drivers/adc/Makefile。
6、 u-boot.xxx 文件
u-boot.xxx 同样也是一系列文件:
u-boot:编译出来的 ELF 格式的 uboot 镜像文件。
u-boot.bin:编译出来的二进制格式的 uboot 可执行镜像文件。
u-boot.cfg: uboot 的另外一种配置文件。
u-boot.imx: u-boot.bin 添加头部信息以后的文件, NXP 的 CPU 专用文件。
u-boot.lds:链接脚本。
u-boot.map: uboot 映射文件,通过查看此文件可以知道某个函数被链接到了哪个地址上。
u-boot.srec: S-Record 格式的镜像文件。
u-boot.sym: uboot 符号文件。
u-boot-nodtb.bin:和 u-boot.bin 一样, u-boot.bin 就是 u-boot-nodtb.bin 的复制文件。
7、 .config 文件
uboot 配置文件, 使用命令“make xxx_defconfig”配置 uboot 以后就会自动生成。
.config 文件中都是以“CONFIG_”开始的配置项,这些配置项就是 Makefile 中的变量,因此后面都跟有相应的值, **uboot 的顶层 Makefile 或子 Makefile 会调用这些变量值。**在.config 中会有大量的变量值为‘y’,这些为‘y’的变量一般用于控制某项功能是否使能,为‘y’的话就表示功能使能。
比如 cmd/Makefile中:
obj-$(CONFIG_CMD_BOOTM) += bootm.o
#相当于
obj-y += bootm.o
obj-y 包含着所有要编译的文件对应的.o 文件,这里表示需要编译文件 cmd/bootm.c(总的来说就是obj-y变量中存放着需要编译的文件,谁要被编译,就放在这个变量中)。相当于通过“CONFIG_CMD_BOOTD=y”来使能 bootm 这个命令,进而编译 cmd/bootm.c 这个文件,这个文件实现了命令 bootm。在 uboot 和 Linux 内核中都是采用这种方法来选择使能某个功能,编译对应的源码文件。
8、 README
文件描述了 uboot 的详细信息,包括 uboot 该如何编译、 uboot 中各文件夹的含义、相应的命令等等。
VScode 工程创建
本节这是学到一种VScode的使用方法:在工程目录中和搜索内容中,隐藏指定文件。
(1)在根目录新建工作区uboot.code-workspace。
(2)新建.vscode 文件夹下的settings.json的文件:
发现 arch 目录下没有 avr32 这个文件夹,因为被隐藏了。
{
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"arch/avr32":true,
},
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"arch/avr32":true,
}
}
U-Boot 顶层 Makefile 分析
在阅读 uboot 源码之前,肯定是要先看一下顶层 Makefile,分析 gcc 版本代码的时候一定是先从顶层 Makefile 开始的,然后再是子 Makefile,这样通过层层分析 Makefile 即可了解整个工程的组织结构。
版本号
5 VERSION = 2016
6 PATCHLEVEL = 03
7 SUBLEVEL =
8 EXTRAVERSION =
9 NAME =
VERSION 是主版本号,
PATCHLEVEL 是补丁版本号,
SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2016.03”。
EXTRAVERSION 是附加版本信息,
NAME 是和名字有关的,一般不使用这两个。
MAKEFLAGS 变量
主目录中的 Makefile 调用子目录中的 Makefile:
调用子 make:
$(MAKE) -C subdir
$(MAKE)就是调用“make”命令,
-C 指定子目录。
subdir 子目录
向子 make 传递变量:
export VARIABLE …… #导出变量给子 make 。
unexport VARIABLE…… #不导出变量给子 make。
有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个 make的执行过程中,它们的值始终自动的传递给子 make。
命令输出
uboot 可以在终端显示短命令(默认)或完整命令(V=1)。
话不多说,直接上代码:
73 ifeq ("$(origin V)", "command line")
74 KBUILD_VERBOSE = $(V)
75 endif
76 ifndef KBUILD_VERBOSE
77 KBUILD_VERBOSE = 0
78 endif
79
80 ifeq ($(KBUILD_VERBOSE),1)
81 quiet =
82 Q =
83 else
84 quiet=quiet_
85 Q = @
86 endif
通过V的值确定quiet和Q的值,决定命令行输出格式,
对于Q:
$(Q)$(MAKE) $(build)=tools
#有@不会在终端输出命令
#无@会在终端输出命令
对于quiet:
#变量 quiet 为空的话,整个命令都会输出。
#变量 quiet 为“quiet_”的话,仅输出短版本。
#变量 quiet 为“silent_”的话,整个命令都不会输出()。
设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出。啊~ 这 ~ ,目前有个疑惑,暂定为quiet比@优先级高。
静默输出
编译的时候使用“make -s”即可实现静默输出,编译 uboot 的时候不需要输出命令。
88 # If the user is running make -s (silent mode), suppress echoing of
89 # commands
90
91 ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
92 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
93 quiet=silent_
94 endif
95 else # make-3.8x
96 ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
97 quiet=silent_
98 endif
99 endif
100
101 export quiet Q KBUILD_VERBOSE
就是根据make -s,检测到-s,quiet=silent_则静默输出。
设置编译结果输出目录
uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,
比如 “make O=out”就是设置目标文件输出到 out 目录中 。为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定 O 参数。来看Makefile顶层文件中的手法:
#判断“O”是否来自于命令行,如果来自命令行的话条件成立, KBUILD_OUTPUT就为$(O),
#因此变量 KBUILD_OUTPUT 就是输出目录。
124 ifeq ("$(origin O)", "command line")
125 KBUILD_OUTPUT := $(O)
126 endif
#判断 KBUILD_OUTPUT 是否为空。
#调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。
135 ifneq ($(KBUILD_OUTPUT),)
......
138 saved-output := $(KBUILD_OUTPUT)
139 KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT)&& /bin/pwd)
......
155 endif # ifneq ($(KBUILD_OUTPUT),)
代码检查
uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文
件。
判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量
KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0。
命令行老手法了!!!
176 ifeq ("$(origin C)", "command line")
177 KBUILD_CHECKSRC = $(C)
178 endif
179 ifndef KBUILD_CHECKSRC
180 KBUILD_CHECKSRC = 0
181 endif
模块编译
在 uboot 中允许单独编译某个模块。
使用命令“ make M=dir”即可,旧语法“ make SUBDIRS=dir”也是支持的。
186 ifdef SUBDIRS
187 KBUILD_EXTMOD ?= $(SUBDIRS)
188 endif
189
190 ifeq ("$(origin M)", "command line")
191 KBUILD_EXTMOD := $(M)
192 endif
196 PHONY += all
197 ifeq ($(KBUILD_EXTMOD),)
198 _all: all
199 else
200 _all: modules
201 endif
202
203 ifeq ($(KBUILD_SRC),)
204 # building in the source tree
205 srctree := .
206 else
207 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
208 # building in a subdirectory of the source tree
209 srctree := ..
210 else
211 srctree := $(KBUILD_SRC)
212 endif
213 endif
214 objtree := .
215 src := $(srctree)
216 obj := $(objtree)
217
218 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
219
220 export srctree objtree VPATH
第 197 行判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块。 一般情况下我们不会在 uboot 中编译模块,所以此处会编译 all 这个目标。
第 203 行判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即srctree 为“.” ,一般不设置 KBUILD_SRC。
获取主机架构和系统
顶层 Makefile 会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下:
227 HOSTARCH := $(shell uname -m | \
228 sed -e s/i.86/x86/ \
229 -e s/sun4u/sparc64/ \
230 -e s/arm.*/arm/ \
231 -e s/sa110/arm/ \
232 -e s/ppc64/powerpc/ \
233 -e s/ppc/powerpc/ \
234 -e s/macppc/powerpc/\
235 -e s/sh.*/sh/)
236
237 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
238 sed -e 's/\(cygwin\).*/cygwin/')
239
240 export HOSTARCH HOSTOS
第 227 行定义了一个变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称。
shell 中的“|”表示管道,意思是将左边的输出作为右边的输入,
sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”。
第 237 行定义了变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“name -
s”来获取主机 OS。
tr ‘[:upper:]’ ‘[:lower:]’”表示将所有的大写字母替换为小写字母,因此得到“linux”。
“sed -e ‘s/(cygwin).*/cygwin/’”的输入,用于将cygwin.*替换为 cygwin。
设置目标架构、交叉编译器和配置文件
编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 。
“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE:
244 # set default to nothing for native builds
245 ifeq ($(HOSTARCH),$(ARCH))
246 CROSS_COMPILE ?=
247 endif
248
249 KCONFIG_CONFIG ?= .config
250 export KCONFIG_CONFIG
第 245 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROS_COMPILE= arm-linuxgnueabihf- 。
每次编译 uboot 的时候都要在 make 命令后面设置ARCH 和 CROS_COMPILE,使用起来很麻烦,可以直接修改顶层 Makefile,在里面加入 ARCH和 CROSS_COMPILE 的定义,这就是之前我们遇到过的问题,上一篇笔记编译uboot编译报错的原因就是总找不到交叉编译器。
config 默认是没有的,需要使用命令“make xxx_defconfig”对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。
默认情况下.config 和xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。如果后续自行调整了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。相当于 xxx_defconfig 只是一些初始配置,而.config 里面的才是实时有效的配置。
调用 scripts/Kbuild.include
主 Makefile 会调用文件 scripts/Kbuild.include 这个文件,此文件里面定义了很多变量,后边边用边学。
交叉编译工具变量设置
顶层 Makefile 中相关代码如下:
333 AS = $(CROSS_COMPILE)as
335 ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
336 LD = $(CROSS_COMPILE)ld.bfd
337 else
338 LD = $(CROSS_COMPILE)ld
339 endif
340 CC = $(CROSS_COMPILE)gcc
341 CPP = $(CC) -E
342 AR = $(CROSS_COMPILE)ar
343 NM = $(CROSS_COMPILE)nm
344 LDR = $(CROSS_COMPILE)ldr
345 STRIP = $(CROSS_COMPILE)strip
346 OBJCOPY = $(CROSS_COMPILE)objcopy
347 OBJDUMP = $(CROSS_COMPILE)objdump
这些命令应该很熟悉,在进行裸机开发时,手写Makefile就是为了省事,设置了这些交叉编译工具变量如LD、CC、OBJDUMP、OBJCOPY等。
导出其他变量
在顶层 Makefile 会导出很多变量,重点来看一下下面这几个变量:
ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
make xxx_defconfig 过程
太多了,目前消受不了,等未知时间后再学习。
make命令流程
重点是“make xxx_defconfig”和“make”这两个命令的执行流程:
(1)、make xxx_defconfig: 用于配置 uboot,这个命令最主要的目的就是生成.config 文件。
(2)、make:用于编译 uboot,这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件,比如 u-boot.imx 等等。
总结:重点是使用 uboot,而不是 uboot 的研究者,我们要做的是缕清 uboot 的流程。秉承一开始学习stm32时候的原则,能看懂是什么功能,会用就行,太多了,要被劝退了!