一.配置选择:
----------------------
配置取决于板子和cpu的类型,所有的这些信息都封装在配置文件中
"include/configs/<board_name>.h".
例如:对于TQM823L 模块,所有的设置都在
"include/configs/TQM823L.h".
1.选择一个处理器架构和板子类型:
---------------------------------------------------
所有支持的板子有一个可用的默认配置,仅仅通过 "make <board_name>_defconfig".
例如: 对于TQM823L 模块类型:
cd u-boot
make TQM823L_defconfig
这样就会在当前顶层或者指定的编译目录下生成一个.config 文件。 如果有需要, 可以使用 make
menuconfig 进行修改保持。
注意:如果你正在为一个板子找默认配置文件, 你以前确定用过但是现在找不到了,查看doc/README文档,这里面是不在支持的板子清单。
2.配置编译的两种方式:一般默认在本地进行编译,文件也存在源码目录中。但有两种方式去改变这种方式,可以在源码外目录进行编译
a. 添加 O=make命令行:
make O=/tmp/build distclean
make O=/tmp/build NAME_defconfig
make O=/tmp/build all
b. 设置环境变量 KBUILD_OUTPUT去指向预定位置
export KBUILD_OUTPUT=/tmp/build
make distclean
make NAME_defconfig
make all
注意: 这个命令行输入 "O=" 会覆盖 KBUILD_OUTPUT 环境变量
二.添加不在配置列表的板子配置。如有系统板子不在配置列表内,然后你需要把uboot移植到一个硬件平台。那么你需要有这些步骤:
1) 创建新目录去存放你自己板子的代码,添加一些你需要的文件。在你自己板子目录,至少需要一个Makefile和一个“<board.c>”文件。
2)为你的板子创建一个新的配置文件"include/configs/<board>.h"
3)如果你正在移植 U-Boot 到一个新的 CPU, 然后也需要创建一个新的目录去存放CPU的特定代码,并添加一些特定代码
4)运行"make <board>_defconfig" 用你新的板子名字.
5) 类型"make", 你需要得到一个工作 "u-boot.srec"文件,并安装到你的目标系统中
6)调试和解决可能出现的任何问题。当然最后一步比听起来更困难。
三.u-boot编译
1.指定编译器,我用的是STM32MP157的板子,一般编译之前会配置SDK,会执行环境脚本source <$PATH>/environment-setup-cortexa7t2hf-neon-vfpv4-openstlinux_weston-linux-gnueabi
注意:其他板子不一样,两种方式:在编译时指定make CROSS_COMPILE=xxxxx,或者修改顶层Makefile。
2.uboot编译分为两步:配置 编译
1)第一步配置:执行make xxx_defconfig进行配置,生成.config文件
配置完成后也可以生成索引文件,在配置完毕生成.config 后, 可以生成 tags 和 cscope 索引文件, 方便用 vim 查
看。
- make tags
- make cscope
配置过程分析:
执行 make stm32mp15_trusted_defconfig 后, 会匹配到顶层 Makefile 的:
495 %config: scripts_basic outputmakefile FORCE
496 $(Q)$(MAKE) $(build)=scripts/kconfig $@
对于目标,stm32mp15_trusted_defconfig
,展开则有:
495 stm32mp15_trusted_defconfig: scripts_basic outputmakefile FORCE
496 $(Q)$(MAKE) $(build)=scripts/kconfig stm32mp15_trusted_defconfig
其中$(build)
在kbuild.include
中定义:
build := -f $(srctree)/scripts/Makefile.build obj
所以, 会执行类似如下命令:
make -f ./scripts/Makefile.build obj=scripts/kconfig stm32mp15_trusted_defconfig
在 Makefile.build 会 include 目录 scripts/kconfig/下的 Makefile, 其中含有处理
stm32mp15_trusted_defconfig 的命令:
127 %_defconfig: $(obj)/conf
128 $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
展开后就是:
scripts/kconfig/conf –defconfig=arch/../configs/stm32mp15_trusted_defconfig
执行 make menuconfig 的逻辑类似, 首先会匹配到顶层 Makefile 的:
492 config: scripts_basic outputmakefile FORCE
493 $(Q)$(MAKE) $(build)=scripts/kconfig $@
然后匹配到 scripts/kconfig/Makefile 中的:
34 menuconfig: $(obj)/mconf
35 $< $(silent) $(Kconfig)
展开后就是:
scripts/kconfig/mconf Kconfig
上面的 Kconfig 表示顶层目录下的 Kconfig 文件, 这个文件会 source 其他的
Kconfig, 结构大致如下:
顶层 Kconfig
---- > arch/Kconfig
---- > arch/arm/Kconfig
---- > arch/arm/mach-exynos/Kconfig
---- > arch/arm/mach-stm32mp/Kconfig
---- > board/st/stm32mp1/Kconfig
----> arch/x86/Kconfig
---- > ...
---- > api/Kconfig
---- > common/Kconfig
---- > cmd/Kconfig
---- > disk/Kconfig
---- > dts/Kconfig
---- > env/Kconfig
---- > net/Kconfig
---- > drivers/Kconfig
---- > fs/Kconfig
---- > lib/Kconfig
---- > test/Kconfig
make menuconfig可以从之前的.config文件中继续增加配置并保存。
2)第二步编译:执行make all,生成uboot*.
编译 u-boot.bin
在执行 make 时, 会从顶层 Makefile 开始解析, 默认的目标是 all。
913 all: $(ALL-y) cfg
921 @# Check that this build does not use CONFIG options that we do not
922 @# know about unless they are in Kconfig. All the existing CONFIG
923 @# options are whitelisted, so new ones should not be added.
924 $(call cmd,cfgcheck,u-boot.cfg)
- $@:表示所有脚本参数的内容
- $#:表示返回所有脚本参数的个数
其中 ALL-y 会根据配置扩展成需要编译的目标, 根据stm32mp157 的配置, ALL-y 的值如
下:
804 # Always append ALL so that arch config.mk's can add custom ones
805 ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
828 ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
u-boot.dtb
顶层 Makefile 会包含:
551 ifeq ($(autoconf_is_old),)
552 include config.mk
553 include arch/$(ARCH)/Makefile
554 endif
顶层文件 config.mk 比较重要, 其中含有以下比较重要的变量。 这些变量会依赖一些在
配置时设置的宏, 比如: (这个几个宏的作用可以参考 arch/Kconfig)
CONFIG_SYS_ARCH、 CONFIG_SYS_BOARD、 CONFIG_SYS_VENDOR、 CONFIG_SYS_SOC
从.config里面可以获得这几个宏的值
19 CONFIG_SYS_ARCH="arm"
20 CONFIG_SYS_CPU="armv7"
21 CONFIG_SYS_SOC="stm32mp"
22 CONFIG_SYS_VENDOR="st"
23 CONFIG_SYS_BOARD="stm32mp1"
24 CONFIG_SYS_CONFIG_NAME="stm32mp1"
在 stm32mp15_trusted_defconfig 中定义了宏: CONFIG_ARM=y, 那么在 arch/arm/Kconfig
中会根据下面的配置生成 CONFIG_SYS_ARCH="arm“
4 config SYS_ARCH
5 default "arm"
在 stm32mp15_trusted_defconfig 中定义了宏:CONFIG_ARCH_STM32MP=y, 那么
arch/arm/mach-stm32mp/Kconfig 会 select CPU_V7A, 然后 arch/arm/Kconfig 中会将CONFIG_SYS_CPU 设置为”armv7"
252 config SYS_CPU
253 default "arm720t" if CPU_ARM720T
254 default "arm920t" if CPU_ARM920T
255 default "arm926ejs" if CPU_ARM926EJS
256 default "arm946es" if CPU_ARM946ES
257 default "arm1136" if CPU_ARM1136
258 default "arm1176" if CPU_ARM1176
259 default "armv7" if CPU_V7A
260 default "armv7" if CPU_V7R
261 default "armv7m" if CPU_V7M
262 default "pxa" if CPU_PXA
263 default "sa1100" if CPU_SA1100
264 default "armv8" if ARM64
在arch/arm/mach-stm32mp/Kconfig中会设置 CONFIG_SYS_SOC
为”stm32mp”:
27 config SYS_SOC
28 default "stm32mp"
board/st/stm32mp1/Kconfig 中会设置, CONFIG_SYS_BOARD 为” default "stm32mp1"”,
CONFIG_SYS_VENDOR 为” default "st"”, CONFIG_SYS_CONFIG_NAME 为”default "stm32mp1"”:
1 if TARGET_STM32MP1
2
3 config SYS_BOARD
4 default "stm32mp1"
5
6 config SYS_VENDOR
7 default "st"
8
9 config SYS_CONFIG_NAME
10 default "stm32mp1"
11
12 config CMD_STBOARD
13 bool "stboard - command for OTP board information"
14 default y
15 help
16 This compile the stboard command to
17 read and write the board in the OTP.
18
19 endif
在顶层config.mk中会根据这几个宏执行如下操作:
ARCH=arm
CPU=armv7
BOARD=stm32mp1
VENDOR=st
SOC=stm32mp
CPUDIR=arch/arm/cpu/armv7
BOARDDIR=st/stm32mp1
sinclude ./arch/arm/config.mk
sinclude ./arch/arm/cpu/armv7/config.mk
sinclude ./arch/arm/cpu/armv7/st/stm32mp1/config.mk
sinclude ./board/st/stm32mp1/config.mk
继续分析顶层 Makefile
在 include 完 config.mk 后, 第 553 行,就会 include arch/$(ARCH)/Makefile
, 这个文件干如下几件事:
arch-y =-march=armv7
48 tune-y := $(tune-y)
PLATFORM_CPPFLAGS += $(arch-y) $(tune-y)
machine-y += stm32mp
86 machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y))
machdirs := arch/arm/mach-stm32mp/
88 PLATFORM_CPPFLAGS += $(patsubst %,-I$(srctree)/%include,$(machdirs))
PLATFORM_CPPFLAGS += -Iarch/arm/mach-stm32mp//include
libs-y += $(machdirs)
92 head-y := arch/arm/cpu/$(CPU)/start.o
head-y := arch/arm/cpu/armv7/start.o
libs-y += arch/arm/cpu/armv7/
libs-y += arch/arm/cpu/
libs-y += arch/arm/lib/
回到顶层 Makefile, 设置 LDSCRIPTS 为
arch/arm/cpu/armv7/u-boot.lds
接下来是给 libs-y 赋值, 这个变量包含了 fs、 net、 disk、 driver、 cmd、 env、 common
等目录下的 build-in.o
681 libs-y += lib/
682 libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
683 libs-$(CONFIG_OF_EMBED) += dts/
684 libs-y += fs/
685 libs-y += net/
686 libs-y += disk/
687 libs-y += drivers/
.........................
.........................
722 libs-y += drivers/usb/ulpi/
723 libs-y += cmd/
724 libs-y += common/
725 libs-y += env/
然后将 libs-y 赋值给 u-boot-main, 将 head-y 赋值给
u-boot-init
743 u-boot-init := $(head-y)
744 u-boot-main := $(libs-y)
前面说到 ALL-y 包含了目标文件, 其中会依赖 u-boot 目标, u-boot 目标的对应的
命令如下:
1380 u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
1381 +$(call if_changed,u-boot__)
1382 ifeq ($(CONFIG_KALLSYMS),y)
1383 $(call cmd,smap)
1384 $(call cmd,u-boot__) common/system_map.o
1385 endif
1386
1387 ifeq ($(CONFIG_RISCV),y)
1388 @tools/prelink-riscv $@ 0
1389 endif
u-boot__对应的命令是:
1364 # Rule to link u-boot
1365 # May be overridden by arch/$(ARCH)/config.mk
1366 quiet_cmd_u-boot__ ?= LD $@
1367 cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
1368 -T u-boot.lds $(u-boot-init) \
1369 --start-group $(u-boot-main) --end-group \
1370 $(PLATFORM_LIBS) -Map u-boot.map; \
1371 $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
展开之后就会生成一系列的built.o文件
在生成 u-boot 目标后, u-boot-nodtb.bin 就会生成
1039 u-boot-nodtb.bin: u-boot FORCE
1040 $(call if_changed,objcopy)
1041 $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
1042 $(BOARD_SIZE_CHECK)
在 ALL-y 中包含 u-boot.bin 目标, 根据依赖关系, 需要生成 dts/dt.dtb:
950 u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
951 $(call if_changed,cat)
952
953 u-boot.bin: u-boot-dtb.bin FORCE
954 $(call if_changed,copy)
此外, ALL-y 中也包含设备树镜像 u-boot.dtb:
969 u-boot.dtb: dts/dt.dtb
970 $(call cmd,copy)
可以看到 u-boot.dtb 就是 dts/dt.dtb 重命名了一份。
dt.dtb 对应的命令如下:
927 dtbs: dts/dt.dtb
928 @:
929 dts/dt.dtb: u-boot
930 $(Q)$(MAKE) $(build)=dts dtbs
此时会用到 dts/Makefile,在配置文件 stm32mp15_trusted_defconfig 中指定了设备树镜像的名字:
36 CONFIG_DEFAULT_DEVICE_TREE="stm32mp157c-ev1"
主要:因为这里是“ev1"的板子,而我需要的是“dk2”的板子,所以在编译时应该指定设备树,如
make DEVICE_TREE=stm32mp157c-dk2 all
在 dts/Makefile 中, DTB 为 arch/arm/dts/stm32mp157c-dk2.dtb, 目标 dtbs 的命令如下:
58 dtbs: $(obj)/dt.dtb $(obj)/dt-spl.dtb
59 @:
这里的 obj 所指的就是 dts 目录, 下面是 dts/Makefile 中生成这两个 dtb 的命令
23 $(obj)/dt-spl.dtb: $(DTB) $(objtree)/tools/fdtgrep FORCE
24 $(call if_changed,fdtgrep)
25
26 $(obj)/dt.dtb: $(DTB) FORCE
27 $(call if_changed,shipped)
上面 shipped 所作的仅仅是 cat arch/arm/dts/stm32mp157c-dk2.dtb > dts/dt.dtb,而
fdtgrep 是对 arch/arm/dts/stm32mp157c-dk2.dtb 做了一些裁剪, 然后输出到
dts/dt-spl.dtb 中, 具体参考 scripts/Makefile.lib
./tools/fdtgrep -b u-boot,dm-pre-reloc -b u-boot,dm-spl -RT arch/arm/dts/stm32mp157c-dk2.dtb -n /chosen -n /config -O dtb
| ./tools/fdtgrep -r -O dtb - -o dts/dt-spl.dtb -P pinctrl-0 -P pinctrl-names -P clock-names -P interrupt-parent -P assigned-clocks -P
assigned-clock-rates -P assigned-clock-parents
上面两个 dtb 文件都会依赖 arch/arm/dts/stm32mp157c-dk2.dtb, 在 dts/Makefile 中生
成这个 dtb 文件的命令如下
31 $(DTB): $(dtb_depends)
32 ifeq ($(EXT_DTB),)
33 $(Q)$(MAKE) $(build)=$(ARCH_PATH) $@
dtb_depends 是 arch/arm/dts/stm32mp157c-dk2.dts, ARCH_PATH 是 arch/arm/dts
所以 arch/arm/dts/Makefile 会被 include 进来, 在这个 Makefile 中并没有看到生成
dtb 的命令, 这个命令其实是在 scripts/Makefile.lib 中实现的:
$(obj)/%.dtb: $(src)/%.dts FORCE
$(call if_changed_dep,dtc)
在顶层 Makefile 中 ALL-y 中还有一个 u-boot.bin 会依赖 u-boot-dtb.bin 目标:
950 u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
951 $(call if_changed,cat)
952
953 u-boot.bin: u-boot-dtb.bin FORCE
954 $(call if_changed,copy)
根据命令, 会将 u-boot-nodtb.bin 和 dts/dt.dtb 拼成 u-boot-dtb.bin:
cat u-boot-nodtb.bin dts/dt.dtb > u-boot-dtb.bin
然后将 u-boot-dtb.bin 拷贝一份, 并命名为 u-boot.bin
接着再分析一下:arch/arm/mach-stm32mp/config.mk
6 ifndef CONFIG_SPL
7 ALL-y += u-boot.stm32
8 else
9 ifdef CONFIG_SPL_BUILD
10 ALL-y += spl/u-boot-spl.stm32
11 endif
12 endif
14 MKIMAGEFLAGS_u-boot.stm32 = -T stm32image -a $(CONFIG_SYS_TEXT_BASE) -e $(CO
15
16 u-boot.stm32: MKIMAGEOUTPUT = u-boot.stm32.log
17
18 u-boot.stm32: u-boot.bin FORCE
19 $(call if_changed,mkimage)
22 MKIMAGEFLAGS_u-boot-spl.stm32 = -T stm32image -a $(CONFIG_SPL_TEXT_BASE) -e
23
24 spl/u-boot-spl.stm32: MKIMAGEOUTPUT = spl/u-boot-spl.stm32.log
25
26 spl/u-boot-spl.stm32: spl/u-boot-spl.bin FORCE
27 $(call if_changed,mkimage)
all定制u-boot.stm32或者spl/u-boot-spl.stm32文件。
u-boot.stm32依赖于u-boot.bin;如果定义了SPL_BUILD,spl/u-boot-spl.stm32,文件又依赖与spl/u-boot-spl.bin。所以最终最终需要生成u-boot.bin和spl/u-boot-spl.bin两个文件,u-boot.bin文件上面已经详细说明,接着讲怎么编译u-boot-spl.bin然后生成spl/u-boot-spl.bin
编译 u-boot-spl.bin
除了 u-boot.bin, 我们还需要关注 u-boot-spl.bin, 在 ALL-y 中含有 spl/u-boot-spl.bin(顶层Makefile)。
1534 spl/u-boot-spl.bin: spl/u-boot-spl
1535 @:
1536 spl/u-boot-spl: tools prepare \
1537 $(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_SPL_OF_PLATDAT
1538 $(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_TPL_OF_PLATDAT
1539 $(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all
也就是编译 spl 会用到 scripts/Makefile.spl:
222 all: $(ALL-y)
这里 ALL-y 包含需要生成的目标: spl/u-boot-spl.bin:
242 $(obj)/$(SPL_BIN)-dtb.bin: $(obj)/$(SPL_BIN)-nodtb.bin \
243 $(if $(CONFIG_SPL_SEPARATE_BSS),,$(obj)/$(SPL_BIN)-pad.bin) \
244 $(FINAL_DTB_CONTAINER) FORCE
245 $(call if_changed,cat)
246
247 $(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN)-dtb.bin FORCE
248 $(call if_changed,copy)
上面的 SPL_BIN 就是 u-boot-spl, FINAL_DTB_CONTAINER 是 spl/u-boot-spl.dtb.
最终的 spl/u-boot-spl-dtb.bin 就是将 u-boot-spl-nodtb.bin 跟 spl/u-boot-spl.dtb 拼接
成的。
下面分析 u-boot-spl-nodtb.bin 和 spl/u-boot-spl.dtb 是如何生成的。
305 $(obj)/$(SPL_BIN)-nodtb.bin: $(obj)/$(SPL_BIN) FORCE
306 $(call if_changed,objcopy)
spl/u-boot-spl-nodtb.bin 会依赖 spl/u-boot-spl。
352 # Rule to link u-boot-spl
353 # May be overridden by arch/$(ARCH)/config.mk
354 quiet_cmd_u-boot-spl ?= LD $@
355 cmd_u-boot-spl ?= (cd $(obj) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \
356 $(patsubst $(obj)/%,%,$(u-boot-spl-init)) --start-group \
357 $(patsubst $(obj)/%,%,$(u-boot-spl-main)) \
358 $(patsubst $(obj)/%,%,$(u-boot-spl-platdata)) \
359 --end-group \
360 $(PLATFORM_LIBS) -Map $(SPL_BIN).map -o $(SPL_BIN))
361
362 $(obj)/$(SPL_BIN): $(u-boot-spl-platdata) $(u-boot-spl-init) \
363 $(u-boot-spl-main) $(obj)/u-boot-spl.lds FORCE
364 $(call if_changed,u-boot-spl)
上面用的到 u-boot-spl.lds 依赖关系如下:
372 quiet_cmd_cpp_lds = LDS $@
373 cmd_cpp_lds = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \
374 -D__ASSEMBLY__ -x assembler-with-cpp -std=c99 -P -o $@ $<
375
376 $(obj)/u-boot-spl.lds: $(LDSCRIPT) FORCE
377 $(call if_changed_dep,cpp_lds)
上面的 LDSCRIPTS 表示的是arch/arm/mach-omap2/u-boot-spl.lds,参考
arch/arm/Kconfig, 如果时armv7A默认 CONFIG_SPL_LDSCRIPT 就是
arch/arm/mach-omap2/u-boot-spl.lds, 生成 spl/u-boot-spl.lds 的 log 如下:
log1
生成 u-boot-spl 的 log 如下:
log2
spl 的链接地址是由宏 CONFIG_SPL_TEXT_BASE 指定的
u-boot-spl 一旦生成, 那么 u-boot-spl-nodtb.bin 就会通过 objcopy 的方式生成:
log3
下面是 spl/u-boot-spl.dtb 生成的命令, 在 scripts/Makefile.spl 中:
259 $(obj)/$(SPL_BIN).dtb: dts/dt-spl.dtb FORCE
260 $(call if_changed,copy)
u-boot-spl.dtb 是由 cp dts/dt-spl.dtb spl/u-boot-spl.dtb 而来。
根据前面的分析 dts/dt-spl.dtb 是对 arch/arm/dts/stm32mp157c-dk2.dtb 做了一些裁剪
得到的。
最后就是拼接生成 u-boot-spl-dtb.bin:
cat spl/u-boot-spl-nodtb.bin spl/u-boot-spl.dtb > spl/u-boot-spl-dtb.bin
cp spl/u-boot-spl-dtb.bin spl/u-boot-spl.bin
3) SPL( Secondary Program Loader)
在编译 SPL 时, 执行的是 scripts/Makefile.spl, 这个文件会添加宏:
KBUILD_CPPFLAGS += -DCONFIG_SPL_BUILD
这样同一份代码, 根据宏的不同, 就可以做到 uboot 和 spl 共用。