资讯专栏INFORMATION COLUMN

基于ARM处理器的U-BOOT详细移植总结

zengdongbao / 3943人阅读

摘要:作用是将标准输入中的所有大写字母转换为响应的小写字母。的移植过的源代码是在源代码目录下编译的,所以源代码目录等于目标文件目录,所以条件不满足,将执行分支的代码。

        一个嵌入式产品的开发阶段,需要不断地把bootloader下载到存储器中,如果存储器使用nand flash,但是第一次里面什么都没有,所以只能根据处理器的启动方式从其他方式启动如sd卡或nor存储器启动,然后在SD卡或nor存储器启动的基础之上使用USB或网络接口把u-boot.bin先下载到内存中,然后再把内存中的内容写到nand中,但是写前4页时只能写每页的前2KB数据(对于OK6410开发板来说,处理器使用S3C6410处理器,nand使用每页4KB的存储器,当从nand启动时,处理器会自动地把nand的前4页的每一页的前2KB拷贝片内8KB的SRAM中运行,这是处理器硬件所决定,所以这里只能存每一页的前2KB),前4页后的所有页都是全写。

        由于u-boot开发中需要不断调试u-boot,而此时nand中已经有u-boot,所以可以从nand存储器启动,然后根据开发的下载模式下的菜单选项,可以重新下载u-boot到nand中。

        当u-boot开发好后,并且从nand启动,一上电,处理器硬件会自动把nand flash的前4页中每页前2KB拷贝到片内SRAM中运行,而在SRAM中运行的代码又实现了把nand中从0到240KB(这个大小可以变)的代码拷贝到内存,然后跳到内存中运行,它在内存运行时,会申请更多的空间(除开本身占用的内存空间外,会包括12字节用于abort异常、堆栈空间、malloc内存池空间、环境参数空间、某些全局变量空间),总共2MB,详细查看(三.2.1)u-boot内存分布图。

        一个嵌入式产品出厂时,在nand 存储器里面已经有了u-boot、内核、文件系统,并能实现对应功能(如果使用nand存储器,那么编译好的映像是存储在nand中的,运行是在内存中运行,对于S3C6410,先在8KB片内SRAM运行,然后才跳到内存DDR中运行)。U-boot运行时会把nand里的内核映像拷贝到内存,然后运行内核。

一、U-Boot-1.1.6顶层Makefile文件分析

(顶层makefile文件内容为黑色,其他文件中的内容为蓝色,移植时修改过的代码为红色)

       根据uboot根目录下的Readme文件的说明,可以知道如果想把u-boot使用于开发板,应先配置,即执行make orlinx_nand_ram256_config命令进行配置(在顶层目录Makefile中加入forlinx_nand_ram256_config目标等选项),然后执行make all,就可以生成如下3个文件:U-Boot.bin、U-Boot ELF格式文件、U-Boot.srec。

  1. U-Boot的配置过程

        (1)版本说明

VERSION = 1PATCHLEVEL = 1SUBLEVEL = 6EXTRAVERSION =U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)VERSION_FILE = $(obj)include/version_autogenerated.h

(2)定义主机系统架构

HOSTARCH := $(shell uname -m | /	sed -e s/i.86/i386/ /	    -e s/sun4u/sparc64/ /	    -e s/arm.*/arm/ /	    -e s/sa110/arm/ /	    -e s/powerpc/ppc/ /	    -e s/macppc/ppc/)

        “sed  –e”表示后面跟的是一串命令脚本,而表达式“s/abc/def/”表示要从标准输入中,查找到内容为“abc”的,然后替换成“def”。其中“abc”表达式用可以使用“.”作为通配符。命令“uname –m”将输出主机 CPU 的体系架构类型。如电脑使用 Intel Core2 系列的CPU,那么 “uname  –m”将输出“i686”。 “i686”可以匹配命令“sed  -e s/i.86/i386/”中的“i.86”,因此在机器上执行 Makefile,HOSTARCH 将被设置成“i386” 。

(3)定义主机操作系统类型

HOSTOS := $(shell uname -s | tr "[:upper:]" "[:lower:]" | /	    sed -e "s//(cygwin/).*/cygwin/")export	HOSTARCH HOSTOS# Deal with colliding definitions from tcsh etc.VENDOR=

        “uname  –s”输出主机内核名字,开发主机使用 Linux 发行版 fedora-12,因此“uname  –s”结果是“Linux”。“tr "[:upper:]" "[:lower:]"”作用是将标准输入中的所有大写字母转换为响应的小写字母。因此执行结果是将 HOSTOS 设置为“linux”。

(4)定义执行shell脚本的shell(源码中没有这部分)

# Set shell to bash if possible, otherwise fall back to shSHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; /else if [ -x /bin/bash ]; then echo /bin/bash; /else echo sh; fi; fi)

        "$$BASH"的作用实质上是生成了字符串“$BASH”(前一个$号的作用是指明第二个$是普通的字符)。若执行当前 Makefile 的 shell 中定义了“$BASH”环境变量,且文件“$BASH”是可执行文件,则 SHELL 的值为“$BASH”。否则,若“/bin/bash”是可执行文件,则 SHELL 值为“/bin/bash”。若以上两条都不成立,则将“sh”赋值给 SHELL 变量。如果机器安装了bash  shell,且shell 默认环境变量中定义了“$BASH”,因此 SHELL 被设置为$BASH 。

(5)设定编译输出目录

ifdef O ifeq ("$(origin O)", "command line")BUILD_DIR := $(O)endifendif

        函数$( origin, variable) 输出的结果是一个字符串,输出结果由变量 variable 定义的方式决定,若 variable 在命令行中定义过,则origin函数返回值为"command line"。假若在命令行中执行了“export BUILD_DIR=/tmp/build”的命令,则“$(origin O)”值为“command line”,而 BUILD_DIR 被设置为“/tmp/build”。

        下面内容表示若${BUILD_DIR}表示的目录没有定义,则创建该目录:

ifneq ($(BUILD_DIR),)saved-output := $(BUILD_DIR)# Attempt to create a output directory.$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})

        下面内容表示若$(BUILD_DIR)为空,则将其赋值为当前目录路径(源代码目录)。并检查$(BUILD_DIR)目录是否存在:

# Verify if it was successful.BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))endif # ifneq ($(BUILD_DIR),)

        在下面内容中,CURDIR 变量指示 Make 当前的工作目录,由于当前 Make 在 U-Boot 顶层目录执行 Makefile,因此 CURDIR 此时就是 U-Boot 顶层目录。执行完下面的代码后, SRCTREE,src变量就是U-Boot代码顶层目录,而OBJTREE,obj变量就是输出目录,若没有定义BUILD_DIR环境变量,则SRCTREE, src变量与OBJTREE,obj变量都是U-Boot 源代码目录。而MKCONFIG则表示U-Boot根目录下的mkconfig 脚本。

OBJTREE		:= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))SRCTREE		:= $(CURDIR)TOPDIR		:= $(SRCTREE)LNDIR		:= $(OBJTREE)export	TOPDIR SRCTREE OBJTREEMKCONFIG	:= $(SRCTREE)/mkconfigexport MKCONFIGifneq ($(OBJTREE),$(SRCTREE))REMOTE_BUILD 	:= 1export REMOTE_BUILDendif# $(obj) and (src) are defined in config.mk but here in main Makefile# we also need them before config.mk is included which is the case for# some targets like unconfig, clean, clobber, distclean, etc.ifneq ($(OBJTREE),$(SRCTREE))obj := $(OBJTREE)/src := $(SRCTREE)/elseobj :=src :=endifexport obj src

        在上面内容中,MKCONFIG      := $(SRCTREE)/mkconfig即在根目录下的mkconfig文件。

6)执行make  forlinx_nand_ram256_config过程

        分析这个过程有助于理解移植U-Boot过程中需要修改哪些文件。执行这个命令前提是在移植U-Boot时,在根目录的Makefile中加入了类似如下的内容:

forlinx_nand_ram256_config :  unconfig	@$(MKCONFIG)  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256	其中的依赖“unconfig”定义如下(Makefile文件的330-350行左右):unconfig:	@rm -f $(obj)include/config.h $(obj)include/config.mk /		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp

        其中“@”的作用是执行该命令时不在 shell 显示。“obj”变量就是编译输出的目录,因此“unconfig”的作用就是清除上次执行 make *_config 命令生成的配置文件(如include/config.h,include/config.mk 等)。

        $(MKCONFIG)在上面(5)指定为“$(SRCTREE)/mkconfig”,即根目录的mkconfig文件。如果有$(@:_config=)一项,$(@:_config=)为将传进来的所有参数中的_config 替换为空(其中“@”指规则的目标文件名,在这里就是“forlinx_nand_ram256_config ”。$(text:patternA=patternB),这样的语法表示把 text 变量每一个元素中结尾的 patternA 的文本替换为 patternB,然后输出)。因此$(@:_config=)的作用就是将forlinx_nand_ram256_config中的_config 去掉,得到 forlinx_nand_ram256,而在OK6410移植过的U-BOOT中没有$(@:_config=)项。

    根据以上分析,执行完make forlinx_nand_ram256_config,实际上执行如下命令:

./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256

        所以执行make forlinx_nand_ram256_config即将“smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256”作为参数传递给当前目录下的mkconfig 脚本执行。这些参数实际意义如下:

smdk6410:Target(目标板型号)

arm:Architecture (目标板的CPU架构)

s3c64xx:CPU(具体使用的 CPU型号)

smdk6410:Board

samsung:VENDOR(生产厂家名)

s3c6410:SOC

NAND:

ram256:

在mkconfig文件中,将进行如下几点的工作:

  1. 确定开发板名称BOARD_NAME
APPEND=no  		# no 表示创建新的配置文件,yes 表示追加到配置文件中BOARD_NAME=""  	# Name to print in make outputTARGETS=""while [ $# -gt 0 ] ; docase "$1" in--) shift ; break ;;-a) shift ; APPEND=yes ;;-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;-t) shift ; TARGETS="`echo $1 | sed "s:_: :g"` ${TARGETS}" ; shift ;;*) break ;;esacdone[ "${BOARD_NAME}" ] || BOARD_NAME ="$1"

        对于命令./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256其中没有“--”“-a”“-n”“-t”“*”符号,所以while循环里面没有做任何事情。而头两行中的两个值仍然维持原来的值,但执行完最后一行后,BOARD_NAME的值等于第1个参数,即smdk6410(传进的几个参数在mkconfig文件中以$x表示,$0= mkconfig$1= smdk6410$2= arm$3= s3c64xx$4= smdk6410$5= samsung$6= s3c6410$7= NAND$8= ram256)

注意:

        在U-Boot1.1.6-for-OK6410源代码中BOARD_NAME=""一行后还有一行SETMMU="no",表示在nand启动是用mmu,只是这里先把值设置为no

  1. 检查参数合法性

[ $# -lt 4 ] && exit 1

[ $# -gt 9 ] && exit 1

echo "Configuring for ${BOARD_NAME} board which boot from $7 $8 $9..."

    上面代码的作用是检查参数个数和参数是否正确,参数个数少于 4 个或多于9个都被认为是错误的。环境变量$#表示传递给脚本的参数个数,这里的命令有9个参数,因此$#是9。Configuring for ${BOARD_NAME} board which boot from $7 $8 $9...即执行完make forlinx_nand_ram256_config命令后串口终端输出的信息。

  1. 创建到平台/开发板相关的头文件的符号连接
# Create link to architecture specific headersif [ "$SRCTREE" != "$OBJTREE" ] ; then	mkdir -p ${OBJTREE}/include	mkdir -p ${OBJTREE}/include2	cd ${OBJTREE}/include2	rm -f asm	ln -s ${SRCTREE}/include/asm-$2 asm	LNPREFIX="../../include2/asm/"	cd ../include	rm -rf asm-$2	rm -f asm	mkdir asm-$2	ln -s asm-$2 asmelse	cd ./include	rm -f asm	ln -s asm-$2 asm  # 符号连接,即软链接fi

        第一行代码判断源代码目录和目标文件目录是否一样,可以选择在其他目录下编译U-BOOT,这可令源代码保持干净,可以同时使用不同的配置进行编译。OK6410的U-BOOT移植过的源代码是在源代码目录下编译的,所以源代码目录等于目标文件目录,所以条件不满足,将执行else分支的代码。

        在else分支的代码中,先进入include目录,删除asm文件(这是上一次配置时建立的链接文件),然后再次建立asm文件,并令它链接向asm-$2目录,即asm-arm目录。此举的作用为:在源码中常调用头文件,如#include ,对不同架构需要修改”asm-XXX架构”,先建立asm到asm-arm的符号链接后,以后包含头文件时直接包含

1:rm -f asm-$2/arch2:if [ -z "$6" -o "$6" = "NULL" ] ; then3:		ln -s ${LNPREFIX}arch-$3 asm-$2/arch4:else5:		ln -s ${LNPREFIX}arch-$6 asm-$2/arch6:fi7:if [ "$2" = "arm" ] ; then8:		rm -f asm-$2/proc9:		ln -s ${LNPREFIX}proc-armv asm-$2/proc10:fi

        第1行删除include/asm-arm/arch目录,对于命令./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256$6S3C6410,不为空,也不为NULL,所以第2行条件不满足,执行else分支。第5行中,LNPREFIX为空(在顶层makefile中未定义),所以第5行即执行ln  –s  arch-s3c6410  asm-arm/arch(在U-Boot1.1.6-for-OK6410源代码中,后面又把include/asm-arm/arch链接到了include/asm-arm/arch-s3c64xx)。

       8-9行表示:若目标板是arm架构,则上面的代码将建立符号连接 include/asm-arm/proc,使其链接到目录 include/asm-arm/proc-armv 目录。建立以上的链接的好处:编译 U-Boot 时直接进入链接文件指向的目录进行编译,而不必根据不同开发板来选择不同目录。

注意:

    U-Boot1.1.6-for-OK6410源代码中,在第6行到7行之间,增加了如下部分代码for OK6410

# create link for s3c24xx SoC

if [ "$3" = "s3c24xx" ] ; then

       rm -f regs.h

       ln -s $6.h regs.h

       rm -f asm-$2/arch

       ln -s arch-$3 asm-$2/arch

fi

# create link for s3c64xx SoC

if [ "$3" = "s3c64xx" ] ; then

       rm -f regs.h

       ln -s $6.h regs.h

       rm -f asm-$2/arch

       ln -s arch-$3 asm-$2/arch

fi

即如果"$3" = "s3c64xx",将删除include/regs.h,并把regs.h链接到include/s3c6410.h,在此头文件中做了写S3C6410的寄存器定义。然后删除include/asm-arm/arch目录,重新建立并把include/asm-arm/arch目录链接到include/asm-arm/arch-S3C64xx目录。

    在第9行和10行之间,增加了如下部分代码:

fi

# create link for s3c64xx-mp SoC

if [ "$3" = "s3c64xx-mp" ] ; then

       rm -f regs.h

       ln -s $6.h regs.h

       rm -f asm-$2/arch

       ln -s arch-$3 asm-$2/arch

       由于$3=s3c64xx,所以上面代码中条件不成立,即上面代码没有做任何事。

        创建顶层Makfile包含的文件include/config.mk

# Create include file for Makeecho "ARCH = $2" > config.mk   echo "CPU = $3" >> config.mkecho "BOARD = $4" >> config.mk[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk

        当执行“./mkconfig  smdk6410  arm  s3c64xx  smdk6410  samsung  s3c6410  NAND  ram256”命令后,上面几行代码创建的include/config.mk文件内容如下:

ARCH = armCPU = s3c64xxBOARD = smdk6410VENDOR = samsungSOC = s3c6410
  1. 指定开发板代码所在目录

(可选,如果待移植的U-Boot源代码中已经有了那些目录,就不需要下面的代码,比如三星官方提供的U-Boot源代码。)

# Assign board directory to BOARDIR variableif [ -z "$5" -o "$5" = "NULL" ] ; thenBOARDDIR=$4elseBOARDDIR=$5/$4fi

        以上代码指定 board 目录下的一个目录为当前开发板专有代码的目录。若$5VENDOR)为空则BOARDDIR设置为$4BOARD),否则设置为$5/$4VENDOR/BOARD,即samsung/smdk6410)。在这里由于$5 不为空,即BOARDDIR 被设置为 samsung/smdk6410

  1. 创建开发板相关的头文件include/config.h
if [ "$APPEND" = "yes" ]			# Append to existing config file2:then3: 	echo >> config.h4:else5:		> config.h				# Create new config file6:fi7:echo "/* Automatically generated - do not edit */" >>config.h8:echo "#include " >>config.h9:exit 0	“>” 和 “>>”为linux命令,> config.h表示重新建立config.h文件,echo "#include " >>config.h表示把#include 添加到config.h文件中。	APPEND维持原值”no”,所以config.h被重新建立,并添加了如下内容:/*  Automatically  generated  -  do  not  edit  */#include 	到这里,include/config.h文件中就有以上的内容了。
注意:	在U-Boot1.1.6-for-OK6410源代码中,在第7行到8行之间,增加了如下部分代码for OK6410:	case $7 inSD)	echo "#define FORLINX_BOOT_SD"   >> config.h        SETMMU="no"	;;NAND)	echo "#define FORLINX_BOOT_NAND" >> config.h        SETMMU="yes"	;;*)	;;esaccase $8 inram128)	echo "#define FORLINX_BOOT_RAM128" >> config.h         > ../board/samsung/smdk6410/config.mk    # clear file context        echo "ifndef TEXT_BASE"  >> ../board/samsung/smdk6410/config.mk        if [ ${SETMMU} = "yes" ]        then         echo "TEXT_BASE = 0xC7E00000" >> ../board/samsung/smdk6410/config.mk        else          echo "TEXT_BASE = 0x57E00000" >> ../board/samsung/smdk6410/config.mk        fi       echo "endif" >> ../board/samsung/smdk6410/config.mk	;;ram256)	echo "#define FORLINX_BOOT_RAM256" >> config.h         > ../board/samsung/smdk6410/config.mk # clear file context        echo "ifndef TEXT_BASE"  >> ../board/samsung/smdk6410/config.mk        if [ ${SETMMU} = "yes" ]        then         echo "TEXT_BASE = 0xCFE00000" >> ../board/samsung/smdk6410/config.mk        else          echo "TEXT_BASE = 0x5FE00000" >> ../board/samsung/smdk6410/config.mk        fi        echo "endif" >> ../board/samsung/smdk6410/config.mk	;;*)	;;esacif [ "$9" = "hdmi" ] ; then	echo "#define FORLINX_LCDOUT_HDMI" >> config.hfi	对于OK6410,$7=NAND,所以会把#define FORLINX_BOOT_NAND添加到include/config.h文件中,并把SETMMU值设置为yes(在上面确定开发板名称BOARD_NAME部分代码中把SETMMU值设为no,如果从nand启动,需要用到mmu,所以这里设置为yes)。	对于OK6410,$8=ram256,所以会把#define FORLINX_BOOT_RAM256添加到include/config.h文件中,并且会把include的上层目录的/board/samsung/smdk6410/config.mk文件(开发板代码所在目录)清空(执行“> ../board/samsung/smdk6410/config.mk # clear file context”一行即实现清空), 清空后再往里面添加ifndef  TEXT_BASE,由于SETMMU的值为yes,所以再往里面添加TEXT_BASE = 0xCFE00000(此地址为映射过的地址,使用mmu后,这个内存地址被映射。如果是128内存,此值为0XC7E00000,只要不超过DMC1的最大范围物理地址0x6FFFFFFF对应的虚拟地址即可),最后添加endif。	对于OK6410,$9没有被传入,所以没有把#define FORLINX_LCDOUT_HDMI添加到/include/ config.h文件中。

        总的来说,执行make forlinx_nand_ram256_config命令后,会在mkconfig脚本中进行上面第(6)点中的所有动作,由这些动作可知,要在board目录下新建一个开发板< board_name >目录,在include/configs目录下建立一个.h,里面存放的就是开发板< board_name >的配置信息。

       U-Boot还没有类似Linux一样的可视化配置界面(如用make menuconfig来配置),需要手动修改配置头文件/include/configs/smdk6410.h来裁剪、设置U-Boot。此配置头文件中有以下两类宏:

  • 一类是选项(Options),前缀为“CONFIG_”,它们用于选择CPU、SOC、开发板类型,设置系统时钟、选择设备驱动等。比如:

#define CONFIG_S3C6410          1      /* in a SAMSUNG S3C6410 SoC      */

#define CONFIG_S3C64XX       1      /* in a SAMSUNG S3C64XX Family   */

#define CONFIG_SMDK6410      1      /* on a SAMSUNG SMDK6410 Board  */

  • 另一类是参数(Setting),前缀为“CFG_”,它们用于设置malloc缓冲池的大小、U-BOOT的提示符、U-BOOT下载文件时的默认加载地址、Flash的起始地址等。比如:

#define CFG_MALLOC_LEN  (CFG_ENV_SIZE+128*1024)

#define CFG_PROMPT  

#define CFG_LOAD_ADDR  0x50000000

从下面的编译、链接过程可知,U-Boot中几乎每个文件都被编译和链接,但是这样文件是否包含有效代码,则由宏开关来设置。比如对于网卡驱动drivers/dm9000x.c,它的格式为:#include #include #include #ifdef CONFIG_DRIVER_DM9000#include "dm9000x.h"/*实际代码*/…#endif				/* CONFIG_DRIVER_DM9000 */

        如果在/include/configs/smdk6410.h中定义了宏CONFIG_DRIVER_DM9000,则文件中包含有效代码;否则,文件被注释为空。

    可以这样认为,“CONFIG_”除了设置一些参数外,主要用来设置U-Boot的功能、选择使用文件中的哪一部分;而“CFG_”用来设置更细节的参数。

2. U-Boot的编译、链接过程(接着顶层目录中的Makefile分析)

(1)顶层Config.mk文件、顶层Makefile文件剩余代码分析

配置完后,执行“ make  all ”即可编译。若没有执行过“make forlinx_nand_ram256_config”命令就直接执行“make all”命令则会出现“System not configured - see README”错误信息,

然后停止编译。

       由于执行完“make forlinx_nand_ram256_config”命令后,会在include目录下创建一个config.mk文件,U-Boot就是判断是否有这个文件而确定用户是否执行过“make forlinx_nand_ram256_config”命令,注意exit 1即返回,相关代码如下:

114: ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))…241: all:…249: $(obj)u-boot.bin:	$(obj)u-boot250:      $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@	  …322: else	  …327:	 @echo "System not configured - see README" >&2328:	 @ exit 1329: endif	在顶层Makefile中,继续分析如下代码:# load ARCH, BOARD, and CPU configuration  下面为与ARM相关部分117:  include $(OBJTREE)/include/config.mk118:  export	ARCH CPU BOARD VENDOR SOC…127:  ifeq ($(ARCH),arm)128:  CROSS_COMPILE = arm-linux-129:  endif…162:  export	CROSS_COMPILE164:  # load other configuration165:  include $(TOPDIR)/config.mk

上面代码中127-129行表示ARCH与目标机器体系架构相同,则使用 arm-linux编译器(ARCH=arm)。

注意:

U-Boot1.1.6-for-OK6410源代码中,162行前面加了如下代码:

CROSS_COMPILE = /usr/local/arm/4.3.2/bin/arm-linux-即不管上面如何判断等,交叉编译器始终使用/usr/local/arm/4.3.2/bin目录下的arm-linux-编译器。

       在117和165行中,用于包含config.mk文件,117行将make forlinx_nand_ram256_config命令生成的include/config.mk包含进来,即配置过程中制作出来的include/config.mk文件,其中定义了ARCH/CPU/BOARD/VENDOR/SOC的值分别为arm、s3c64xx、smdk6410、samsung、s3c6410。165行将 U-Boot 顶层目录下的 config.mk 文件包含进来,该文件包含了对编译的一些设置,它根据ARCH/CPU/BOARD/VENDOR/SOC变量的值确定了编译器、编译选项等。

顶层config.mk文件分析:     

	设置 obj 与 srcifneq ($(OBJTREE),$(SRCTREE))ifeq ($(CURDIR),$(SRCTREE))dir :=elsedir := $(subst $(SRCTREE)/,,$(CURDIR))endifobj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/)src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/)$(shell mkdir -p $(obj))elseobj :=src :=endif	由于目标输出到源代码目录下,因此执行完上面的代码后,src 和 obj 都是空。	设置编译选项PLATFORM_RELFLAGS =PLATFORM_CPPFLAGS =     #编译选项PLATFORM_	 =     			#连接选项用这 3 个变量表示交叉编译器的编译选项,在后面 Make 会检查交叉编译器支持的编译选项,然后将适当的选项添加到这3个变量中。	包含与开发板相关的配置文件(跳过54到74行的代码,这些代码是在NetBSD上使用交叉编译器时需要的定义)ifdef	 ARCHsinclude $(TOPDIR)/$(ARCH)_config.mk	# include architecture dependend rulesendif$(ARCH)的值是“arm”,因此将顶层目录下的“arm _ config.mk”包含进来,而顶层目录的arm _ config.mk文件中基本上什么都没有做,只有一行代码用来设置PLATFORM_CPPFLAGS编译选项(与arm处理器相关)。ifdef	CPUsinclude $(TOPDIR)/cpu/$(CPU)/config.mk		# include  CPU	specific rulesendif$(CPU)的值是“s3c64xx”,因此将“cpu/ s3c64xx /config.mk”包含进来。这个脚本主要设定了跟s3c64xx处理器相关的编译选项(与arm相关的PLATFORM_CPPFLAGS编译选项)。ifdef	SOCsinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk	# include  SoC	specific rulesendif$(SOC)的值是s3c6410,因此Make程序尝试将cpu/s3c64xx/s3c6410/config.mk包含进来,而这个文件并不存在,但是由于用的是“sinclude”命令,所以并不会报错。ifdef	 VENDORBOARDDIR = $(VENDOR)/$(BOARD)elseBOARDDIR = $(BOARD)endif$(BOARD)的值是smdk6410,VENDOR的值是 samsung,因此BOARDDIR的值是 samsung/ smdk6410。BOARDDIR 变量表示开发板特有的代码所在的目录。ifdef  BOARDsinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk	# include board specific rulesendif顶层目录下的config.mk文件将“board/samsung/smdk6410/config.mk”包含进来。该脚本内容如下:ifndef  TEXT_BASETEXT_BASE = 0xCFE00000endifU-Boot编译时将使用TEXT_BASE作为代码段连接的起始地址(这个地址是经过MMU映射过的)。	其他代码1  (95行---115行)CONFIG_SHELL	:= $(shell if [ -x "$$BASH" ]; then echo $$BASH; /		    else if [ -x /bin/bash ]; then echo /bin/bash; /		    else echo sh; fi ; fi)ifeq ($(HOSTOS)-$(HOSTARCH),darwin-ppc)HOSTCC		= ccelseHOSTCC		= gccendifHOSTCFLAGS	= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointerHOSTSTRIP	= stripcc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null /		> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)在上面最后两行代码中,变量CC和CFLAGS在后面的代码定义为延时变量,其中的 CC 即 arm-linux-gcc。函数cc-option 用于检查编译器 CC 是否支持某选项。将 2 个选项作为参数传递给 cc-option 函数,该函数调用 CC 编译器检查参数 1 是否支持,若支持则函数返回参数 1,否则返回参数 2 (因此CC 编译器必须支持参数 1 或参数 2,若两个都不支持则会编译出错)。可以像下面这样调用cc-option 函数,并将支持的选项添加到 FLAGS 中:FLAGS +=$(call cc-option,option1,option2)	指定交叉编译工具AS	= $(CROSS_COMPILE)asLD	= $(CROSS_COMPILE)ldCC	= $(CROSS_COMPILE)gccCPP	= $(CC) -EAR	= $(CROSS_COMPILE)arNM	= $(CROSS_COMPILE)nmSTRIP	= $(CROSS_COMPILE)stripOBJCOPY = $(CROSS_COMPILE)objcopyOBJDUMP = $(CROSS_COMPILE)objdumpRANLIB	= $(CROSS_COMPILE)RANLIB

        对于 arm 架构处理器,其中的 CROSS_COMPILE在顶层makefile文件中定义:

CROSS_COMPILE = /usr/local/arm/4.3.2/bin/arm-linux-161行左右)

因此以上代码指定了使用前缀为“arm-linux-”的编译工具,即arm-linux-gccarm-linux-ld 等等。

  • 其他代码130---217行)

        在这部分代码中,143(LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds)给出了LDSCRIPT的值为顶层目录下/board/Samsung/smdk6410/u-boot.lds(:=表示替换以前的值),而189行代码如下:

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

     所以,根据189行,使LDFLAGS中包含“-Bstatic  -T /board/Samsung/smdk6410/u-boot.lds ”和“-Ttext  0xCFE00000”的字样(+=表示添加)

  • 指定隐含的编译规则

        在最后部分代码中(218-246),指定隐含的编译规则,例如:根据以上的定义,以“.s”结尾的目标文件将根据第一条规则由同名但后缀为“.S”的源文件来生成,若不存在“.S”结尾的同名文件则根据最后一条规则由同名的“.c”文件生成。

上面就是顶层目录config.mk文件的内容,下面为顶层目录中Makefile剩下的内容。

166:#########################################################################167:# U-Boot objects....order is important (i.e. start must be first)168:169:OBJS  = cpu/$(CPU)/start.o170:ifeq ($(CPU),i386)171:OBJS += cpu/$(CPU)/start16.o172:OBJS += cpu/$(CPU)/reset.o173:endif174:ifeq ($(CPU),ppc4xx)175:OBJS += cpu/$(CPU)/resetvec.o176:endif177:ifeq ($(CPU),mpc83xx)178:OBJS += cpu/$(CPU)/resetvec.o179:endif180:ifeq ($(CPU),mpc85xx)181:OBJS += cpu/$(CPU)/resetvec.o182:endif183:ifeq ($(CPU),mpc86xx)184:OBJS += cpu/$(CPU)/resetvec.o185:endif186:ifeq ($(CPU),bf533)187:OBJS += cpu/$(CPU)/start1.o	cpu/$(CPU)/interrupt.o	cpu/$(CPU)/cache.o188:OBJS += cpu/$(CPU)/cplbhdlr.o	cpu/$(CPU)/cplbmgr.o	cpu/$(CPU)/flush.o189:endif190:191:OBJS := $(addprefix $(obj),$(OBJS))192:193:LIBS  = lib_generic/libgeneric.a194:LIBS += board/$(BOARDDIR)/lib$(BOARD).a195:LIBS += cpu/$(CPU)/lib$(CPU).a196:ifdef SOC197:LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a198:endif199:LIBS += lib_$(ARCH)/lib$(ARCH).a200:LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a /201:	fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a202:LIBS += net/libnet.a203:LIBS += disk/libdisk.a204:LIBS += rtc/librtc.a205:LIBS += dtt/libdtt.a206:LIBS += drivers/libdrivers.a207:LIBS += drivers/nand/libnand.a208:LIBS += drivers/nand_legacy/libnand_legacy.a209:LIBS += drivers/sk98lin/libsk98lin.a210:LIBS += post/libpost.a post/cpu/libcpu.a211:LIBS += common/libcommon.a212:LIBS += $(BOARDLIBS)213:214:LIBS := $(addprefix $(obj),$(LIBS))215:.PHONY : $(LIBS)216:217:# Add GCC lib218:PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc219:220:# The "tools" are needed early, so put this first221:# Don"t include stuff already done in $(LIBS)222:SUBDIRS	= tools /223:	  examples /224:	  post /225:	  post/cpu226:.PHONY : $(SUBDIRS)227:228:ifeq ($(CONFIG_NAND_U_BOOT),y)229:NAND_SPL = nand_spl230:U_BOOT_NAND = $(obj)u-boot-nand.bin231:endif232:233:__OBJS := $(subst $(obj),,$(OBJS))234:__LIBS := $(subst $(obj),,$(LIBS))235:236:#########################################################################237:#########################################################################238:239:ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)240:241:all:		$(ALL)242:243:$(obj)u-boot.hex:	$(obj)u-boot244:		$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@245:246:$(obj)u-boot.srec:	$(obj)u-boot247:		$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@248:249:$(obj)u-boot.bin:	$(obj)u-boot250:		$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@251:252:$(obj)u-boot.img:	$(obj)u-boot.bin253:		./tools/mkimage -A $(ARCH) -T firmware -C none /254:		-a $(TEXT_BASE) -e 0 /255:		-n $(shell sed -n -e "s/.*U_BOOT_VERSION//p" $(VERSION_FILE) | /256:			sed -e "s/"[	 ]*$$/ for $(BOARD) board"/") /257:		-d $< $@258:259:$(obj)u-boot.dis:	$(obj)u-boot260:		$(OBJDUMP) -d $< > $@261:262:$(obj)u-boot:		depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)263:	UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e "s/.*/(__u_boot_cmd_.*/)/-u/1/p"|sort|uniq`;/264:		cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) /265:			--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) /266:			-Map u-boot.map -o u-boot267:268:$(OBJS):269:		$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))270:271:$(LIBS):272:		$(MAKE) -C $(dir $(subst $(obj),,$@))273:274:$(SUBDIRS):275:		$(MAKE) -C $@ all276:277:$(NAND_SPL):	version278:		$(MAKE) -C nand_spl/board/$(BOARDDIR) all279:280:$(U_BOOT_NAND):	$(NAND_SPL) $(obj)u-boot.bin281:		cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin282:283:version:284:		@echo -n "#define U_BOOT_VERSION /"U-Boot " > $(VERSION_FILE); /285:		echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); /286:		echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion /287:			 $(TOPDIR)) >> $(VERSION_FILE); /288:		echo "/"" >> $(VERSION_FILE)289:290:gdbtools:291:		$(MAKE) -C tools/gdb all || exit 1292:293:updater:294:		$(MAKE) -C tools/updater all || exit 1295:296:env:297:		$(MAKE) -C tools/env all || exit 1298:299:depend dep:300:		for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done301:302:tags ctags:303:		ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include /304:				lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) /305:				fs/cramfs fs/fat fs/fdos fs/jffs2 /306:				net disk rtc dtt drivers drivers/sk98lin common /307:			/( -name CVS -prune /) -o /( -name "*.[ch]" -print /)`308:309:etags:310:		etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include /311:				lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) /312:				fs/cramfs fs/fat fs/fdos fs/jffs2 /313:				net disk rtc dtt drivers drivers/sk98lin common /314:			/( -name CVS -prune /) -o /( -name "*.[ch]" -print /)`315:316:$(obj)System.map:	$(obj)u-boot317:		@$(NM) $< | /318:		grep -v "/(compiled/)/|/(/.o$$/)/|/( [aUw] /)/|/(/./.ng$$/)/|/(LASH[RL]DI/)" | /319:		sort > $(obj)System.map320:321:#########################################################################322:else323:all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin /324:$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot /325:$(SUBDIRS) version gdbtools updater env depend /326:dep tags ctags etags $(obj)System.map:327:	@echo "System not configured - see README" >&2328:	@ exit 1329:endif330:331:.PHONY : CHANGELOG332:CHANGELOG:333:	git log --no-merges U-Boot-1_1_5.. | /334:	unexpand -a | sed -e "s//s/s*$$//" > $@335:336:#########################################################################337:338:unconfig:339:	@rm -f $(obj)include/config.h $(obj)include/config.mk /340:		$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp341:

        在上面代码中的169行,OBJS的第一个值为“cpu/$(CPU)/start.o”,即“cpu/s3c64xx/start.o”,根据上一行的注释,u-boot中OBJS(目标)非常重要,start.o必须放在第一个,即首先编译的start.s。在上面代码的170-191行为其他架构CPU的目标,在这里没什么用处。

注意:

       175-177行的代码在U-Boot1.1.6-for-OK6410源代码中没有,理论上有没有无影响。

        在代码193行到218行中,LIBS 变量指明了 U-Boot 需要的库文件,包括平台/开发板相关的目录、通用目录下相应的库,都通过相应的子目录编译得到的,在215行中,定义伪目标(.PHONY : $(LIBS)),意在当前目录下如果有LIBS变量对应的值(即U-Boot 需要的库文件,.a文件)时,执行make后不会显示“*.a is up-to-date”,而且会重新编译生成对应的.a文件。

注意:在208-209行之间,U-Boot1.1.6-for-OK6410源代码中增加了如下代码:

# add to support onenand. by scsuhLIBS += drivers/onenand/libonenand.aifeq ($(CPU),mpc83xx)LIBS += drivers/qe/qe.aendif	增加对onenand flash的支持。

        228到231行是些与平台无关的代码,因为对于某些开发板(包括OK6410),u-boot支持在nand flash启动,这些开发板配置文件中可能宏定义了CONFIG_NAND_U_BOOT,这样在239行依赖U_BOOT_NAND不会为空。239行代码即在执行make all后,将要生成u-boot.srec,u-boot.bin,System.map。其中u-boot.srec 是 Motorola S-Record format 文件,System.map 是 U-Boot 的符号表,u-boot.bin 是最终烧写到开发板的二进制可执行的文件。如果U_BOOT_NAND不为空,还将生成u-boot-nand.bin文件。

        OBJS、LIBS所代表的.o、.a文件就是U-boot的构成,它们通过268-272行命令,由相应的源文件(或相应子目录下的文件)编译得到。第268-269行规则表示,对于OBJS的每个成员,都将进入cpu/$(CPU)目录(即cpu/s3c64xx)编译它们,对于smdk6410开发板,OBJS为cpu/s3c64xx/start.o,它将由cpu/s3c64xx/start.S编译得到。第271-272行规则表示,对于LIBS中的每个成员,都将进入相应的子目录执行“make”命令。这些子目录的Makefile,结构相似,它们将Makefile中指定的文件编译、链接成一个库文件。

        当所有的OBJS、LIBS所表示的.o和.a文件都生成后,最后连接,这对应上面代码中的243到267行,先使用262-266行的规则链接得到ELF格式的U-BOOT,最后转换为二进制格式的u-boot.bin、S-Record格式的U-Boot.srec等等(即243-261行)。对于ELF格式的U-Boot的依赖,下面分别介绍:

  • 依赖目标depend

行为依赖目标depend的规则,对于300行中$(SUBDIRS),进入该目录执行“make _depend”,生成各个子目录的.depend 文件,.depend 列出每个目标文件的依赖文件。

  • 依赖 SUBDIRS

对于version依赖,即U-Boot的版本,这里不详细介绍。274-275行为依赖SUBDIRS的规则,SUBDIRS的值222-226有定义,所以将执行tools、examples、post、post/cpu目录下的makefile。

  • 依赖OBJS、LIBS

这两个依赖在上面已经说明。

  • 依赖LDSCRIPT

此依赖在顶层config.mk文件中有定义,LDSCRIPT的值为顶层目录下/board/Samsung/smdk6410/u-boot.lds。

        U-BOOT规则命令中,264-266行表示真正连接,LDFLAGS确定了连接方式,LDFLAGS里/board/Samsung/smdk6410/u-boot.lds ”和“-Ttext  0xCFE00000”的字样(根据顶层config.mk文件),这些字样指定了程序的布局、地址(但是连接起始地址为连接脚本中的偏移地址0+0xCFE00000)。所以,$(LDFLAGS)即使用u-boot.lds链接脚本及-Ttext  0xCFE00000。执行连接命令其实就是把 start.o 和各个子目录 makefile 生成的库文件按照 LDFLAGS 连接在一起,生成 ELF 文件 u-boot 和连接时内存分配图文件 u-boot.map。

注意:在上面代码中250到251行之间, U-Boot1.1.6-for-OK6410源代码中增加了一行代码:$(OBJDUMP) -d $< > $<.dis。目的是生成U-Boot的反汇编文件。

        顶层Makefile代码中341行以后的代码都是添加CPU架构相关的_config文件(整个 makefile 剩下的内容全部是各种不同的开发板的*_config:目标的定义),本开发板OK6410添加了如下(当然还有三星s3c24及64系列的其他处理器):

forlinx_nand_ram256_config :  unconfig

    @$(MKCONFIG) smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256

注意:在顶层目录makefile文件的末尾部分,是执行make clean后的规则,在U-Boot1.1.6-for-OK6410源代码中增加了二行代码:分别在2261行后加了:        

-o -name "*~" -o -name ".depend*" /

2263行后增加了:

 rm -f u-boot*

2)链接脚本分析(/board/Samsung/smdk6410/u-boot.lds

24:OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")25:/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/26:OUTPUT_ARCH(arm)   /* 指定输出平台为arm */27:ENTRY(_start)28:SECTIONS29:{30:	. = 0x00000000; 31:32:	. = ALIGN(4);  /* 代码以4字节对齐 */33:	.text      :    /* 指定代码段,.text的基地址由LDFLAGS中-Ttext $(TEXT_BASE)指定*/34:	{35:	  cpu/s3c64xx/start.o	(.text)  /* 代码段的第一个代码部份 */36:	  cpu/s3c64xx/s3c6410/cpu_init.o	(.text)37:	  cpu/s3c64xx/onenand_cp.o	(.text)38:	  cpu/s3c64xx/nand_cp.o	(.text)39:	  cpu/s3c64xx/movi.o (.text) 40:	  *(.text)  				/*其他代码部分*/41:	  lib_arm/div0.o42:	}43:44:	.           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121955.html

相关文章

  • 树莓派4B安装uboot

    摘要:要想放到树莓派上运行,需要官方提供的固件四下载直接下载我们所需的在文件夹中,将其中的复制到卡的分区中之前分区时大小为,格式为的那个,再将编译好的一同复制进去。 2021SC@SDUSC 一、uboot的介绍 1.1、计算机系统的主要部件 (1)计算机系统就是以CPU为核心来运行的系统。典型的...

    izhuhaodev 评论0 收藏0
  • Linux学习

    摘要:学习树莓派的的初识学习树莓派的的初识初识初识学习书籍正点原子嵌入式驱动开发指南章节第三十章学习内容书中介绍的获取可以有三个途径第一个是的官方代码。网上的烧写树莓派教程很多,但是为了学习我选择了官方的代码。Linux学习 - 树莓派4b的U-Boot的初识初识U-Boot学习书籍:《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.1》章节:第三十章学习内容:书中介绍uboot的获...

    不知名网友 评论0 收藏0
  • Linux内核移植入门

    摘要:表示,不是用压缩的。两者的不同之处在于,解压缩内核到低端内存第一个,解压缩内核到高端内存以上。如果内核比较小,那么采用或都行,如果比较大应该用。使得内核可以启动文件的拷贝或指向的链接。 ...

    tomorrowwu 评论0 收藏0
  • 摄像头逆向分析之旅

    摘要:前言摄像头是我们常见的设备,它的安全关系着用户的隐私,而厂商为了防止逆向分析,在程序的实现上会使用各种方法来阻止逆向工程。本文通过某款知名的摄像头的逆向分析,简单介绍了我是如何通过串口一步步的对摄像头中的关键程序进行逆向的方法。 前言 摄像头是我们常见的IoT设备,它的安全关系着用户的隐私,...

    RyanQ 评论0 收藏0
  • 基于STM32Cube MX开发TencentOS-Tiny软件包

    摘要:基于开发的软件包导师汪礼超学员崔林威摘要腾讯物联网操作系统是腾讯面向物联网领域开发的实时操作系统,具有低功耗,低资源占用,模块化,可裁剪等特性。图中断函数处理进行生成工程配置,按如下界面进行配置,最后点击,并点击。 ...

    shiyang6017 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<