uboot配置,编译,移植


前言

1.此文章不仅对uboot进行配置,编译,移植,而且对其配置,编译,移植过程进行分析,理解其原理。
2.uboot是通用的启动代码,我们编译移植最好找到和自己的开发板最相近的版本,比如厂商已经匹配的uboot,uboot分类:官方版,SoC厂商,开发板厂商。
3.我们可以根据自己入手的开发板做一次移植,再根据官方给出的uboot做一次移植,从而很好的学习此方面的知识。

uboot文章连载:
1.uboot命令集&环境变量
2.uboot配置,编译,移植
3.uboot启动过程
4.uboot命令体系
5.uboot的环境变量
6.uboot的驱动
7.uboot启动Linux内核

Linux文章连载


1.文件及文件夹

uboot下主要的文件夹和文件如下,接下来会介绍主要的配置编译文件,可以对其配置过程有完整的认识:

├── board
│   └── samsung
│       ├── common
│       └── x210
├── common				//env、command、控制台、crc校验等和硬件无关的代码。
├── config.mk
├── cpu					//SoC相关初始化和控制代码(CPU、中断、串口等SoC内部外设,start.S)。
│   └── s5pc11x
│       └── s6pc110
├── disk
├── doc
│   └── uImage.FIT		//fit镜像树
├── drivers				//从Linux来的一部分驱动,是uboot阶段需要使用的硬件,比如网卡驱动、Inand/SD卡、NandFlash等的驱动。
│   ├── bios_emulator
│   │   ├── include
│   │   │   └── x86emu
│   │   └── x86emu
│   ├── block
│   ├── dma
│	......
│
├── fs						//文件系统,从linux移植
│   ├── cramfs
│   ├── ext2
│   └── fat
├── include					//头文件
│   ├── asm-arm
│   │   ├── arch-s5pc11x
│   │   └── proc-armv
│   └── linux
│       ├── byteorder
│       ├── mmc
│       └── mtd
├── lib_arm
├── libfdt					//设备树相关(启动传参,(3.4后的版本Linux改用设备树启动传参))
├── lib_generic
├── Makefile
├── mkconfig
├── net
├── post					//power on self test
├── rules.mk
├── sd_fusing				//烧录uboot镜像到SD卡的代码(此代码在Linux中运行,其编译采用gcc,而不是arm-linux-gcc)
└── tools
    ├── bddb
    ├── easylogo
    ├── env
    ├── gdb
    ├── logos
    ├── scripts
    └── updater

2.README文件

uboot的源代码文件中的README文件主要讲述了:

(1).配置及编译

配置取决于主板和CPU类型的组合,include/configs/xxx.h是所有有关硬件和CPU的配置项。

其中编译过程需要指定交叉编译工具链,uboot建议不要修改Makefile,而是通过添加环境变量或者写一个脚本来覆盖CROSS_COMPILE环境变量:

CROSS_COMPILE=ppc_4xx-
export CROSS_COMPILE

uboot编译后生成的文件默认放在源文件目录下,指定目录可以使用:

make O=/tmp/build distclean
make O=/tmp/build NAME_config
make O=/tmp/build all

或者将BUILD_DIR导出到环境变量:

export BUILD_DIR=/tmp/build
make distclean
make NAME_config
make all

注意:命令行“O=”设置会覆盖BUILD_DIR环境变量。

如果我们的开发板在uboot中没有,可以自己配置,关于修改建议README也给出了:
1.使用现有条目作为示例,为顶层“Makefile”和“MAKEALL”脚本添加一个新的板配置选项。
2.创建一个新的目录来保存特定于电路板的代码。添加任何需要的文件。在电路板目录中,至少需要“Makefile”、一个“<board>.c”、“flash.c”和“u-boot.lds”。
3.为您的开发板创建一个新的配置文件“include/configs/<\board>.h”
3.如果你要将U-Boot移植到一个新的CPU上,那么也要创建一个新的目录来保存你的CPU特定代码。添加任何需要的文件。
4.用你的新名字运行“make<board>\u config”。
5.输入“make”,你应该会在目标系统上安装一个工作的“u-boot.srec”文件。
6.调试并解决可能出现的任何问题。

(2).镜像格式

uboot支持两种镜像格式:

FIT:
基于扁平化镜像树格式–FIT(类似于扁平化设备树FDT)。它允许使用包含多个组件(多个内核、ramdisk等)的镜像。更多详细信息请参见doc/uImage.FIT目录

旧的uImage格式:
旧的镜像格式基于二进制文件,基本上可以是任何文件,前面有一个特殊的头;请参见include/image.h,uImage的头部定义了以下镜像属性:

  • 目标系统(我们是Linux)
  • 目标CPU架构(我们是ARM)
  • 加载地址
  • 入口地址
  • 镜像名
  • 镜像时间戳

头部由一个特殊的魔数标记,并且头部和镜像的数据部分都通过CRC32校验和来防止损坏。

3.sd_fusing文件夹

此文件夹功能:将uboot.bin文件分成BL1,BL2。使用脚本将BL1和BL2烧录到SD卡中

步骤:
1:进入sd_fusing目录下
sd_fusing.sh需要改一下,其中的uboot_inand.bin改成u-boot.bin
2:make new
3:插入sd卡,进入sd_fusing目录下 执行 ./sd_fusing.sh /dev/sdb(sdb是sd卡的名字,ls /dev/sd*查看),烧录成功。

4.主Makefile

把他简化一下,将其做的主要工作整理出来,注释详细解释了其用途:

#1.
VERSION = 1
PATCHLEVEL = 3
SUBLEVEL = 4
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h						#此文件是自动生成的,内容为#define U_BOOT_VERSION "U-Boot 1.3.4",其内容来自以上变量,其在主makefile的all中生成

#2.
MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

#3.
include $(obj)include/config.mk												#不是源码自带的,要在配置过程(make x210_sd_config)中才会生成这个文件
export	ARCH CPU BOARD VENDOR SOC											#变量在以上这个文件中定义,从上面这个文件中取出,导出这五个变量作为哦环境变量

#4.
ifeq ($(ARCH),arm)
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-		#编译工具链名字前缀,包含路径(这个设置值只要能保证找到那个交叉编译工具链即可,不一定非得是全路径的,相对路径也可以。(如果已经将工具链导出到环境变量,并且设置了符号链接,这样CROSS_COMPILE = arm-linux-就可以))
export	CROSS_COMPILE
endif

#5.
include $(TOPDIR)/config.mk													#包含根目录下的config.mk。(TOPDIR在主Makefile中定义,若在根目录下编译,那么TOPDIR为根目录)

#6.
OBJS  = cpu/$(CPU)/start.o													#添加待编译文件
OBJS := $(addprefix $(obj),$(OBJS))											#addprefix是makefile自带函数,为变量添加前缀,我们的obj在自己的总目录下编译时就为./,所以在这里OBJS还是=它本身

#7.
LIBS  = lib_generic/libgeneric.a											#添加库,将所有需要的库都包含进来,我只将ARM架构需要的库添加进来了,若是其他架构则不一样
#LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo "board/$(VENDOR)/common/lib$(VENDOR).a"; fi)								#我们目录下没有此文件,所以我将其注释掉了
LIBS += cpu/$(CPU)/lib$(CPU).a												#我是cpu/s5pc11x/libs5pc11x.a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a										#cpu/s5pc11x/s5pc110/libs5pc110.a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += drivers/bios_emulator/libatibiosemu.a
LIBS += drivers/block/libblock.a
LIBS += drivers/dma/libdma.a
LIBS += drivers/hwmon/libhwmon.a
LIBS += drivers/i2c/libi2c.a
LIBS += drivers/input/libinput.a
LIBS += drivers/misc/libmisc.a
LIBS += drivers/mmc/libmmc.a
LIBS += drivers/mtd/libmtd.a
LIBS += drivers/mtd/nand/libnand.a
LIBS += drivers/mtd/nand_legacy/libnand_legacy.a
LIBS += drivers/mtd/onenand/libonenand.a
LIBS += drivers/mtd/ubi/libubi.a
LIBS += drivers/mtd/spi/libspi_flash.a
LIBS += drivers/net/libnet.a
LIBS += drivers/net/sk98lin/libsk98lin.a
LIBS += drivers/pci/libpci.a
LIBS += drivers/pcmcia/libpcmcia.a
LIBS += drivers/spi/libspi.a
LIBS += drivers/rtc/librtc.a
LIBS += drivers/serial/libserial.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/video/libvideo.a
LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a
LIBS += api/libapi.a
LIBS += post/libpost.a
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS) $(VERSION_FILE)

LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a
LIBBOARD := $(addprefix $(obj),$(LIBBOARD))

#8.
#add gcc lib
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc	#CC是gcc命令,CFLAGS是编译选项,不在此主makefile中,是和硬件平台相关的,猜测也应该在config.mk这个编译选项文件中,grep查找一下,果然在。
																						#关于参数-print-libgcc-file-name,可以参考文章:https://www.codes91.com/article/detail_4019385_2.html

#9.
# The "tools" are needed early, so put this first
SUBDIRS	= tools \																		#添加三个文件夹给变量SUBDIRS,后面要用,
	  examples \
	  api_examples
.PHONY : $(SUBDIRS)

#10.
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(obj)u-boot.dis				#添加all的依赖

#11.
all:		$(ALL)																		#编译生成各种文件									
$(obj)u-boot.hex:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

$(obj)u-boot.img:	$(obj)u-boot.bin													#生成.img文件
		./tools/mkimage -A $(ARCH) -T firmware -C none \
		-a $(TEXT_BASE) -e 0 \
		-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
			sed -e 's/"[	 ]*$$/ for $(BOARD) board"/') \
		-d $< $@

$(obj)u-boot.sha1:	$(obj)u-boot.bin
		$(obj)tools/ubsha1 $(obj)u-boot.bin

$(obj)u-boot.dis:	$(obj)u-boot														#生成反汇编文件
		$(OBJDUMP) -d $< > $@

$(obj)u-boot:		depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)			#生成u-boot可执行文件
		UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
		sed  -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
		cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
			--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
			-Map u-boot.map -o u-boot

$(OBJS):	depend $(obj)include/autoconf.mk											#生成start.o
		$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

$(LIBS):	depend $(obj)include/autoconf.mk											#生成库文件
		$(MAKE) -C $(dir $(subst $(obj),,$@))
$(LIBBOARD):	depend $(LIBS) $(obj)include/autoconf.mk
		$(MAKE) -C $(dir $(subst $(obj),,$@))

$(SUBDIRS):	depend $(obj)include/autoconf.mk											#编译SUBDIRS变量的三个文件夹中的内容
		$(MAKE) -C $@ all

$(LDSCRIPT):	depend $(obj)include/autoconf.mk
		$(MAKE) -C $(dir $@) $(notdir $@)

$(VERSION_FILE):																		#生成version_autogenerated.h
		@( printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' "$(U_BOOT_VERSION)" \
		 '$(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion $(TOPDIR))' \
		 ) > $@.tmp
		@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@
		
#12.																				#生成tools下包含的工具:gdb、updater、env
gdbtools:
		$(MAKE) -C tools/gdb all || exit 1
updater:
		$(MAKE) -C tools/updater all || exit 1
env:
		$(MAKE) -C tools/env all MTD_VERSION=${MTD_VERSION} || exit 1

#13.																				#遍历SUBDIRS,编译/tools /examples /api_examples三个目录
depend dep:	$(VERSION_FILE)
		for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done

###############################################################################################
#14.																				#tag相关的内容
TAG_SUBDIRS += include
....
#一大部分代码和ctags,etags,cscope相关,这些是用来浏览c代码的,不学习,这个时间还不如用来看uboot源码
###############################################################################################
#15.																			#生成System.map文件
$(obj)System.map:	$(obj)u-boot
		@$(NM) $< | \
		grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
		sort > $(obj)System.map

#16.												
$(obj)include/autoconf.mk.dep: $(obj)include/config.h include/common.h
	@$(XECHO) Generating $@ ; \
	set -e ; \
	: Generate the dependancies ; \
	$(CC) -x c -DDO_DEPS_ONLY -M $(HOST_CFLAGS) $(CPPFLAGS) \
		-MQ $(obj)include/autoconf.mk include/common.h > $@

$(obj)include/autoconf.mk: $(obj)include/config.h
	@$(XECHO) Generating $@ ; \
	set -e ; \
	: Extract the config macros ; \
	$(CPP) $(CFLAGS) -DDO_DEPS_ONLY -dM include/common.h | \
		sed -n -f tools/scripts/define2mk.sed > $@

sinclude $(obj)include/autoconf.mk.dep

#17.
#config的反向工程:
unconfig:
	@rm -f $(obj)include/config.h $(obj)include/config.mk \
		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
		$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
		$(obj)board/$(VENDOR)/$(BOARD)/config.mk
#######################以上makefile基本完成工作,以下只是某些配置项,uboot有成千上百个配置,适配不同的硬件,其他公司的配置略,我的配置项如下:###########################
#18.
#========================================================================
x210_nand_config :	unconfig
	@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
	@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

x210_sd_config :	unconfig
	@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
	@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
#========================================================================

#19.
clean:																	#清理文件,不细说
...
clobber:clean
...
distclean:clobber unconfig
...

#20.
backup:																	#生成tar包,注意自己有没有装tar工具,我这里没有装gtar,我用tar打包了		
	F=`basename $(TOPDIR)` ; cd .. ; \
	#gtar --force-local -zcvf `date "+$$F-%Y-%m-%d-%T.tar.gz"` $$F
	tar --force-local -zcvf `date "+$$F-%Y-%m-%d-%T.tar.gz"` $$F

上面的Makefile的细节:
1.version_autogenerated.h
是在make时生成的,在主Makefile中的all目标下,有生成此文件的语句,下文会分析

$(VERSION_FILE):
		@( printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' "$(U_BOOT_VERSION)" \
		 '$(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion $(TOPDIR))' \
		 ) > $@.tmp
		@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@

2.总的配置文件:

MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

3.include/config.mk
在配置过程中生成include/config.mk文件(注意不是根目录下的config.mk),其中包含着我们的板子的信息,此文件是在配置过程中自动生成的

ARCH   = arm
CPU    = s5pc11x
BOARD  = x210
VENDOR = samsung
SOC    = s5pc110

我们配置这五个变量供主Makefile使用,主Makefile会将此变量导出为环境变量(上面讲过),通过不同的板子配置生成不同内容的此文件,让主Makefile加载,那么就可以很方便的适配不同的板子。(我们是怎么自动生成这个文件的呢:mkconfig中生成,下文会讲)。

4.交叉编译工具链前缀环境变量设置
工程中我们还可以通过make CROSS_COMPILE=xxxx指定交叉编译工具链

5.总的编译文件
指导整个编译过程,include会在这里原地展开,config.mk相当于主Makefile的一个子文件,下面一节会详细分析

include $(TOPDIR)/config.mk	

8.添加gcclib

PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc

首先看一下-print-libgcc-file-name选项,其作用是显示编译器配套库的名称(gcc help中有解释),我如下运行此选项,发现得到gcc库的路径

root@ubuntu:/mnt/linuxshare/arm/uboot/out_dir_uboot# gcc -print-libgcc-file-name
/usr/lib/gcc/x86_64-linux-gnu/5/libgcc.a
root@ubuntu:/mnt/linuxshare/arm/uboot/out_dir_uboot# arm-linux-gcc -print-libgcc-file-name
/usr/local/arm/arm-2009q3/bin/../lib/gcc/arm-none-linux-gnueabi/4.4.1/libgcc.a

dirname 去除文件名,剩下纯路径,那么此变量就是:
-Lgcclib的路径 -lgcc

11.编译
(1)由可执行文件u-boot生成各种格式的文件:

$(obj)u-boot.hex:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

OBJCFLAGS += --gap-fill=0xff,由config.mk定义

(2)添加文件头
为了将bin下载到SD等存储器中,我们需要添加文件头,用来校验内容,和裸机我们碰到的mkv210_image.c的作用相似,其在tools文件夹中

$(obj)u-boot.img:	$(obj)u-boot.bin
		./tools/mkimage -A $(ARCH) -T firmware -C none \
		-a $(TEXT_BASE) -e 0 \
		-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
			sed -e 's/"[	 ]*$$/ for $(BOARD) board"/') \
		-d $< $@

(3)生成反汇编文件

$(obj)u-boot.dis:	$(obj)u-boot
		$(OBJDUMP) -d $< > $@

(4)u-boot可执行文件和map文件

$(obj)u-boot:		depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT)
		UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
		sed  -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
		cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
			--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
			-Map u-boot.map -o u-boot

主要是执行ld将.o文件和.a库文件链接成可执行文件u-boot,并且生成map文件u-boot.map,细节:
UNDEF_SYM变量部分代码简化成:

objdump -x libtest.a | sed -n -e  's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq 

objudmp -x libtest.a 可以得到 libtest.a 库中所有的 symbol, 包括函数,数组,文件等,这里我们简称为符号 .
然后将得到的符号通过"管道" 传递给sed 进行编辑。接下来对sed 进行分析:

sed -n -e  's/.*\(__u_boot_cmd_.*\)/-u\1/p' 

sed -n -e 代表使用 silent 模式,以及直接在命令行进行编辑动作。
’ ’ 两个单引号之间表示将要进行的动作 。
标记成黑色的 s 代表会进行替换操作(‘s/要被取代的字串/新的字串/’).
p 代表打印出来。
因此上面的表达式的最浅显的意思是:
.*\(__u_boot_cmd_.*\) 替换为 -u\1
并且由于sed 支持正则表达式, 在正则表达式中括号()代表分组, 使用 \1 \2 \3 获取第一个分组,第二个分组, 第三个分组。因此上述表达式进一步被理解为:
.*__u_boot_cmd_.* 替换为 -u.*__u_boot_cmd_.*
在正则表达式中 . 代表任意字符(不包括空格),* 代表任意个, 因此我们将上述语句按照汉语翻译一遍:
使用 objdump -x 解析 libtest.a 得到一些符号,将这些符号使用sed 进行处理, 处理方式是: 找到符号中有__u_boot_cmd_字段的那一部分, 然后在这些字段前面加上-u。
接下来 |sort|uniq 就比较好理解了,就是进行排序和消除重复。
总结来说就是找到一些字段,在字段前面加上-u ,最后赋值给 UNDEF_SYM 这个变量。
-u是接下来ld需要用的选项:

-u symbol
–undefined=symbol
Force symbol to be entered in the output file as an undefined symbol. Doing this may, for example, trigger linking of additional modules from standard libraries. -u may be repeated with different option arguments
to enter additional undefined symbols. This option is equivalent to the “EXTERN” linker script command.

作用是插入未定义变量。这里就是u_boot_cmd 变量插入到可执行文件中,顾名思义和uboot的命令有关,将所有命令提取出来插入到可执行文件中。

(5)生成.o文件、lib文件,编译api、example等目录,具体的编译要看相应目录的makefile

$(OBJS):	depend $(obj)include/autoconf.mk
		$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

$(LIBS):	depend $(obj)include/autoconf.mk
		$(MAKE) -C $(dir $(subst $(obj),,$@))

$(LIBBOARD):	depend $(LIBS) $(obj)include/autoconf.mk
		$(MAKE) -C $(dir $(subst $(obj),,$@))

$(SUBDIRS):	depend $(obj)include/autoconf.mk
		$(MAKE) -C $@ all

$(LDSCRIPT):	depend $(obj)include/autoconf.mk
		$(MAKE) -C $(dir $@) $(notdir $@)

(6)使用tools/setlocalversion脚本生成version_autogenerated.h文件

$(VERSION_FILE):
		@( printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' "$(U_BOOT_VERSION)" \
		 '$(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion $(TOPDIR))' \
		 ) > $@.tmp
		@cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@

其中CONFIG_SHELL 在config.mk文件中:

CONFIG_SHELL	:= $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
		    else if [ -x /bin/bash ]; then echo /bin/bash; \
		    else echo sh; fi ; fi)

setlocalversion和git有关,设置本地的版本名字
15.System.map和uboot.map
生成System.map文件:

$(obj)System.map:	$(obj)u-boot
		@$(NM) $< | \
		grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
		sort > $(obj)System.map

之前生成u-boot可执行文件时生成过uboot.map文件:

ld -Map uboot.map

接下来讲讲这两个文件有什么作用:
节选一小段System.map,可以看到程序的起始地址_start为c3e00010 ,该文件按链接地址由小到大的顺序列出了所有符号(SDRAM初始化完成后,需要将U-Boot加载到此地址):

地址  类型 符号
c3e00010 T _start
c3e00030 t _undefined_instruction
c3e00034 t _software_interrupt
c3e00038 t _prefetch_abort

符号类型的解释,大写表示global,相应的小写表示是local。前面几个是经常使用到的:
A绝对地址,不会再被连接(重定向?)
B未初始化数据段,常以BSS表示
D数据段
T代码段
U未定义符号
R只读数据

uboot.map节选,包含链接过程中涉及的目标文件将其所依赖的库文件

Archive member included because of file (symbol)

cpu/s5pc11x/libs5pc11x.a(interrupts.o)	 	cpu/s5pc11x/start.o (do_irq)
cpu/s5pc11x/libs5pc11x.a(cpu.o) 			cpu/s5pc11x/libs5pc11x.a(interrupts.o) (reset_cpu)
cpu/s5pc11x/libs5pc11x.a(setup_hsmmc.o) 	cpu/s5pc11x/libs5pc11x.a(cpu.o) (setup_hsmmc_cfg_gpio)

16.include/autoconf.mk
把include/common.h以及它所包含的头文件中的以“CONFIG_”为前缀的所有宏提取出来,按tools/scripts/define2mk.sed 宏处理规则来处理宏定义,并写入autoconf.mk文件,在make 命令编译源码的时候,包括autoconf.mk文件,形成源码或模块的编译规则,决定哪些模块编入镜像。

17.config配置以及config的反向操作
config:
运行脚本mkconfig,在config之前会unconfig一下,新建board/samsung/x210/config.mk,文件中指定TEXT_BASE = 0xc3e00000,TEXT_BASE是链接到SDRAM的地址,uboot开始执行的地址,我们看System.map的头地址就是来自这。
@:_config=就是将x210_nand_config替换成x210_nand,也就是mkconfig脚本传了x210_nand arm s5pc11x x210 samsung s5pc1106个参数
($@makefile中表示目标,_config=就是匹配目标中的子字符串,将字符串替换为=后面的字符串,我们这为空,所以就替换为空,也就是删除_config部分)

x210_nand_config :	unconfig
	@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
	@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

x210_sd_config :	unconfig
	@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
	@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

unconfig:

unconfig:
	@rm -f $(obj)include/config.h $(obj)include/config.mk \
		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp \
		$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep \
		$(obj)board/$(VENDOR)/$(BOARD)/config.mk

总结

makefile配置,编译过程总结:先配置,后编译,绿色为配置时生成文件,红色为编译时生成文件。
在这里插入图片描述

5.mkconfig

配置时用到的脚本,我们看看其中做了什么事,拿x210_nand_config配置为例子,上文讲到给mkconfig传6个参数,其中第一个参数表明我们配置何种类型的uboot,我这里有两种选择,x210_nand和x210_sd。我简化了一下内容,比较简单,注释了一下主要的事情:

#!/bin/sh -e
APPEND=no	# Default: Create new config file		是否存在文件的标志,如果为否,则新建文件,直接写即可不用担心覆盖内容。如果为是,则存在文件,那么不能将文件呢容覆盖,我们需要接续写,脚本接续写很简单,就是echo>>,从头写就是新建一个文件:echo>
BOARD_NAME=""	# Name to print in make output

while [ $# -gt 0 ] ; do									#$#表示参数个数,$# = 6,gt :--
	case "$1" in										#$1是x210_sd,在下面的case中没有,所以直接*)退出while循环。
	--) shift ; break ;;
	-a) shift ; APPEND=yes ;;
	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
	*)  break ;;
	esac
done

[ "${BOARD_NAME}" ] || BOARD_NAME="$1"					#BOARD_NAME=x210_sd

[ $# -lt 4 ] && exit 1									#4<=$#<=6
[ $# -gt 6 ] && exit 1

echo "Configuring for ${BOARD_NAME} board..."

#################################################################################
#创建符号链接,注意如果在Windows共享目录下,是看不到符号链接文件的,如果需要看到,则在在压缩时加上参数h:tar -cjvhf uboot.tar.bz2 uboot
#符号链接文件的存在就是整个配置过程的核心,这些符号链接文件(文件夹)的主要作用是给头文件包含等过程提供指向性连接。根本目的是让uboot具有可移植性。
cd ./include
rm -f asm												
ln -s asm-$2 asm										#指定为arm架构
													
rm -f asm-$2/arch
if [ -z "$6" -o "$6" = "NULL" ] ; then					#-z是判断是否为空,-o表示或
	ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
	ln -s ${LNPREFIX}arch-$6 asm-$2/arch				#创建arch链接,指定SOC为s5pc110
fi

# create link for s5pc11x SoC
if [ "$3" = "s5pc11x" ] ; then
        rm -f regs.h
        ln -s $6.h regs.h
        rm -f asm-$2/arch
        ln -s arch-$3 asm-$2/arch
fi

if [ "$2" = "arm" ] ; then
	rm -f asm-$2/proc
	ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi
#################################################################################

# Create include file for Make
echo "ARCH   = $2" >  config.mk
echo "CPU    = $3" >> config.mk
echo "BOARD  = $4" >> config.mk

[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk

[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk

# Create board specific header file
if [ "$APPEND" = "yes" ]	# Append to existing config file
then
	echo >> config.h
else
	> config.h		# Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h

exit 0

1.创建include/下的文件config.mk和config.h
2.六个参数的作用,总体就是为了实现uboot不同平台下的可移植性
3.config.h指向x210_nand.h,这个是我们x210开发板的主要配置文件
x210_sd.h文件会被用来生成一个autoconfig.mk文件,这个文件会被主Makefile引入,指导整个编译过程。这里面的这些宏定义会影响我们对uboot中大部分.c文件中一些条件编译的选择。从而实现最终的可移植性。

6.config.mk

主要记录了一些命令的选项环境变量、include一些生成的文件、

# clean the slate ...
PLATFORM_RELFLAGS =
PLATFORM_CPPFLAGS =
PLATFORM_LDFLAGS =

#########################################################################

CONFIG_SHELL	:= $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
		    else if [ -x /bin/bash ]; then echo /bin/bash; \
		    else echo sh; fi ; fi)

HOSTCC		= gcc
HOSTCFLAGS	= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer		#没用
HOSTSTRIP	= strip

#########################################################################
# Option checker (courtesy linux kernel) to ensure
# only supported compiler options are used
cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
		> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)

# Include the make variables (CC, etc...)
AS	= $(CROSS_COMPILE)as
LD	= $(CROSS_COMPILE)ld
CC	= $(CROSS_COMPILE)gcc
CPP	= $(CC) -E
AR	= $(CROSS_COMPILE)ar
NM	= $(CROSS_COMPILE)nm
LDR	= $(CROSS_COMPILE)ldr
STRIP	= $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
RANLIB	= $(CROSS_COMPILE)RANLIB

# Load generated board configuration
sinclude $(OBJTREE)/include/autoconf.mk

ifdef	ARCH
sinclude $(TOPDIR)/$(ARCH)_config.mk	# include architecture dependend rules
endif
ifdef	CPU
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk	# include  CPU	specific rules
endif
ifdef	SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk	# include  SoC	specific rules
endif
ifdef	VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
ifdef	BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk	# include board specific rules
endif

#########################################################################

ifneq (,$(findstring s,$(MAKEFLAGS)))			#如果静默编译,那么ar也不需要带-v选项了,-v用来打印ar内容
ARFLAGS = cr
else
ARFLAGS = crv
endif
RELFLAGS= $(PLATFORM_RELFLAGS)
DBGFLAGS= -g # -DDEBUG
OPTFLAGS= -Os #-fomit-frame-pointer
ifndef LDSCRIPT
#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
else
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
endif
OBJCFLAGS += --gap-fill=0xff

gccincdir := $(shell $(CC) -print-file-name=include)

CPPFLAGS := $(DBGFLAGS) $(OPTFLAGS) $(RELFLAGS)		\
	-D__KERNEL__
ifneq ($(TEXT_BASE),)
CPPFLAGS += -DTEXT_BASE=$(TEXT_BASE)
endif

ifneq ($(OBJTREE),$(SRCTREE))
CPPFLAGS += -I$(OBJTREE)/include2 -I$(OBJTREE)/include
endif

CPPFLAGS += -I$(TOPDIR)/include
CPPFLAGS += -fno-builtin -ffreestanding -nostdinc	\
	-isystem $(gccincdir) -pipe $(PLATFORM_CPPFLAGS)

ifdef BUILD_TAG
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes \
	-DBUILD_TAG='"$(BUILD_TAG)"'
else
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes
endif

CFLAGS += $(call cc-option,-fno-stack-protector)

# $(CPPFLAGS) sets -g, which causes gcc to pass a suitable -g<format>
# option to the assembler.
AFLAGS_DEBUG :=

AFLAGS := $(AFLAGS_DEBUG) -D__ASSEMBLY__ $(CPPFLAGS)

LDFLAGS += -Bstatic -T $(LDSCRIPT) $(PLATFORM_LDFLAGS)
ifneq ($(TEXT_BASE),)
LDFLAGS += -Ttext $(TEXT_BASE)
endif

# Location of a usable BFD library, where we define "usable" as
# "built for ${HOST}, supports ${TARGET}".  Sensible values are
# - When cross-compiling: the root of the cross-environment
# - Linux/ppc (native): /usr
# - NetBSD/ppc (native): you lose ... (must extract these from the
#   binutils build directory, plus the native and U-Boot include
#   files don't like each other)
#
# So far, this is used only by tools/gdb/Makefile.

BFD_ROOT_DIR =		/usr


#########################################################################
#有些变量又export了一遍,没什么用
export	CONFIG_SHELL HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE \
	AS LD CC CPP AR NM STRIP OBJCOPY OBJDUMP \
	MAKE
export	TEXT_BASE PLATFORM_CPPFLAGS PLATFORM_RELFLAGS CPPFLAGS CFLAGS AFLAGS

#########################################################################
%.s:	%.S
	$(CPP) $(AFLAGS) -o $@ $<
%.o:	%.S
	$(CC) $(AFLAGS) -c -o $@ $<
%.o:	%.c
	$(CC) $(CFLAGS) -c -o $@ $<

这个要讲一下:

LDFLAGS += -Bstatic -T $(LDSCRIPT) $(PLATFORM_LDFLAGS)
ifneq ($(TEXT_BASE),)
LDFLAGS += -Ttext $(TEXT_BASE)
endif

ld命令的选项,-Ttext指定了,存在自动生成的文件board/samsung/x210/config.mk,LDSCRIPT指定了链接文件board/samsung/x210/u-boot.lds。

arm_config.mk

PLATFORM_CPPFLAGS += -DCONFIG_ARM -D__ARM__

cpu/s5pc11x/config.mk

PLATFORM_RELFLAGS += -fno-strict-aliasing  -fno-common -ffixed-r8 -msoft-float

# Make ARMv5 to allow more compilers to work, even though its v6.
PLATFORM_CPPFLAGS += -march=armv5te
# =========================================================================
# Supply options according to compiler version
# =========================================================================
PLATFORM_CPPFLAGS +=$(call cc-option,-mapcs-32,-mabi=apcs-gnu)
PLATFORM_CPPFLAGS +=$(call cc-option,-mno-thumb-interwork,)
PLATFORM_RELFLAGS +=$(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,))

7.u-boot.lds

uboot的链接脚本\board\samsung\x210\u-boot.lds

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	. = 0x00000000;

#声明一些代码段优先链接,这些代码必须放在前16k才能让整个代码生效:
	. = ALIGN(4);
	.text      :
	{
	  cpu/s5pc11x/start.o	(.text)
	  cpu/s5pc11x/s5pc110/cpu_init.o	(.text)
	  board/samsung/x210/lowlevel_init.o	(.text)
          cpu/s5pc11x/onenand_cp.o      (.text)                 
          cpu/s5pc11x/nand_cp.o (.text)                     
          cpu/s5pc11x/movi.o (.text) 
          common/secure_boot.o (.text) 
	  common/ace_sha1.o (.text)
	  cpu/s5pc11x/pmic.o (.text)
	  *(.text)
	}

	. = ALIGN(4);
	.rodata : { *(.rodata) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	.got : { *(.got) }

#命令相关的设置,需特别注意,由于现在没学习cmd相关源码,之后会西细讲这里的作用
	__u_boot_cmd_start = .;
	.u_boot_cmd : { *(.u_boot_cmd) }
	__u_boot_cmd_end = .;

#mmu相关
	. = ALIGN(4);
	.mmudata : { *(.mmudata) }

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) }
	_end = .;
}

(1)ENTRY(_start)用来指定整个程序的入口地址。所谓入口地址就是整个程序的开头地址,可以认为就是整个程序的第一句指令。有点像C语言中的main。
(2)在代码段中注意文件排列的顺序。指定必须放在前面部分的那些文件就是那些必须安排在前16KB内的文件,这些文件中的函数在前16KB会被调用。在后面第二部分(16KB之后)中调用的程序,前后顺序就无所谓了。
(3)链接脚本中除了.text .data .rodata .bss段等编译工具自带的段之外,编译工具还允许我们自定义段。譬如uboot中的.u_boot_cmd段就是自定义段。自定义段很重要,之后会讲cmd的细节。

8.include/autoconfig.mk

定义平台相关的宏,其根据./include/common.h和./include/config.h也就是./include/configs/x210_sd.h。

9.tools目录

这个目录有一些帮助编译的工具,先放着。

开发板厂商提供的uboot的配置及编译

以下内容基于x210开发板:

配置:make x210_sd_config
出现:Configuring for x210_sd board…说明配置好了。
配置取决于主板和CPU类型的组合;所有此类信息都保存在配置文件“include/configs/<board_name>.h”中。

编译:make

注意事项:
1.我们需要的交叉编译工具链为arm-2009q3,注意自己是否安装
2.makefile的第147行内容为:CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
3.我们的uboot可以x210_sd_config也可以x210_nand_config,也可以通过mkmovi生成inand或者mmc使用的镜像文件

小细节
1.make distclean不彻底
某些文件在distclean、clean、unconfig后未被删除,因为其大小不一样,说明我们的uboot的ditclean、clean、unconfig是不完善的:
我们执行配置编译等动作,并用du记录整个uboot目录的大小,distclean、clean、unconfig清理uboot后任然比原始文件大,说明清理不彻底,有些文件未被清理。
make x210_sd_config后文件大小:

du -b --max-depth=1 uboot
17532575	uboot/uboot
17532786	uboot/out_dir_uboot

make unconfig后文件大小:

17532575	uboot/uboot
17532612	uboot/out_dir_uboot

make后文件大小:

26896624	uboot/out_dir_uboot

make clean后文件大小:

22708642	uboot/out_dir_uboot

make distclean 后文件大小:(distclean后再clean文件大小不变,表示distclean清理包括了clean中的内容,或者distclean 运行了clean)

17541008	uboot/out_dir_uboot

2.tools文件夹内容何时编译?
并没有找到,我把主makefile所有关于tools的编译过程全部屏蔽了,但是tools还是被编译了,很奇怪,可能我没有找到真正编译他的地方,有知道的同志求教。

没有弄明白的知识

uboot可以学到文件系统fatfs,两种启动方式:普通传参和镜像树传参fit,设备树,驱动,命令,环境变量

现在还不知道SUBDIRS 下的目录有什么作用,tools下有哪些工具。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值