当前 uboot 的配置已经完全变成 Linux 内核的配置形式了,完全可以按照 Linux 内核的分析方法分析 uboot。
uboot 和 Linux 的代码配置项由 Kconfig 来完成的,关于 Kconfig 语法,可参见:linux/Documentation/kbuild/kconfig-language.txt
在编译时,先进行配置,目的是根据需求选择功能以及编译生成方式类型(模式或包含在内核中)。
配置项操作可以由如下命令中的其中一条来完成:
make config
:这个要求用户手动选择所有配置项,配置完成后生成 .config 文件。make menuconfig
:显示以curses的图形配置菜单,当已有 .config 文件存在时,它会根据 .config 文件设定默认项。若不存在 .config 文件,则根据各级 Kconfig 文件来设定菜单项。完成配置后,生成.config文件。make oldconfig
:与 make menuconfig 相同,区别在于这个没有图形界面,当已有 .config 文件存在时,它根据.config文件设定默认项,若 kconfig 有新配置项时,会提示你进行选择;若不存在 .config 文件,则根据各级 Kconfig 文件来设定菜单项。完成配置后,生成 .config 文件。在执行完其中一条 config 命令后,会生成 .config 及 autoconf 文件,autoconf 是根据配置项生成的相应宏定义,供 makefile 使用,当执行 make 命令时,就会根据 autoconf 定义的宏及 makefile 去编译源码。
u-boot
的编译跟kernel
编译一样,分两步执行:
1、第一步:配置,执行make xxx_defconfig
进行各项配置,生成.config
文件 。
2、第二步:编译,执行make
进行编译,生成可执行的二进制文件u-boot.bin
或u-boot.elf
。
make targets,targets 就是我们前述的那些命令,我们可以通过 make help 打印出来内核构建系统所支持的目标完整列表。
如下所示,是 make help 所打印的所有目标:
onlylove@ubuntu:~/linux/uboot/alientek_uboot$ make help
Cleaning targets:
clean - Remove most generated files but keep the config(清除大多数生成的文件,但是保留配置)
mrproper - Remove all generated files + config + various backup files(清除所有生成的文件、配置文件和各种备份文件)
distclean - mrproper + remove editor backup and patch files(此项就是 mrproper 的升级版本,多加了清除备份和补丁文件)
Configuration targets:
config - Update current config utilising a line-oriented program(利用命令行更新当前配置)
nconfig - Update current config utilising a ncurses menu based program(使用基于ncurses菜单的程序更新当前配置)
menuconfig - Update current config utilising a menu based program(利用菜单程序更新当前配置)
xconfig - Update current config utilising a QT based front-end(利用基于Qt的前端更新当前配置)
gconfig - Update current config utilising a GTK based front-end(利用基于GTK +的前端更新当前配置)
oldconfig - Update current config utilising a provided .config as base(使用提供的.config作为基础更新当前配置)
localmodconfig - Update current config disabling modules not loaded(更新当前配置禁用未加载的模块)
localyesconfig - Update current config converting local mods to core(更新当前配置将本地mods转换为core)
silentoldconfig - Same as oldconfig, but quietly, additionally update deps(与oldconfig相同,但是安静地更新deps)
defconfig - New config with default from ARCH supplied defconfig(来自ARCH的默认配置提供了defconfig)
savedefconfig - Save current config as ./defconfig (minimal config)(保存当前配置为./defconfig(最小配置))
allnoconfig - New config where all options are answered with no(新的配置,所有选项的答案都是no)
allyesconfig - New config where all options are accepted with yes(新的配置,所有选项的答案都是yes)
allmodconfig - New config selecting modules when possible(新的配置,尽可能的选择模块)
alldefconfig - New config with all symbols set to default(将所有符号设置为默认值的新配置)
randconfig - New config with random answer to all options(对所有选项进行随机答案的新配置)
listnewconfig - List new options(列出新选项)
olddefconfig - Same as silentoldconfig but sets new symbols to their default value(与 silentoldconfig 相同,但是对默认值设置新的符号)
Other generic targets:
all - Build all necessary images depending on configuration(根据配置构建所有必要的镜像)
* u-boot - Build the bare u-boot(构建空的 uboot)
dir/ - Build all files in dir and below(在dir和子目录下构建所有文件)
dir/file.[oisS] - Build specified target only(仅构建指定的目标)
dir/file.lst - Build specified mixed source/assembly target only(仅构建指定的混合源/汇编目标)
(requires a recent binutils and recent build (System.map))(需要最新的binutils和最新的版本(System.map))
tags/ctags - Generate ctags file for editors(生成 ctags 索引文件)
etags - Generate etags file for editors(生成 etags 索引文件)
cscope - Generate cscope index(生成 cscope 索引)
ubootrelease - Output the release version string (use with make -s)(输出发行版本字符串(需要用 make -s))
ubootversion - Output the version stored in Makefile (use with make -s)(输出保存在 Makefile 中的版本(需要用 make -s))
Static analysers
checkstack - Generate a list of stack hogs(生成堆栈列表)
Documentation targets:
U-Boot bootloader internal documentation in different formats:
htmldocs - HTML
pdfdocs - PDF
psdocs - Postscript
xmldocs - XML DocBook
mandocs - man pages
installmandocs - install man pages generated by mandocs
cleandocs - clean all generated DocBook files
make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build
make V=2 [targets] 2 => give reason for rebuild of target
make O=dir [targets] Locate all output files in "dir", including .config
make C=1 [targets] Check all c source with $CHECK (sparse by default)
make C=2 [targets] Force check of all c source with $CHECK
make RECORDMCOUNT_WARN=1 [targets] Warn about ignored mcount sections
make W=n [targets] Enable extra gcc checks, n=1,2,3 where
1: warnings which may be relevant and do not occur too often
2: warnings which occur quite often but may still be relevant
3: more obscure warnings, can most likely be ignored
Multiple levels can be combined with W=12 or W=123
Execute "make" or "make all" to build all targets marked with [*]
For further info see the ./README file
onlylove@ubuntu:~/linux/uboot/alientek_uboot$
在 ./Document/kbuild/makefiles.txt
中有详细介绍。
Makefiles 有 5 个部分:
顶层 Makefiles 读取从uboot 配置程序中生成的 .config 文件。
在上述的所有文件中,除了顶层的 Makefile,其他文件都或直接、或间接的和 它相关联。这些关联可以分为两类:
1、直接包含:在一个文件中,用 include 来包含另外的文件
2、间接包含:使用 make -f 来调用,-f 是使用不同的 makefile 文件来进行 make 的选项。
使用 NXP 官方提供uboot,使用 iMX6ULL 芯片进行测试,执行命令:make V=1 mx6ull_14x14_lq_emmc_defconfig
。
onlylove@ubuntu:~/linux/uboot/nxp_uboot/uboot-imx-rel_imx_4.1.15_2.1.0_ga$ make V=1 mx6ull_14x14_lq_emmc_defconfig
# 生成了 scripts/basic/fixdep 工具
make -f ./scripts/Makefile.build obj=scripts/basic
cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c
rm -f .tmp_quiet_recordmcount
# 生成了 scripts/kconfig/conf 工具
make -f ./scripts/Makefile.build obj=scripts/kconfig mx6ull_14x14_lq_emmc_defconfig
cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="" -DNCURSES_WIDECHAR=1 -DLOCALE -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c
cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c
cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c
cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="" -DNCURSES_WIDECHAR=1 -DLOCALE -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c
# 执行 scripts/kconfig/conf 工具 生成.config
scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_lq_emmc_defconfig Kconfig
#
# configuration written to .config
#
通过以上输出日志,可以确定 make xxx_defconfig
的执行主要分成三个部分:
1、执行 make -f ./scripts/Makefile.build obj=scripts/basic
,编译生成 scripts/basic/fixdep
工具。
2、执行 make -f ./scripts/Makefile.build obj=scripts/kconfig mx6ull_14x14_lq_emmc_defconfig
, 编译生成 scripts/kconfig/conf
工具。
3、执行 scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_lq_emmc_defconfig Kconfig
,生成最终的 .config 配置文件。
make xxx_defconfig
的执行主要分成三个部分:
1、执行 make -f ./scripts/Makefile.build obj=scripts/basic
,编译生成 scripts/basic/fixdep
工具。
2、执行 make -f ./scripts/Makefile.build obj=scripts/kconfig mx6ull_14x14_lq_emmc_defconfig
, 编译生成 scripts/kconfig/conf
工具。
3、执行 scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_lq_emmc_defconfig Kconfig
,生成最终的 .config 配置文件。
执行 make xxx_defconfig
命令时,u-boot 根目录下的 Makefile 中有唯一的规则匹配目标:
ifeq ($(config-targets),1)
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target
KBUILD_DEFCONFIG := sandbox_defconfig
export KBUILD_DEFCONFIG KBUILD_KCONFIG
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
注释意思为,仅限 *config 目标,确保先决条件已经更新,并在 scripts/kconfig 下创建 *config 目标。
上面有两个变量 config 和 %config,% 符号为通配符,对应所有的 xxxconfig 目标。我们的 make xxx_defconfig 就对应 %config,我们并没有执行 make config 命令。
KBUILD_EXTMODE
赋值代码:
# Use make M=dir to specify directory of external module to build
# 使用make M=dir指定要构建的外部模块的目录
# Old syntax make ... SUBDIRS=$PWD is still supported
# 旧语法make…SUBDIRS=$PWD仍然支持
# Setting the environment variable KBUILD_EXTMOD take precedence
# 优先设置环境变量KBUILD EXTMOD
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
由注释可以知道,SUBDIRS 这个变量是通过执行 make 的时候传进来的,我们并没有执行此项,所以未定义SUBDIRS,第一个分支不会去走。第二个 if 语句为 ifeq 语句,这里使用 origin 函数。
$(origin <variable>)
函数名称: 获取变量信息— origin。
函数功能: 获取此变量(参数)相关的信息,告诉我们这个变量的出处(定义方式)。
返回值:
ifeq ("$(origin M)", "command line")
语句含义,如果make传入的命令行变量存在且是M,那么,变量KBUILD_EXTMOD变为变量M的值。
第一阶段中我们并没有传入 M 值,则 KBUILD_EXTMOD 值为空。
# To make sure we do not include .config for any of the *config targets
# catch them early, and hand them over to scripts/kconfig/Makefile
# It is allowed to specify more targets when calling make, including
# mixing *config targets and build targets.
# 为了确保我们不为任何配置目标包含.config,尽早捕获它们,
# 并将它们交给scripts/kconfig/Makefile。
# 当调用make时,允许指定更多的目标,包括混合配置目标和构建目标。
# For example 'make oldconfig all'. 例如'make oldconfig all'
# Detect when mixed targets is specified, and make a second invocation
# of make so .config is not included in this case either (for *config).
# 检测何时指定了混合目标,并进行第二次make so调用,在这种情况下也不包括.config(对于config)。
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
no-dot-config-targets := clean clobber mrproper distclean \
help %docs check% coccicheck \
ubootversion backup
config-targets := 0
mixed-targets := 0
dot-config := 1
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
dot-config := 0
endif
endif
ifeq ($(KBUILD_EXTMOD),)
ifneq ($(filter config %config,$(MAKECMDGOALS)),)
config-targets := 1
ifneq ($(words $(MAKECMDGOALS)),1)
mixed-targets := 1
endif
endif
endif
$(filter PATTERN…,TEXT)
函数名称: 过滤函数— filter。
函数功能: 过滤掉字串“ TEXT”中所有不符合模式“ PATTERN”的单词,保留所
有符合此模式的单词。可以使用多个模式。模式中一般需要包含模式字
符“ %”。存在多个模式时,模式表达式之间使用空格分割。
返回值:空格分割的“ TEXT”字串中所有符合模式“ PATTERN”的字串。
函数说明:“ filter”函数可以用来去除一个变量中的某些字符串
$(filter-out PATTERN...,TEXT)
函数名称: 反过滤函数— filter-out
函数功能: 和“ filter”函数实现的功能相反。过滤掉字串“ TEXT”中所有符合模式“ PATTERN”的单词,保留所有不符合此模式的单词。
可以有多个模式。存在多个模式时,模式表达式之间使用空格分割
返回值: 空格分割的“ TEXT”字串中所有不符合模式“ PATTERN”的字串。
函数说明: “ filter-out”函数也可以用来去除一个变量中的某些字符串,(实现和“ filter”函数相反)
$(words TEXT)
函数名称: 统计单词数目函数 — words
函数功能: 计算字串 “TEXT” 中单词数目。
返回值: “TEXT” 中单词数。
函数说明:
代码执行过程分析:
1、ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
函数含义为,如果 过滤掉 MAKECMDGOALS 不符合 no-dot-config-targets 后结果不为空,则执行分支语句。很显然过滤后为空,则不执行分支语句,dot-config 依然 值为1。
2、ifeq ($(KBUILD_EXTMOD),)
含义为,如果 KBUILD_EXTMOD
为空,执行分支语句。因为 KBUILD_EXTMOD
为空,因此分支语句被执行。
3、 ifneq ($(filter config %config,$(MAKECMDGOALS)),)
含义为,滤掉 MAKECMDGOALS 不符合 config 和 %config 模式的字符串,因此返回值为 xxx_defconfig,xxx_defconfig 再与 空进行比较,if 语句为 ifneq ,很显然, filter 语句不为空,与空进行比较,满足 ifneq 执行语句。
4、config-targets := 1
含义为将 config-targets 重新赋值为 1。
5、ifneq ($(words $(MAKECMDGOALS)),1)
含义为,通过统计 MAKECMDGOALS 中单词数,返回 1。ifneq
判断 MAKECMDGOALS 中单词数为1,不执行分支语句。mixed-targets 的值依旧为 0。
6、分析结束后,变量值如下:
MAKECMDGOALS = xxx_defconfig
KBUILD_EXTMOD =
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
no-dot-config-targets := clean clobber mrproper distclean help %docs check% coccicheck ubootversion backup tests
config-targets := 1
mixed-targets := 0
dot-config := 1
因为 config-targets 的值等于1,因此 %config 规则可以执行。
ifeq ($(config-targets),1)
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target
KBUILD_DEFCONFIG := sandbox_defconfig
export KBUILD_DEFCONFIG KBUILD_KCONFIG
config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
else
# 不会执行,因此省略
endif
1、定义2个环境变量,KBUILD_DEFCONFIG := sandbox_defconfig
和KBUILD_KCONFIG
为空。
2、config
没有匹配,不会执行。
3、%config
依赖scripts_basic
outputmakefile
FORCE
。
PHONY += FORCE
FORCE:
FORCE
被定义为一个空目标。如果一个目标添加 FORCE
依赖,每次编译都会去先去执行 FORCE
(实际上什么都不做),然后运行命令更新目标,这样就能确保目标每次都会被更新。
# ===========================================================================
# Rules shared between *config targets and build targets
# 在配置目标和构建目标之间共享的规则
# Basic helpers built in scripts/
# 脚本中内置的基本帮助程序
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
# To avoid any implicit rule to kick in, define an empty command.
# 要避免引入任何隐式规则,请定义一个空命令。
scripts/basic/%: scripts_basic ;
Q = @
,MAKE = make
,build
变量的定义在 scripts/Kbuild.include
文件中。
主 Makefile
包含 scripts/Kbuild.include
文件,详细内容如下:
# We need some generic definitions (do not try to remake the file).
# 我们需要一些通用定义(不要尝试重新创建文件)。
scripts/Kbuild.include: ;
include scripts/Kbuild.include
scripts/Kbuild.include
文件中包含如下内容:
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj
srctree 定义在主 Makefile 中:
ifeq ($(KBUILD_SRC),)
# building in the source tree 在源代码树中构建
srctree := .
else
ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
# building in a subdirectory of the source tree 在源树的子目录中构建
srctree := ..
else
srctree := $(KBUILD_SRC)
endif
endif
KBUILD_SRC:make 时使用 O 输入源码构建目录,在执行时没有传入,因此为空。因此执行 srctree := . 语句。
主 Makefile 中 scripts_basic 的展开为:
scripts_basic:
# 根据传入的 obj 参数显示的执行 ./scripts/Makefile.build 文件
make -f ./scripts/Makefile.build obj=scripts/basic
rm -f .tmp_quiet_recordmcount
PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
# 如果使用单独的输出目录,
# 则outputmakefile在输出目录中生成一个Makefile。
# 这允许在输出目录中方便地使用make。
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
KBUILD_SRC 为空,所以ifneq 中的语句不会执行。 outputmakefile 为空。
%config 展开后,规则如下。
make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
MAKECMDGOALS = xxx_defconfig
KBUILD_EXTMOD =
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
no-dot-config-targets := clean clobber mrproper distclean help %docs check% coccicheck ubootversion backup tests
config-targets := 1
mixed-targets := 0
dot-config := 1
KBUILD_SRC =
build := -f ./scripts/Makefile.build obj
KBUILD_DEFCONFIG := sandbox_defconfig
KBUILD_KCONFIG =
1、make -f ./scripts/Makefile.build obj=scripts/basic(scripts_basic规则中执行)。
2、rm -f .tmp_quiet_recordmcount(scripts_basic规则中执行)。
3、make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig(%config规则中执行)。
make -f scripts/Makefile.build obj=scripts/basic 命令由于没有指定目标,所以会在 script/Makefile.build 中处理默认目标__build:
# We keep a list of all modules in $(MODVERDIR)
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
在 scripts/Makefile.build 中会包含进 scripts/basic 目录下的 Kbuild/Makefile,,所以该make命令的实际效果是去编译出 scripts/basic 目录下的三个 host program,也就是 fixdep docproc和hash。
# The filename Kbuild has precedence over Makefile
# 文件Kbuild优先于Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
什么是host program?一般认为是和内核无关,但是要在编译过程中使用的工具程序。关于这些程序的编译,参考 scripts/Makefile.host 文件,以及 Documentation/kbuild/makefile.txt 文件中关于 host program 的这一节。
scripts/basic 文件夹中的 Makefile 内容如下:
hostprogs-y := fixdep
always := $(hostprogs-y)
# fixdep is needed to compile other host programs
$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep
文件 scripts/Makefile.build 会包含 obj 变量所指代目录内的 Makefile 的,在这里就是 script/kconfig/Makefile。
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
# Added for U-Boot (backward compatibility)
%_config: %_defconfig
@:
在这里,xxx_defconfig 需要依赖于同目录下的conf程序。这其实就是 Linux 内核进行Kconfig操作的主程序之一了,类似的还有mconf,qconf和gconf等。他们其实都是 host program。关于它们是如何被编译出来的,还请参见 scripts/kconfig/Makefile 文件,主要是借助于 bison,flex 和 gperf 三个工具来生成c源程序文件,之后再编译出来的。
kconfig 的定义在 scripts/kconfig 文件夹中的 Makefile 内容中:
ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := Kconfig
endif
ifeq ($(quiet),silent_)
silent := -s
endif
由于变量 KBUILD_KCONFIG 在arm架构Makefile中没有被定义,所以 Kconfig 被定义成 arch/arm/kconfig。silent 是确定是否执行静态编译。所以这个目标的规则就简化成:
$(obj)/conf -s --defconfig=arch/arm/configs/xxx_defconfig arch/arm/Kconfig
这个命令就是读取并解析以 arch/arm/Kconfig 为首的内核功能选项配置文件,并将文件 arch/arm/configs/xxx_defconfig 所设置的默认值分配给对应的所有选项,最终生成配置文件 .config。