大概内容如下:
就是启动内核:
在u-boot目录下执行"tree . -d > 1.txt",可以得到目录的结构,精简如下:
├── arch
│ ├── arm // 1. 架构相关
│ │ ├── cpu
│ │ │ ├── armv7
│ │ │ │ ├── mx6
│ │ ├── dts
│ │ │ └── include
│ │ │ └── dt-bindings -> ../../../../include/dt-bindings
│ │ ├── include
│ │ │ ├── asm
│ │ │ │ ├── arch-imx
│ │ │ │ ├── arch-imx8
│ │ │ │ ├── arch-imx8m
│ │ │ │ ├── imx-common
│ │ │ └── debug
│ │ ├── lib
│ │ ├── mach-rockchip
│ │ │ ├── rk3036
│ │ │ ├── rk3288
│ │ │ └── rk3399
│ │ ├── lib
├── board // 单板相关
│ ├── freescale
│ │ ├── common
│ │ │ └── p_corenet
│ │ ├── corenet_ds
│ │ ├── mx6ul_14x14_ddr3_arm2
│ │ ├── mx6ul_14x14_evk
│ │ ├── mx6ul_14x14_lpddr2_arm2
│ │ ├── mx6ull_ddr3_arm2
│ │ ├── mx6ullevk
├── cmd // 通用的命令
│ ├── fastboot
│ └── mvebu
├── common // 通用的
│ ├── eeprom
│ ├── init
│ └── spl
├── configs
├── disk
├── drivers // 各类驱动
├── fs // 文件系统
│ ├── cbfs
│ ├── cramfs
│ ├── ext4
│ ├── fat
│ ├── jffs2
│ ├── reiserfs
│ ├── sandbox
│ ├── ubifs
│ ├── yaffs2
│ └── zfs
├── include
├── lib // 库
├── net // 网络协议
文档:本Git仓库01_all_series_quickstart\04_嵌入式Linux应用开发基础知识\doc_pic\04.2018_Makefile
打印Makefile的规则和变量:make -p
可以把make命令规则和变量存入文件:make -p > 1.txt
然后执行vi 1.txt,使用vi命令删除注释::g/^#/d
IMX6ULL: make mx6ull_14x14_evk_defconfig
STM32MP157: make stm32mp15_trusted_defconfig
执行过程:

分析过程:
mx6ull_14x14_evk_defconfig: scripts/kconfig/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
就是:
UBOOTVERSION=2017.03 scripts/kconfig/conf --defconfig=arch/../configs/mx6ull_14x14_evk_defconfig Kconfig
总体分析:scripts/kconfig/conf.c
更详细的配置过程:make mx6ull_14x14_evk_defconfig V=1

defconfig_file = "arch/../configs/mx6ull_14x14_evk_defconfig";
name = "Kconfig"
conf_parse(name); // 解析uboot根目录下的Kconfig文件
conf_read(defconfig_file); // 读配置文件
conf_set_all_new_symbols(def_default); // 设置new_symbols为默认值
conf_write(NULL); // 写到.config
深入分析:scripts/kconfig/conf.c
参考文档:
任一个Linux内核的Documentation\kbuild\kconfig-language.rst
https://www.rt-thread.org/document/site/programming-manual/kconfig/kconfig/
对于各类内核,只要支持menuconfig配置界面,都是使用Kconfig。
在配置界面中,可以选择、设置选项,这些设置会保存在.config文件里。
编译脚本会包含.config,根据里面的值决定编译哪些文件、怎么编译文件。
.config文件也会被转换为头文件,C程序可以从头文件中获得配置信息。

问题:
在配置界面中操作的结果保存在.config文件中,示例如下:
#
# Automatically generated file; DO NOT EDIT.
# U-Boot Configuration
#
CONFIG_CREATE_ARCH_SYMLINK=y
# CONFIG_ARC is not set
CONFIG_ARM=y
# CONFIG_AVR32 is not set
# CONFIG_BLACKFIN is not set
# CONFIG_M68K is not set
# CONFIG_MICROBLAZE is not set
# CONFIG_MIPS is not set
# CONFIG_NDS32 is not set
# CONFIG_NIOS2 is not set
# CONFIG_OPENRISC is not set
# CONFIG_PPC is not set
# CONFIG_SANDBOX is not set
# CONFIG_SH is not set
# CONFIG_SPARC is not set
# CONFIG_X86 is not set
# CONFIG_XTENSA is not set
CONFIG_SYS_ARCH="arm"
CONFIG_SYS_CPU="armv7"
CONFIG_SYS_SOC="mx6"
CONFIG_SYS_VENDOR="freescale"
CONFIG_SYS_BOARD="mx6ullevk"
CONFIG_SYS_CONFIG_NAME="mx6ullevk"
#
# ARM architecture
#
CONFIG_HAS_VBAR=y
CONFIG_HAS_THUMB2=y
CONFIG_CPU_V7=y
CONFIG_SYS_ARM_ARCH=7
CONFIG_SYS_CACHE_SHIFT_6=y
CONFIG_SYS_CACHELINE_SIZE=64
编译脚本会包含.config文件,它会根据里面的变量比如CONFIG_CPU_V7选择u-boot特性。
在Kconfig文件中,假设配置项的名字是XXX,在.config文件中:
CONFIG_XXXCONFIG_=ABC,则对应的变量名为ABC_XXX在menuconfig界面,可以看到这个配置项:

在配置界面,使用方向箭头游走到Display information about the CPU during start up后,可以:
输入Y,选择配置项,在.config中对应CONFIG_DISPLAY_CPUINFO=y
输入N,不选择配置项,在.config中对应# CONFIG_DISPLAY_CPUINFO is not set
上图中的配置项怎么实现的?
在common/Kconfig文件中,它对应下列代码:

上面是一个精简的例子,完整的例子可以从Linux中获得,如下:
config SGI_SNSC
bool "SGI Altix system controller communication support"
depends on (IA64_SGI_SN2 || IA64_GENERIC)
default y
help
If you have an SGI Altix and you want to enable system
controller communication from user space (you want this!),
say Y. Otherwise, say N.
解释如下:
config option,这是Kconfig的基本entry;其他entry是用来管理config的。XXX选项就会被选中。menuconfig界面输入H键时,就会提示帮助信息。示例代码:rt-smart/kernel/src/Kconfig,代码如下:
menu "Boot media"
config NOR_BOOT
bool "Support for booting from NOR flash"
depends on NOR
help
Enabling this will make a U-Boot binary that is capable of being
booted via NOR. In this case we will enable certain pinmux early
as the ROM only partially sets up pinmux. We also default to using
NOR for environment.
config NAND_BOOT
bool "Support for booting from NAND flash"
default n
help
Enabling this will make a U-Boot binary that is capable of being
booted via NAND flash. This is not a must, some SoCs need this,
some not.
config ONENAND_BOOT
bool "Support for booting from ONENAND"
default n
help
Enabling this will make a U-Boot binary that is capable of being
booted via ONENAND. This is not a must, some SoCs need this,
some not.
config QSPI_BOOT
bool "Support for booting from QSPI flash"
default n
help
Enabling this will make a U-Boot binary that is capable of being
booted via QSPI flash. This is not a must, some SoCs need this,
some not.
config SATA_BOOT
bool "Support for booting from SATA"
default n
help
Enabling this will make a U-Boot binary that is capable of being
booted via SATA. This is not a must, some SoCs need this,
some not.
config SD_BOOT
bool "Support for booting from SD/EMMC"
default n
help
Enabling this will make a U-Boot binary that is capable of being
booted via SD/EMMC. This is not a must, some SoCs need this,
some not.
config SPI_BOOT
bool "Support for booting from SPI flash"
default n
help
Enabling this will make a U-Boot binary that is capable of being
booted via SPI flash. This is not a must, some SoCs need this,
some not.
endmenu
界面如下:

解释如下:
menu “xxx"表示一个菜单,菜单名是"xxx”
menu和endmenu之间的entry都是"xxx"菜单的选项
在上面的例子中子菜单有6个选项:

示例代码:rt-smart/kernel/src/Kconfig,代码如下:
config DEBUG_UART
bool "Enable an early debug UART for debugging"
help
The debug UART is intended for use very early in U-Boot to debug
problems when an ICE or other debug mechanism is not available.
choice
prompt "Select which UART will provide the debug UART"
depends on DEBUG_UART
default DEBUG_UART_NS16550
config DEBUG_UART_ALTERA_JTAGUART
bool "Altera JTAG UART"
help
Select this to enable a debug UART using the altera_jtag_uart driver.
You will need to provide parameters to make this work. The driver will
be available until the real driver model serial is running.
config DEBUG_UART_ALTERA_UART
bool "Altera UART"
help
Select this to enable a debug UART using the altera_uart driver.
You will need to provide parameters to make this work. The driver will
be available until the real driver model serial is running.
endchoice
界面如下:

在上述界面中,设置了DEBUG_UART,才有后续的"选择菜单"。
解释如下:
menuconfig XXX和config XXX类似,
唯一不同的是该选项除了能设置y/m/n外,还可以实现菜单效果(能回车进入该项内部)。
示例代码:Kconfig
menuconfig EXPERT
bool "Configure standard U-Boot features (expert users)"
default y
help
This option allows certain base U-Boot options and settings
to be disabled or tweaked. This is for specialized
environments which can tolerate a "non-standard" U-Boot.
Use this only if you really know what you are doing.
if EXPERT
config SYS_MALLOC_CLEAR_ON_INIT
bool "Init with zeros the memory reserved for malloc (slow)"
default y
help
This setting is enabled by default. The reserved malloc
memory is initialized with zeros, so first malloc calls
will return the pointer to the zeroed memory. But this
slows the boot time.
It is recommended to disable it, when CONFIG_SYS_MALLOC_LEN
value, has more than few MiB, e.g. when uses bzip2 or bmp logo.
Then the boot time can be significantly reduced.
Warning:
When disabling this, please check if malloc calls, maybe
should be replaced by calloc - if one expects zeroed memory.
config TOOLS_DEBUG
bool "Enable debug information for tools"
help
Enable generation of debug information for tools such as mkimage.
This can be used for debugging purposes. With debug information
it is possible to set breakpoints on particular lines, single-step
debug through the source code, etc.
endif # EXPERT
界面如下:

menuconfig常用格式有2种:
menuconfig M
if M
config C1
config C2
endif
或:
menuconfig M
config C1
depends on M
config C2
depends on M
第1项menuconfig M跟config M语法是一样的,
不同之处在于menuocnfig M后面可以跟着好几个依赖于M的config C1、config C2等子配置项。
在上面的menuconfig中就有if/endif的使用,它的语法如下:
"if" <expr>
<if block>
"endif"
示例如下,只有定义了的EXPERT项,SYS_MALLOC_CLEAR_ON_INIT等才会显示出来:
if EXPERT
config SYS_MALLOC_CLEAR_ON_INIT
bool "Init with zeros the memory reserved for malloc (slow)"
default y
help
This setting is enabled by default. The reserved malloc
memory is initialized with zeros, so first malloc calls
will return the pointer to the zeroed memory. But this
slows the boot time.
It is recommended to disable it, when CONFIG_SYS_MALLOC_LEN
value, has more than few MiB, e.g. when uses bzip2 or bmp logo.
Then the boot time can be significantly reduced.
Warning:
When disabling this, please check if malloc calls, maybe
should be replaced by calloc - if one expects zeroed memory.
config TOOLS_DEBUG
bool "Enable debug information for tools"
help
Enable generation of debug information for tools such as mkimage.
This can be used for debugging purposes. With debug information
it is possible to set breakpoints on particular lines, single-step
debug through the source code, etc.
endif # EXPERT
source 语句用于读取另一个文件中的 Kconfig 文件,
比如Kconfig中就包含了其他Kconfig:
source "arch/Kconfig"
comment 语句出现在界面的第一行,用于定义一些提示信息,如cmd/Kconfig:
comment "Commands"
界面如下:

config 100ASK
bool "test for 100ask"
default y
help
just for Kconfig test
menu "100ASK menu"
config IMX6ULL_TEST
tristate "menu test item 1 IMX6ULL"
help
just for test menu.
config STM32MP157_TEST
tristate "menu test item 2 IMX6ULLh"
help
just for test menu.
endmenu
config CHOICE_ON_100ASK
bool "Enable choice for 100ask"
help
Enable choice for 100ask
choice
prompt "choice test for 100ask"
depends on CHOICE_ON_100ASK
config CHOICE_ITEM1
bool "CHOICE_ITEM1"
help
CHOICE_ITEM1.
config CHOICE_ITEM2
bool "CHOICE_ITEM2"
help
CHOICE_ITEM2.
endchoice
menuconfig 100ASK_MENUCFG
bool "test for menuconfig of 100ask"
if 100ASK_MENUCFG
config IMX6ULL_TEST_MENUCFG
tristate "menuconfig test item 1 IMX6ULL"
help
just for test menuconfig.
config STM32MP157_MENUCFG
tristate "menuconfig test item 2 IMX6ULLh"
help
just for test menuconfig.
endif
menu "test menu"
config TEST_A
bool "TEST A"
default y
config TEST_B
bool "TEST B"
default y
endmenu
choice
prompt "test choise"
default TEST_C
config TEST_C
bool "TEST C"
default y
config TEST_D
bool "TEST D"
default y
endchoice
menuconfig TEST_MENUCONFIG
bool "test menuconfig"
default n
if TEST_MENUCONFIG
config TEST_E
bool "TEST E"
default y
config TEST_F
bool "TEST F"
default y
endif
IMX6ULL:
make mx6ull_14x14_evk_defconfig
make
STM32MP157:
make stm32mp15_trusted_defconfig
make
make过程
参考资料:https://www.cnblogs.com/jianhua1992/archive/2022/11/02/16852774.html
PHONY := _all
_all:
_all: all
KCONFIG_CONFIG ?= .config
# 定义一些常用变量
# 比如
# build := -f $(srctree)/scripts/Makefile.build obj
include scripts/Kbuild.include
# 定义交叉编译工具链
CC = $(CROSS_COMPILE)gcc
-include include/config/auto.conf
-include include/config/auto.conf.cmd
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
$(Q)touch include/config/auto.conf
-include include/autoconf.mk
-include include/autoconf.mk.dep
# 里面又包含架构相关、CPU相关的文件
# sinclude $(srctree)/arch/$(ARCH)/config.mk # include architecture dependend rules
# sinclude $(srctree)/$(CPUDIR)/config.mk # include CPU specific rules
include config.mk
# 架构相关参数、相关文件、库
# head-y := arch/arm/cpu/$(CPU)/start.o
# libs-y += arch/arm/cpu/$(CPU)/
include arch/$(ARCH)/Makefile
# 确定链接脚本
ifndef LDSCRIPT
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds
endif
endif
# 默认要处理的库的路径
libs-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-$(CONFIG_FMAN_ENET) += drivers/net/fm/
libs-$(CONFIG_SYS_FSL_DDR) += drivers/ddr/fsl/
libs-$(CONFIG_SYS_FSL_MMDC) += drivers/ddr/fsl/
libs-$(CONFIG_ALTERA_SDRAM) += drivers/ddr/altera/
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
$(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;
$(u-boot-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
# all目标要生成的依赖
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
all: $(ALL-y)
u-boot.hex u-boot.srec: u-boot FORCE
$(call if_changed,objcopy)
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
$(call cmd,smap)
$(call cmd,u-boot__) common/system_map.o
endif
上课时临时精简出来的Makefile:
ifeq ($(KBUILD_SRC),)
# That's our default target when none is given on the command line
PHONY := _all
_all:
endif # ifeq ($(KBUILD_SRC),)
# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)
PHONY += all
_all: all
HOSTCC = cc
# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.
KBUILD_MODULES :=
KBUILD_BUILTIN := 1
# 引入很多变量,
# 比如:
# build := -f $(srctree)/scripts/Makefile.build obj
include scripts/Kbuild.include
# 定义交叉编译工具链
AS = $(CROSS_COMPILE)as
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
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 := 0
mixed-targets := 0
dot-config := 1
ifeq ($(mixed-targets),1)
else
ifeq ($(config-targets),1)
else
PHONY += scripts
scripts: scripts_basic include/config/auto.conf
$(Q)$(MAKE) $(build)=$(@)
ifeq ($(dot-config),1)
-include include/config/auto.conf
-include include/config/auto.conf.cmd
# To avoid any implicit rule to kick in, define an empty command
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
@# If the following part fails, include/config/auto.conf should be
@# deleted so "make silentoldconfig" will be re-run on the next build.
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
@# include/config.h has been updated after "make silentoldconfig".
@# We need to touch include/config/auto.conf so it gets newer
@# than include/config.h.
@# Otherwise, 'make silentoldconfig' would be invoked twice.
$(Q)touch include/config/auto.conf
-include include/autoconf.mk
-include include/autoconf.mk.dep
前面生成了.config,但是它不是最终版本的配置文件。
顶层Makefile会包含2个配置文件:include/config/auto.conf、include/autoconf.mk。
u-boot中有非常多的配置文件:
.config:来自单板的默认配置、Kconfig
include/config/auto.conf:来自.config,去掉了很多注释
u-boot.cfg:它的内容跟头文件类似,来自
.config
头文件include/common.h,又包含了"#include
/* Automatically generated - do not edit */
#define CONFIG_IMX_CONFIG board/freescale/mx6ullevk/imximage.cfg
#define CONFIG_BOARDDIR board/freescale/mx6ullevk
#include
#include
#include
#include
#include
#include
include/autoconf.mk:来自u-boot.cfg,但是移除include/config/auto.conf的内容以免重复
需要进一步处理:
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
$(Q)touch include/config/auto.conf
上述规则的命令里,有2个操作:
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf
第1个操作相当于:make silentoldconfig,生成了文件
make silentoldconfig
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
进入scripts/kconfig是它的Makefile:
silentoldconfig: $(obj)/conf
$(Q)mkdir -p include/config include/generated
$(Q)test -e include/generated/autoksyms.h || \
touch include/generated/autoksyms.h
$< $(silent) --$@ $(Kconfig)
scripts/kconfig/conf分析:
scripts/kconfig/conf --silentoldconfig Kconfig
1. sync_kconfig = 1
2. name = "Kconfig", conf_parse(name);
3. name = conf_get_configname(); = ".config"
4. conf_read(NULL); #
第2个操作相当于make -f scripts/Makefile.autoconf,会生成一系列头文件:
ln -fsn arch-mx6 arch/arm/include/asm/archscripts/Makefile.autoconf分析:
__all: include/autoconf.mk include/autoconf.mk.dep
include include/config/auto.conf
include scripts/Kbuild.include
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
include config.mk
include/autoconf.mk.dep: include/config.h FORCE
$(call cmd,autoconf_dep)
u-boot.cfg: include/config.h FORCE
$(call cmd,u_boot_cfg)
include/autoconf.mk: u-boot.cfg
$(call cmd,autoconf)
include/config.h: scripts/Makefile.autoconf create_symlink FORCE
$(call filechk,config_h)
create_symlink:
ifdef CONFIG_CREATE_ARCH_SYMLINK
ifneq ($(KBUILD_SRC),)
$(Q)mkdir -p include/asm
$(Q)if [ -d $(KBUILD_SRC)/arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \
dest=arch/$(ARCH)/mach-$(SOC)/include/mach; \
else \
dest=arch/$(ARCH)/include/asm/arch-$(if $(SOC),$(SOC),$(CPU)); \
fi; \
ln -fsn $(KBUILD_SRC)/$$dest include/asm/arch
else
$(Q)if [ -d arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \
dest=../../mach-$(SOC)/include/mach; \
else \
dest=arch-$(if $(SOC),$(SOC),$(CPU)); \
fi; \
ln -fsn $$dest arch/$(ARCH)/include/asm/arch
endif
endif

以IMX6ULL为例,先说结论:

细节如下:
执行make命令时,要编译得到哪些文件由ALL-y决定
规则如下
include config.mk # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
_all : all
# Always append ALL so that arch config.mk's can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
all: $(ALL-y)
ALL-y的值为:
checkarmreloc u-boot-dtb.imx u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check u-boot.dtb
u-boot-dtb.imx依赖于u-boot-dtb.bin,u-boot-dtb.bin又依赖于u-boot-nodtb.bin和dts/dt.dtb
各类u-boot文件都依赖于u-boot,先编译得到u-boot,它由u-boot-init、u-boot-main两部分链接而成
u-boot-init为:arch/arm/cpu/armv7/start.o
u-boot-main为:lib/built-in.o fs/built-in.o net/built-in.o 等等
如何编译各个built-in.o,以lib/built-in.o为例
obj-y使用如下的命令链接得到u-boot:
quiet_cmd_u-boot__ ?= LD $@
cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
-T u-boot.lds $(u-boot-init) \
--start-group $(u-boot-main) --end-group \
$(PLATFORM_LIBS) -Map u-boot.map
再编译得到设备树文件: make -f ./scripts/Makefile.build obj=dts dtbs
顶层Makefile如下:
dts/dt.dtb: checkdtc u-boot
(
Q
)
(Q)
(Q)(MAKE) $(build)=dts dtbs
* scripts/Makefile.build中,会包含 dts/Makefile
```shell
DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb
$(DTB): arch-dtbs
$(obj)/dt.dtb: $(DTB) FORCE
$(call if_changed,shipped)
targets += dt.dtb
$(DTB): arch-dtbs
$(Q)test -e $@ || ( \
echo >&2; \
echo >&2 "Device Tree Source is not correctly specified."; \
echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'"; \
echo >&2 "or build with 'DEVICE_TREE=' argument"; \
echo >&2; \
/bin/false)
arch-dtbs:
$(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs
.SECONDARY: $(obj)/dt.dtb.S
obj-$(CONFIG_OF_EMBED) := dt.dtb.o
dtbs: $(obj)/dt.dtb
@:
最后把u-boot、设备树打包: make -f ./scripts/Makefile.build obj=arch/arm/imx-common u-boot-dtb.imx
include config.mk # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
%.imx: %.bin
$(Q)$(MAKE) $(build)=arch/arm/imx-common $@
顶层Makefile里:
从顶层Makefile开始分析:
include config.mk # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
include arch/$(ARCH)/Makefile # arch/arm/Makefile, 里面含有head-y libs-y
libs-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 += drivers/dma/
libs-y += drivers/gpio/
libs-y += drivers/i2c/
libs-y += drivers/mmc/
libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)
libs-y := $(sort $(libs-y))
u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
u-boot-alldirs := $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-))))
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
对于arch/arm/Makefile:
head-y := arch/arm/cpu/$(CPU)/start.o
libs-y += arch/arm/cpu/$(CPU)/
libs-y += arch/arm/cpu/
libs-y += arch/arm/lib/
所以,顶层Makefile包含了arch/arm/Makefile,确定了head-y为arch/arm/cpu/armv7/start.o。
还定义了libs-y变量,它里面含有多个目录。
顶层Makefile里定义了libs-y,会进入里面每一目录,使用它的Makefile进行编译:
u-boot-main := $(libs-y)
$(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) ;
u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples
$(u-boot-dirs): prepare scripts
# make -f $(srctree)/scripts/Makefile.build obj=arch/arm/cpu/arm7
$(Q)$(MAKE) $(build)=$@
比如:u-boot-main里面含有一个目录"arch/arm/cpu/armv7/“,在u-boot-dirs中它末尾的”/"被取消,就变成:arch/arm/cpu/armv7。
然后使用scripts/Makefile.build进行处理: ( Q ) (Q) (Q)(MAKE) ( b u i l d ) = (build)= (build)=@ ,展开就是:
make -f scripts/Makefile.build obj=arch/arm/cpu/armv7/
scripts/Makefile.build是编译u-boot源码的关键:
src := $(patsubst $(prefix)/%,%,$(obj)) # obj=arch/arm/cpu/arm7/, src=arch/arm/cpu/armv7
# 第1个目标
__build:
# The filename Kbuild has precedence over 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) # 包含 arch/arm/cpu/arm7/Makefile
ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a # 如果包含的arch/arm/cpu/armv7/Makefile里定义了lib-y
endif
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o # 如果包含的arch/arm/cpu/armv7/Makefile里定义了obj-y
endif
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE
$(call cmd,force_checksrc)
$(call if_changed_rule,cc_o_c)
ifdef builtin-target
quiet_cmd_link_o_target = LD $@
# If the list of objects to link is empty, just create an empty built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
$(cmd_secanalysis),\
rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
targets += $(builtin-target)
endif # builtin-target
顶层Makefile里:
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
# Rule to link u-boot
# May be overridden by arch/$(ARCH)/config.mk
quiet_cmd_u-boot__ ?= LD $@
cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
-T u-boot.lds $(u-boot-init) \
--start-group $(u-boot-main) --end-group \
$(PLATFORM_LIBS) -Map u-boot.map
quiet_cmd_smap = GEN common/system_map.o
cmd_smap = \
smap=`$(call SYSTEM_MAP,u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(c_flags) -DSYSTEM_MAP="\"$${smap}\"" \
-c $(srctree)/common/system_map.c -o common/system_map.o
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
$(call cmd,smap)
$(call cmd,u-boot__) common/system_map.o
endif
就是把head-y和各个libs-y下的built-in.o链接得到u-boot。
对于IMX6ULL,要使用的是u-boot-dtb.imx,它含有u-boot和设备树。
在顶层Makefile里:
include config.mk # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
%.imx: %.bin
$(Q)$(MAKE) $(build)=arch/arm/imx-common $@
u-boot-dtb.imx依赖于u-boot-dtb.bin,u-boot-dtb.bin又依赖于u-boot-nodtb.bin和dts/dt.dtb。
在顶层Makefile里:
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
$(call if_changed,cat)
u-boot-nodtb.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
$(BOARD_SIZE_CHECK)
所以,编译出u-boot后,会先制作u-boot-nodtb.bin;然后编译dts/dt.dtb;最后生成u-boot-dtb.bin、imx文件:

顶层Makefile里:
dts/dt.dtb: checkdtc u-boot
# make -f $(srctree)/scripts/Makefile.build obj=dts dtbs
$(Q)$(MAKE) $(build)=dts dtbs
需要用到scripts/Makefile.build中,它会包含 dts/Makefile, dts/Makefile中含有dtbs目标:
DEVICE_TREE ?= $(CONFIG_DEFAULT_DEVICE_TREE:"%"=%)
DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb # 就是 arch/arm/dts/imx6ull-14x14-evk.dtb
$(obj)/dt.dtb: $(DTB) FORCE
$(call if_changed,shipped)
targets += dt.dtb
$(DTB): arch-dtbs
$(Q)test -e $@ || ( \
echo >&2; \
echo >&2 "Device Tree Source is not correctly specified."; \
echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'"; \
echo >&2 "or build with 'DEVICE_TREE=' argument" ; \
echo >&2; \
/bin/false)
arch-dtbs:
$(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs
.SECONDARY: $(obj)/dt.dtb.S
obj-$(CONFIG_OF_EMBED) := dt.dtb.o
dtbs: $(obj)/dt.dtb
@:
最后会使用$(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs来处理,进入:使用arch/arm/dts/Makefile,里面定了dtb-y:
dtb-$(CONFIG_MX6) += imx6ul-14x14-ddr3-arm2.dtb \
imx6ul-14x14-ddr3-arm2-emmc.dtb \
imx6ul-14x14-ddr3-arm2-gpmi-weim.dtb \
imx6ul-14x14-lpddr2-arm2.dtb \
imx6ul-14x14-evk.dtb \
imx6ul-14x14-evk-emmc.dtb \
PHONY += dtbs
dtbs: $(addprefix $(obj)/, $(dtb-y))
@:
要编译出各类dtb文件,用到scripts/Makefile.lib,规则如下:
quiet_cmd_dtc = DTC $@
# Modified for U-Boot
# Bring in any U-Boot-specific include after the '/dts-v1/;' header
cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
cat $< $(if $(u_boot_dtsi),\
| sed '/^\/ {$$/{x;s%$$%\#include \"$(u_boot_dtsi)\"%;G;}') | \
$(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) - ; \
$(DTC) -O dtb -o $@ -b 0 \
-i $(dir $<) $(DTC_FLAGS) \
-d $(depfile).dtc.tmp $(dtc-tmp) ; \
cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
$(obj)/%.dtb: $(src)/%.dts FORCE
$(call if_changed_dep,dtc)
当制作出设备树文件后,在顶层Makefile里有如下规则,进而制作出u-boot-dtb.bin:
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
$(call if_changed,cat)
回到顶层Makefile,看看如何制作imx映像文件:
include config.mk # 里面会包含arch/arm/config.mk(含有ALL-y += u-boot-dtb.imx)
%.imx: %.bin
$(Q)$(MAKE) $(build)=arch/arm/imx-common $@
现在u-boot-dtb.imx的依赖文件u-boot-dtb.bin已经生成了,将会使用arch/arm/imx-common/Makefile来生产imx文件:
u-boot.imx: u-boot.bin $(IMX_CONFIG) $(PLUGIN).bin FORCE
$(call if_changed,mkimage)
最后会使用如下命令生产imx映像文件:
./tools/mkimage -n board/freescale/mx6ullevk/imximage.cfg.cfgtmp -T imximage -e 0x87800000 -d u-boot-dtb.bin u-boot-dtb.imx
使用如下命令配置、编译STM32MP157的u-boot:
make stm32mp15_trusted_defconfig
make
所涉及的配置过程、编译过程,跟前面分析的IMX6ULL几乎一样。
顶层Makefile会包含2个配置文件:include/config/auto.conf、include/autoconf.mk。
u-boot中有非常多的配置文件:
.config:来自单板的默认配置、Kconfig
include/config/auto.conf:来自.config,去掉了很多注释
u-boot.cfg:它的内容跟头文件类似,来自
.config
头文件include/common.h,又包含了"#include
/* Automatically generated - do not edit */
#define CONFIG_BOARDDIR board/st/stm32mp1
#include
#include
#include
#include
#include
#include
include/autoconf.mk:来自u-boot.cfg,但是移除include/config/auto.conf的内容以免重复


结论:
执行make命令时,要编译得到哪些文件由ALL-y决定
规则如下
include config.mk # 里面会包含arch/arm/config.mk
# arch/arm/Makefile又包含$(machdirs)/config.mk
# 就是包含 arch/arm/mach-stm32mp/config.mk
# 里面有 ALL-y += u-boot.stm32
_all : all
# Always append ALL so that arch config.mk's can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
all: $(ALL-y)
ALL-y的值为:
checkarmreloc u-boot.stm32 u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check u-boot.dtb
u-boot.stm32依赖于u-boot.bin
各类u-boot文件都依赖于u-boot,先编译得到u-boot,它由u-boot-init、u-boot-main两部分链接而成
u-boot-init为:arch/arm/cpu/armv7/start.o
u-boot-main为:lib/built-in.o fs/built-in.o net/built-in.o 等等
如何编译各个built-in.o,以lib/built-in.o为例
obj-y使用如下的命令链接得到u-boot:
quiet_cmd_u-boot__ ?= LD $@
cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
-T u-boot.lds $(u-boot-init) \
--start-group $(u-boot-main) --end-group \
$(PLATFORM_LIBS) -Map u-boot.map
再编译得到设备树文件: make -f ./scripts/Makefile.build obj=dts dtbs
顶层Makefile如下:
dts/dt.dtb: checkdtc u-boot
$(Q)$(MAKE) $(build)=dts dtbs
scripts/Makefile.build中,会包含 dts/Makefile
DTB := arch/$(ARCH)/dts/$(DEVICE_TREE).dtb
$(DTB): arch-dtbs
$(obj)/dt.dtb: $(DTB) FORCE
$(call if_changed,shipped)
targets += dt.dtb
$(DTB): arch-dtbs
$(Q)test -e $@ || ( \
echo >&2; \
echo >&2 "Device Tree Source is not correctly specified."; \
echo >&2 "Please define 'CONFIG_DEFAULT_DEVICE_TREE'"; \
echo >&2 "or build with 'DEVICE_TREE=' argument" ; \
echo >&2; \
/bin/false)
arch-dtbs:
$(Q)$(MAKE) $(build)=arch/$(ARCH)/dts dtbs
.SECONDARY: $(obj)/dt.dtb.S
obj-$(CONFIG_OF_EMBED) := dt.dtb.o
dtbs: $(obj)/dt.dtb
@:
最后制作u-boot.stm32,在arch/arm/mach-stm32mp/config.mk中有如下规则:
u-boot.stm32: u-boot.bin FORCE
$(call if_changed,mkimage)
if_changed函数在scripts/Kbuild.include中定义:
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
@set -e; \
$(echo-cmd) $(cmd_$(1)); \
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)
展开$(call if_changed,mkimage),就是执行如下命令:
cmd_mkimage
cmd_mkimage命令在顶层Makefile中定义:
cmd_mkimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -d $< $@ \
参考资料:https://blog.csdn.net/zhoutaopower/article/details/123133291
BootROM、SPL、u-boot等概念,重定位等概念。
BootROM:硬件初始化、把程序从非XIP设备复制进RAM,从RAM里执行
如何支持多种启动方式(SD卡、EMMC、USB、UART启动)
BootROM被用来启动用户程序,用户程序可能有几百KB、几MB,但是片内的RAM只有几KB:
相对寻址、绝对寻址
重定位有2种方法:
u-boot的源码大致可以分为2个阶段:

board_init_f函数的本意是在Flash上执行,程序的数据段并未被复制到内存,也没有清除BSS段。它以及它调用的子函数都不能使用全局变量、静态变量。有些芯片以上电,内置的BROM会把U-Boot复制到内存,对于这种情况,我们也把它当作仍然在Flash上运行,仍然不能使用全局变量、静态变量。
board_init_f函数是C函数,调用它之前要设置栈。并且board_init_f函数里可能会调用malloc之类的堆函数:
gd_t结构体,在这个结构体里记录堆地址gd_t结构体?它的地址记录在r9寄存器里在调用board_init_f之前,先调用board_init_f_alloc_reserve等函数,过程如下:

查看board_init_f_alloc_reserve函数,可以知道第1阶段的内存使用情况如下:

设置好global_data的指针,设置好栈后,_main函数调用board_init_f函数。而board_init_f函数的核心是调用init_sequence_f数组里的各个函数:

init_sequence_f数组内容如下:

board_init_f函数最大的作用就是为各个功能预留了内存,比如重定位后的U-boot放在哪里?Framebuffer、设备树放在哪里?
它先确定DDR的内存范围,然后从上往下划分内存。
这些信息记录在global_data里:
// setup_mon_len
gd->mon_len = (ulong)&__bss_end - (ulong)_start; /* u-boot.bin的大小 */
// fdtdec_setup
gd->fdt_blob = (ulong *)&_end;
// initf_malloc
gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
gd->malloc_ptr = 0;
// dram_init
gd->ram_size = imx_ddr_size();
// setup_dest_addr
gd->ram_size = board_reserve_ram_top(gd->ram_size);
#ifdef CONFIG_SYS_SDRAM_BASE
gd->ram_top = CONFIG_SYS_SDRAM_BASE;
#endif
gd->ram_top += get_effective_memsize();
gd->ram_top = board_get_usable_ram_top(gd->mon_len);
gd->relocaddr = gd->ram_top;
// reserve_logbuffer
gd->relocaddr -= LOGBUFF_RESERVE;
// reserve_uboot
gd->relocaddr -= gd->mon_len;
gd->relocaddr &= ~(4096 - 1);
#ifdef CONFIG_E500
/* round down to next 64 kB limit so that IVPR stays aligned */
gd->relocaddr &= ~(65536 - 1);
#endif
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10,
gd->relocaddr);
gd->start_addr_sp = gd->relocaddr;
// reserve_malloc
gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN;
// reserve_board
gd->start_addr_sp -= sizeof(bd_t);
gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t));
memset(gd->bd, '\0', sizeof(bd_t));
// reserve_global_data
gd->start_addr_sp -= sizeof(gd_t);
gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t));
// reserve_fdt
gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
gd->start_addr_sp -= gd->fdt_size;
gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
// reserve_stacks
gd->start_addr_sp -= 16;
gd->start_addr_sp &= ~0xf;