×
嵌入式 > 嵌入式开发 > 详情

uboot移植详解

发布时间:2020-09-02 发布时间:
|

uboot是一段小程序,它在系统上电是开始执行,初始化硬件设备;准备好软件环境;最后调用操作系统内核。

这里主要分析移植过程。

U-boot中有几千个文件,要想了解对于某款开发板,使用哪些文件、哪个文件先执行、可执行文件占用内存的情况,最好的方法就是阅读它的Makefile文件。

要想使用哪款开发板就需首先执行“make _config”命令进行配置,然后执行“make all”就可生成三个文件,分别是:

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 ">>config.h

创建新的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目录下新建一个开发板的目录,则在include/configs目录下也要建立一个文件,里面存放的就是开发板的配置信息。

配置文件中有两类宏:一类是选项(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_config”命令就直接执行“makeall”命令则会出现如下的才错误信息,然后停止编译:

Systemnotconfigured-seeREADME

U-Boot是如何知道用户没有执行过“make_config”命令的呢?阅读U-Boot源代码就可以发现了,Makefile中有如下代码:

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_config”命令执行过程生成的,若从没有执行过“make_config”命令则include/config.mk必然不存在。因此Make就执行else分支的代码,在输出“Systemnotconfigured-seeREADME”的信息后就返回了。

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_.*\)/-u\1/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确定了连接的方式。(前面有提到)。

分析完这些对移植的整体轮廓应该有了一个认识,了解其原理了。具体要修改什么就要根据实际需要了。



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
一只老鸟的嵌入式ARM学习心得