uboot移植详解
《嵌入式Linux应用开发完全手册》 韦东山编著 第15章 移植 U-boot
本文引用地址:https://www.eepw.com.cn/article/201611/316102.htmhttp://xgc94418297.blog.163.com/blog/static/112966040200952971543686/
uboot是一段小程序,它在系统上电是开始执行,初始化硬件设备;准备好软件环境;最后调用操作系统内核。
这里主要分析移植过程。
U-boot中有几千个文件,要想了解对于某款开发板,使用哪些文件、哪个文件先执行、可执行文件占用内存的情况,最好的方法就是阅读它的Makefile文件。
要想使用哪款开发板就需首先执行“make
U-Boot.bin:二进制可执行文件,可直接烧入ROM、NORFLASH
U-Boot.elf
U-Boot.srec:Motorola S-Record格式的可执行文件
U-Boot编译命令
对于TX2440开发板,编译U-Boot需要执行如下的命令:
$makeTX2440_config
$makeall
使用上面的命令编译U-Boot,编译生成的所有文件都保存在源代码目录中。为了保持源代码目录的干净,可以使用如下命令将编译生成的文件输出到一个外部目录,而不是在源代码目录中,下面的2种方法都将编译生成的文件输出到/tmp/build目录:
$exportBUILD_DIR=/tmp/build
$makeTX2440_config
$makeall
或
$makeO=/tmp/buildTX2440_config(注意是字母O,而不是数字0)
$makeall
makeTX2440_config命令执行过程
下面分析命令“makeTX2440_config”执行过程,为了简化分析过程这里主要分析将编译目标输出到源代码目录的情况。
TX2410_config:unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t TX2410 NULL s3c24x0
其中的依赖“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
其中“@”的作用是执行该命令时不在shell显示。“obj”变量就是编译输出的目录,因此“unconfig”的作用就是清除上次执行make*_config命令生成的配置文件(如include/config.h,include/config.mk等)。
$(MKCONFIG)在上面指定为“$(SRCTREE)/mkconfig”。$(@:_config=)为将传进来的所有参数中的_config替换为空(其中“@”指规则的目标文件名,在这里就是“TX2440_config”。$(text:patternA=patternB),这样的语法表示把text变量每一个元素中结尾的patternA的文本替换为patternB,然后输出)。因此$(@:_config=)的作用就是将TX2440_config中的_config去掉,得到TX2440。
因此@$(MKCONFIG) $(@:_config=) arm arm920t TX2440 NULL s3c24x0”实际上就是执行了如下命令:
./mkconfigTX2440arm arm920t TX2410 NULL s3c24x0
即将“TX2440arm arm920t TX2440 NULL s3c24x0”作为参数传递给当前目录下的mkconfig脚本执行。
在mkconfig脚本中给出了mkconfig的用法:
#Parameters:TargetArchitectureCPUBoard[VENDOR][SOC]
因此传递给mkconfig的参数的意义分别是:
TX2440:Target(目标板型号)
arm:Architecture(目标板的CPU架构)
arm920t:CPU(具体使用的CPU型号)
TX2440:Board
NULL:VENDOR(生产厂家名)
s3c24x0:SOC
下面分步分析mkconfig的作用:
(1)确定开发板的名称BOARD_NAME
APPEND=no #no表示创建新的配置文件,yes表示追加到配置文件中
BOARD_NAME="" #Nametoprintinmakeoutput
TARGETS=""
while[$#-gt0];do
case"$1"in
--)shift;break;;
-a)shift;APPEND=yes;;
-n)shift;BOARD_NAME="${1%%_config}";shift;;
-t)shift;TARGETS="`echo$1|seds:_::g`${TARGETS}";shift;;
*)break;;
esac
done
["${BOARD_NAME}"]||BOARD_NAME="$1"
环境变量$#表示传递给脚本的参数个数,这里的命令有6个参数,因此$#是6。shift的作用是使$1=$2,$2=$3,$3=$4….,而原来的$1将丢失。因此while循环的作用是,依次处理传递给mkconfig脚本的选项。由于我们并没有传递给mkconfig任何的选项,因此while循环中的代码不起作用。
最后将BOARD_NAME的值设置为$1的值,在这里就是“TX2440”。
(2)创建到平台/开发板的头文件连接
33 if["$SRCTREE"!="$OBJTREE"];then/******判断源代码目录和目标文件目录是否一样,可以选择在其他目录下编译U-boot这可令代码目录保持干净。我们使用的是直接在源代码目录下编译的,第33行不满足,跳到else分支的代码******/
45 else
46 cd./include
47 rm-fasm
48 ln-sasm-$2asm
49 fi
50
第46~48行进入include目录,删除asm文件(上一次配置时建立的链接文件),然后再次建立asm文件,并令它链向 asm-$2目录,即asm-arm.
51 rm-fasm-$2/arch
52
53 if[-z"$6"-o"$6"="NULL"];then
54 ln-s${LNPREFIX}arch-$3asm-$2/arch
55 else
56 ln-s${LNPREFIX}arch-$6asm-$2/arch
57 fi
建立符号链接include/asm-arm/arch,若$6(SOC)为空,则使其链接到include/asm-arm/arch-arm920t目录,否则就使其链接到include/asm-arm/arch-s3c24x0目录。(事实上include/asm-arm/arch-arm920t并不存在,因此$6是不能为空的,否则会编译失败)
59 if["$2"="arm"];then
60 rm-fasm-$2/proc
61 ln-s${LNPREFIX}proc-armvasm-$2/proc
62 fi
若目标板是arm架构,则上面的代码将建立符号连接include/asm-arm/proc,使其链接到目录proc-armv目录。
建立以上的链接的好处:编译U-Boot时直接进入链接文件指向的目录进行编译,而不必根据不同开发板来选择不同目录。
(3)创建顶层Makefile包含的文件include/config.mk
64 #
65 #CreateincludefileforMake
66 #
67 echo"ARCH=$2">config.mk
68 echo"CPU=$3">>config.mk
69 echo"BOARD=$4">>config.mk
70
71 ["$5"]&&["$5"!="NULL"]&&echo"VENDOR=$5">>config.mk
72
73 ["$6"]&&["$6"!="NULL"]&&echo"SOC=$6">>config.mk
上面代码将会把如下内容写入文件inlcude/config.mk文件:
ARCH=arm
CPU=arm920t
BOARD=TX2440
SOC=s3c24x0
(4)创建开发板相关的头文件include/config.h
75 #
76 #Createboardspecificheaderfile
77 #
78 if["$APPEND"="yes"] #Appendtoexistingconfigfile
79 then
80 echo>>config.h
81 else
82 >config.h #Createnewconfigfile
83 fi
84 echo"/*Automaticallygenerated-donotedit*/">>config.h
85 echo "#include
创建新的include/config.h文件。若APPEND为yes,则将新的配置内容追加到include/config.h文件后面。由于APPEND的值保持“no”,因此config.h被创建了,内容如下:
/*Automaticallygenerated-donotedit*/
#include
下面总结命令makeTX2440_config执行的结果(仅针对编译目标输出到源代码目录的情况):
(1) 创建开发板名称BOARD_NAME等于$1
(2)创建到目标板相关的文件的链接
ln-sasm-armasm
ln-sarch-$6asm-$2/arch
ln-sproc-armvasm-arm/proc //如果$2不是arm,此行没有
(3)创建i顶层Makefile包含的文件 include/config.mk文件,内容如下所示:
ARCH=$2
CPU=$3
BOARD=$4
VENDOR=$5
SOC=$6
(4)创建与目标板相关的文件include/config.h,如下所示:
/*Automaticallygenerated-donotedit*/
#include
从这四个结果可以知道,如果要在board目录下新建一个开发板
配置文件中有两类宏:一类是选项(option)前缀为“CONFIG_”它们用于选择CPU、SOC、开发板类型、设置系统时钟、选择设备驱动。
如:#define CONFIG_ARM920T1/* This is an ARM920T Core*/
#defineCONFIG_S3C24101/* in a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK24101/* on a SAMSUNG SMDK2410 Board */
另一类是参数(setting),前缀为“CFG_”,它们用于设置malloc缓冲池的大小、U-BOOT提示符、U-boot下载文件时的默认加载地址、FLASH的起始地址等
如:#defineCFG_LONGHELP/* undef to save memory*/
#defineCFG_PROMPT"SMDK2410 # "/* Monitor Command Prompt*/
#defineCFG_CBSIZE256/* Console I/O Buffer Size*/
#defineCFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */
#defineCFG_MAXARGS16/* max number of command args*/
#define CFG_BARGSIZECFG_CBSIZE/* Boot Argument Buffer Size*/
可以这样认为,“CONFIG_”除了设置一些参数外,主要来设置U-Boot的功能、选择使用文件中的哪一部分;而“CFG_”用来设置更细节的参数。
U-Boot的编译、链接过程
配置完后,执行“make all”既可编译,从Makefile中可以了解U-Boot使用了哪些文件、哪个文件首先执行、可执行文件占用内存情况。
若没有执行过“make
Systemnotconfigured-seeREADME
U-Boot是如何知道用户没有执行过“make
ifeq($(obj)include/config.mk,$(wildcard$(obj)include/config.mk))#config.mk存在
all:
sinclude$(obj)include/autoconf.mk.dep
sinclude$(obj)include/autoconf.mk
……
else #config.mk不存在
……
@echo"Systemnotconfigured-seeREADME">&2
@exit1
……
endif #config.mk
若include/config.mk文件存在,则$(wildcard$(obj)include/config.mk)命令执行的结果是“$(obj)include/config.mk”展开的字符串,否则结果为空。由于include/config.mk是“make
117 include$(obj)include/config.mk
118 export ARCHCPUBOARDVENDORSOC
119
127 ifeq($(HOSTARCH),arm)
128 CROSS_COMPILE=arm-linux-
127 endif
163 #loadotherconfiguration
164 include$(TOPDIR)/config.mk
第117和164行用于包含其他的config.mk文件,第117行所包含的文件就是在上面的配置过程中制造出来的include/config.mk文件,其中定义了ARCH、CPU、BOARD、SOC等4个变量的值为arm、arm920t、TX2440、s3c24x0。第164行包含顶层的config.mk文件,它根据上面4个变量的值确定了编译器、编译选项。其中对我们理解编译过程有帮助的是BOARDDIR、LDFLAGS的值
#U-Bootobjects....orderisimportant(i.e.startmustbefirst)
169 OBJS=cpu/$(CPU)/start.o
LIBS+=cpu/$(CPU)/lib$(CPU).a
ifdefSOC
LIBS+=cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
ifeq($(CPU),ixp)
LIBS+=cpu/ixp/npe/libnpe.a
endif
LIBS+=lib_$(ARCH)/lib$(ARCH).a
LIBS+=fs/cramfs/libcramfs.afs/fat/libfat.afs/fdos/libfdos.afs/jffs2/libjffs2.a
fs/reiserfs/libreiserfs.afs/ext2/libext2fs.afs/yaffs2/libyaffs2.a
fs/ubifs/libubifs.a
……
LIBS+=common/libcommon.a
LIBS+=libfdt/libfdt.a
LIBS+=api/libapi.a
LIBS+=post/libpost.a
LIBS:=$(addprefix$(obj),$(LIBS))
LIBS变量指明了U-Boot需要的库文件,包括平台/开发板相关的目录、通用目录下相应的库,都通过相应的子目录编译得到的。
OBJS、LIBS所代表的.o、.a文件就是U-Boot的构成,他们通过如下命令由相应的源文件或相应的子目录下的文件编译得到
268 $(OBJS):
269 $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
271 $(LIBS):
272 $(MAKE) -C $(dir $(subst $(obj),,$@))
274 $(SUBDIRS):
275$(MAKE) -C $@ all
第268、269两行的规则表示,对于OBJS中的每个成员,都将进入cpu/$(cpu)目录编译他们。现在OBJS为cpu/arm920t/start.o,它将由cpu/arm920t/start.s编译得到。第271、272两行规则表示,对于LIBS中的每个成员,都将进入相应的子目录执行“make”命令。这些子目录中的Makefile,结构相似,他们将Makefile中指定的文件编译、链接成一个库文件。
当所有的OBJS、LIBS所表示的.o、.a文件都生成后就剩下链接了,这对用Makefile中如下代码
245 $(obj)u-boot.srec:$(obj)u-boot
246 $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
248 $(obj)u-boot.bin:$(obj)u-boot
249$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
251 $(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 $< $@
263 $(obj)u-boot.dis:$(obj)u-boot
254 $(OBJDUMP) -d $< > $@
265 $(obj)u-boot:depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e s/.*(__u_boot_cmd_.*)/-u1/p|sort|uniq`;
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS)
266 --start-group $(__LIBS) --end-group $(PLATFORM_LIBS)
267 -Map u-boot.map -o u-boot
263到267的规则链接到ELF格式的U-Boot,最后转换为二进制格式的U-Boot.bin、S-Record格式的U-Boot.srec。LDFLAGS确定了连接的方式。(前面有提到)。
分析完这些对移植的整体轮廓应该有了一个认识,了解其原理了。具体要修改什么就要根据实际需要了。
评论