weiqi7777

cortex-a8 uboot系列: 第四章 uboot的配置与编译过程

0
阅读(4179)

一、  ubootMakefile分析

1.     版本号

使用几个变量来说明版本号。最终的版本号是变量U_BOOT_VERSION。版本号分为3个级别。

VERSION:主版本号

PATHCHLEVEL 补丁版本号

SUBLEVEL 子版本号

Makelfile最终生成U_BOOT_VERSION这个变量,这个变量记录了Makefile中配置的版本号。

VERSION = 1

PATCHLEVEL = 3

SUBLEVEL = 4

EXTRAVERSION = weiqi7777

U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)

VERSION_FILE = $(obj)include/version_autogenerated.h

 

include/version_autogenerated.h文件是编译过程中自动生成的一个文件,在编译后的uboot中就有了。里面的内容是一个宏定义,宏定义的值就是在Makefile中配置的uboot的版本号。

clip_image001[4]

2.     HOSTARCHHOSTOS

设置两个环境变量。使用export导出。

HOSTARCH:主机的架构体系。先使用命令uname –m得到主机的架构体系,使用管道操作,对得到的结果进行替换。如在虚拟机中,使用命令uname –m 得到i686,使用sed –e s/i.86/i386 i686替换成i386

HOSTOS:主机的操作系统。使用命令uname –s得到主机操作系统Linux,使用tr命令,将Linux转换为小写linux,在使用sed命令,将(cygwin)xxx替换成cygwin

关于cygwin是以前windows下仿linux环境软件,现在有了虚拟机,都不使用了。

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/ppc64/ppc/ \

        -e s/macppc/ppc/)

HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \

        sed -e 's/\(cygwin\).*/cygwin/')

export  HOSTARCH HOSTOS

3.     静默编译

默认编译时命令行会打印出来很多编译信息。但是有时候不想要看到这些编译信息,就后台编译即可。就需要静默编译。

Uboot编译如果需要静默编译,加-s选项,即make –s-s会作为MAKEFLAGS传给Makefileif判断不满足,XECHO就等于空。

# Allow for silent builds

ifeq (,$(findstring s,$(MAKEFLAGS)))

XECHO = echo

else

XECHO = :

endif

4.     设置编译方法

Makefile提供两种编译管理方法。默认情况下文件夹中的.c文件,编译出来的.o会和.c在同一个目录下,这叫原地编译。原地编译的好处是处理简单,但是坏处是污染了原目录,另外一套代码只能按照一种配置和编译方法进行处理,无法同时生成2个以上的配置编译方式。

为了解决以上两种缺陷,uboot支持单独输出文件夹方式编译(linux kernel也支持)。基本思路是编译时另外制定一个输出目录,将来所有的编译生成的.o文件和其他文件都放在这个输出目录里,源代码目录不生成任何文件,对源代码目录就不污染了。这样输出目录就承载了本次配置编译的所有结果。

使用的用法:

第一种:在make时,添加O= make的编译选项

make  O=/tmp/build  all

第二部:添加环境变量,BUILD_DIR

export  BUILD_DIR=/tmp/build

make

如果上述两种都使用,第一种的优先级高。如果都没有使用,那么就认为是原地编译。

# U-boot build supports producing a object files to the separate external

# directory. Two use cases are supported:

# 1) Add O= to the make command line

# 'make O=/tmp/build all'

# 2) Set environement variable BUILD_DIR to point to the desired location

# 'export BUILD_DIR=/tmp/build'

# 'make'

# The second approach can also be used with a MAKEALL script

# 'export BUILD_DIR=/tmp/build'

# './MAKEALL'

# Command line 'O=' setting overrides BUILD_DIR environent variable.

# When none of the above methods is used the local build is performed and

# the object files are placed in the source directory.

ifdef O

ifeq ("$(origin O)", "command line")

BUILD_DIR := $(O)

endif

endif

判断是否定义了O,当使用make O=xxx时,此时就定义了O,下面判断O的来源是不是来自命令行,是的话,就定义一个变量BUILD_DIR,值为$(O),也就是xxx

ifneq ($(BUILD_DIR),)

saved-output := $(BUILD_DIR)

# Attempt to create a output directory.

$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${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),)

判断变量BUILD_DIR是否定义,如果在之前使用O参数,就会定义这个变量,ifneq就会为真,执行下面的语句。如果之前未使用O参数,BUILD_DIR变量就不会定义,ifneq判断为假,后续的语句全部就跳过了,也就是使用本地编译。

定义saved-output变量,和BUILD_DIR的值一样,指示保存的输出的目录。然后使用shell命令判断输出的目录是否存在,不存在的话,就强制创建目录。然后调用shell命令进入到该目录中,使用pwd命令,获取目录的绝对路径。判断该路径是否为空,为空说明输出目录不正确,输出错误信息。

$(error output directory "$(saved-output)" does not exist) 表示产生错误,并输出信息output directory "$(saved-output)" does not exist,此时Makefile停止工作。

makefile中打印警告或者错误消息的方法:$(warning xxxxx)或者$(error xxxxx) ,但是对于$(error xxxxx)makefile会退出。

通过上面的程序,BUILD_DIR保存的是目录的绝对路径,saved-output保存的是目录的名字(也就是O传参指定的)。

5.     定义文件树目录

OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))

SRCTREE := $(CURDIR)

TOPDIR      := $(SRCTREE)

LNDIR       := $(OBJTREE)

export  TOPDIR SRCTREE OBJTREE

MKCONFIG    := $(SRCTREE)/mkconfig

export MKCONFIG

ifneq ($(OBJTREE),$(SRCTREE))

REMOTE_BUILD    := 1

export REMOTE_BUILD

endif

定义几个变量。

OBJTREE  编译uboot生成的文件放置的根目录

SRCTREE  源代码的目录,其实就是当前目录,因为是在当前目录下编译的。$(CURDIR)是一个makefile自带的环境变量,值是pwd的结果。

默认编译下,OBJTREESRCTREE是相等的;在O=XX这种编译下两个不相等,以实现单独输出文件夹方式编译。

 

TOPDIR:     源程序的顶层目录

MKCONFIG 配置脚本的目录位置,他的值就是源码根目录下的mkconfig文件,这个mkconfig是一个脚本,也就是uboot配置的脚本。以后uboot会调用这个脚本,生成配置文件。

后面比较生成目标的目录的源代码目录是否一致,一致的话,说明是本地编译,REMOTE_BUILD为空,否则说明是单独输出文件夹方式编译,REMOTE_BUILD1

# $(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)/

else

obj :=

src :=

endif

export obj src

得到objsrc两个变量。这两个变量在后面指示某些要使用的文件的位置。obj就是生成文件的根目录,src就是uboot源代码的根目录

6.     导入配置文件

uboot编译的时候,首先要make 配置文件,这里是make x210_sd_config 执行该命令,会调用以下的命令。执行$(MKCONFIG)程序,传递6个参数,第一个参数将目标x210_sd_configconfig替换成空,也就是第一个参数为x210_sd

对于$(@:_config=)@表示目标,也就是x210_sd_config_config=表示将x210_sd_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

脚本执行后,就会在$(obj)include目录下生成一个config.mk的文件,这个文件里面记录了下图所示几个参数。也会在$(obj)board/samsung/x210/config.mk文件中写入TEXT_BASE = 0xc3e00000(链接器脚本使用的链接地址)

clip_image002[4]

在主Makefile中将上面这个配置文件包含进来,得到几个变量的值。并导出。上面的ARCH参数,影响交叉编译器。

ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))

# load ARCH, BOARD, and CPU configuration

include $(obj)include/config.mk

export  ARCH CPU BOARD VENDOR SOC

7.     设置交叉编译器

判断ARCH(从include/config.mk文件中得来)是什么,从而确定使用什么交叉编译器对代码进行编译。

ifndef CROSS_COMPILE

ifeq ($(HOSTARCH),$(ARCH))

CROSS_COMPILE =

else

ifeq ($(ARCH),ppc)

CROSS_COMPILE = ppc_8xx-

endif

ifeq ($(ARCH),arm)

#CROSS_COMPILE = arm-linux-

#CROSS_COMPILE = /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux-

#CROSS_COMPILE = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux-

CROSS_COMPILE = /usr/local/arm/bin/arm-none-linux-gnueabi-

endif

export         CROSS_COMPILE

Makefile中,判断了很多种架构的CPU,使用在上面定义的ARCH变量的值。对于我们使用arm的,只用关心arm架构下的交叉编译器。

这里只需要定义交叉编译器的前缀就行了。

8.     添加额外的配置文件config.mk

# load other configuration

include $(TOPDIR)/config.mk

           uboot根目录下的config.mk文件包含进来,这个是一个脚本文件。

           在这个文件中,定义了若干的变量:

 

#

# 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

设置了工具链变量。以后uboot编译会用到这些变量来进行编译。

 

# 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

根据主Makefile导出的参数(ARCH,CPU,SOC,VENDOR,BOARD),决定引入一些配置文件。sinclude相当于-include,包含文件的时候,文件不存在,makefile不理会。

 

对于第一个autoconf.mk,不是由源码提供,配置过程自动生成,也就是make配置后会生成这个文件。这个文件的作用指导整个uboot的编译过程。文件的内容就是很多CONFIG_开头的宏(和linux.config文件类似),这些宏影响uboot编译过程的走向(原理就是条件编译)。在uboot中有很多地方代码使用条件编译编写,条件编译是用来实现可移植性的。

CONFIG_CMD_FAT=y

CONFIG_USB_OHCI=y

CONFIG_SYS_CLK_FREQ=24000000

CONFIG_CMD_ITEST=y

CONFIG_S3C_HSMMC=y

CONFIG_DISPLAY_BOARDINFO=y

CONFIG_CMD_XIMG=y

CONFIG_CMD_CACHE=y

CONFIG_STACKSIZE="0x40000"

CONFIG_BOOTDELAY=3

CONFIG_CHECK_MPLL_LOCK=y

CONFIG_NR_DRAM_BANKS=2

CONFIG_ETHADDR="00:40:5c:26:0a:5b"

CONFIG_CMD_CONSOLE=y

这个文件不是凭空产生,配置过程需要原材料来产生这个文件。原材料在源码目录的include/configs/xxx.h头文件。(x210开发板中为include/configs/x210_sd.h)。这个头文件里面全部都是宏定义,这些宏定义就是对当前开发板的移植。每一个开发板的移植都对应这个目录下的一个头文件,这个头文件每一个宏定义都很重要,这些配置的宏定义就是移植uboot的关键所在。

    其他包含的文件,都是提前写好的,用来指导编译。

判断是否启动方式是否从NAND,是的话,使用nand的链接脚本。否则使用其他链接脚本。

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

make x210_sd_config后,会在board/samsung/x210下产生config.mk文件。这个文件会在之前的sinclude中包含进来。

clip_image003[4]

TEXT_BASE是将来整个uboot链接时指定的虚拟链接起始地址。Uboot中启用了MMU,因此这个0xc3e00000就等于物理地址0x23e00000(也可能是0x33e00000)。

 

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

ifneq ($(TEXT_BASE),)

LDFLAGS += -Ttext $(TEXT_BASE)

endif

链接的时候,使用了 LDFLAGS参数,这个参数包含了–Tu-boot.lds  –Ttext TEXT_BASE。使用u-boot.lds作为链接脚本,但是u-boot.lds中的起始地址是0x0,所以使用–Ttext TEXT_BASE,将链接起始地址进行偏移。

自动推导,对.S的代码,编译成.s的,对.s的代码,编译成.o的,对.c的代码,也编译成.o的。

ifndef REMOTE_BUILD

%.s:    %.S

    $(CPP) $(AFLAGS) -o $@ $<

%.o:    %.S

    $(CC) $(AFLAGS) -c -o $@ $<

%.o:    %.c

    $(CC) $(CFLAGS) -c -o $@ $<

else

$(obj)%.s:  %.S

    $(CPP) $(AFLAGS) -o $@ $<

$(obj)%.o:  %.S

    $(CC) $(AFLAGS) -c -o $@ $<

$(obj)%.o:  %.c

    $(CC) $(CFLAGS) -c -o $@ $<

endif

9.     设置起始OBJS

设置uboot的起始段的目标。对于使用的s5pc11xOBJS=cpu/s5pc11x/start.o。这个start.o中的代码,就是uboot最开始执行的代码。对于其他架构,还需要包括其他的.o代码。

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

# U-Boot objects....order is important (i.e. start must be first)

OBJS  = cpu/$(CPU)/start.o

ifeq ($(CPU),i386)

OBJS += cpu/$(CPU)/start16.o

OBJS += cpu/$(CPU)/reset.o

endif

OBJS := $(addprefix $(obj),$(OBJS))

$(addprefix )给变量添加前缀,这里是往$(OBJS)变量前加上$(obj)的值。

10. 设置LIBS

设置库文件。

LIBS  = lib_generic/libgeneric.a

LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \

    "board/$(VENDOR)/common/lib$(VENDOR).a"; fi)

LIBS += cpu/$(CPU)/lib$(CPU).a

ifdef SOC

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.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

ifeq ($(CPU),mpc83xx)

LIBS += drivers/qe/qe.a

endif

ifeq ($(CPU),mpc85xx)

LIBS += drivers/qe/qe.a

endif

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))

 

# Add GCC lib

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

 

           这些库文件,以后会被其他代码所调用。

11. 判断是否从nandinand启动

判断启动方式,得到不同的输出bin

ifeq ($(CONFIG_NAND_U_BOOT),y)

NAND_SPL = nand_spl

U_BOOT_NAND = $(obj)u-boot-nand.bin

endif

 

ifeq ($(CONFIG_ONENAND_U_BOOT),y)

ONENAND_IPL = onenand_bl1

U_BOOT_ONENAND = $(obj)u-boot-onenand.bin

endif

    对于九鼎的x210开发板,有两种启动方式,一种是nand启动,一种是inand启动,也就是SD启动。

12. 得到新的变量

__OBJS := $(subst $(obj),,$(OBJS))

__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))

    这两个变量,在编译的时候会被用到。

13. 得到目标

ALL就是总目标,包含若干个文件。

ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND) $(obj)u-boot.dis

ifeq ($(ARCH),blackfin)

ALL += $(obj)u-boot.ldr

endif

    最终uboot编译后,就会生成这些目标文件。其中u-boot.bin就是生成的烧录到开发板的uboot文件。

14. make目标

uboot根目录下make其实就是make all

目标all,依赖$(ALL)$(ALL)是由多个文件组成,所以就一次性生成了多个文件。

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.ldr:   $(obj)u-boot

        $(LDR) -T $(CONFIG_BFIN_CPU) -f -c $@ $< $(LDR_FLAGS)

$(obj)u-boot.ldr.hex:   $(obj)u-boot.ldr

        $(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@ -I binary

$(obj)u-boot.ldr.srec:  $(obj)u-boot.ldr

        $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@ -I binary

$(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 $< $@

$(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)

        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

首先是生成uboot的可执行程序,然后该uboot转换为其他格式的文件。

目标里面有一些是重要的:

u-boot是最终编译链接生成的ubootelf格式的可执行文件。

u-boot.hex:  elf转化的hex文件

u-boot.dis:   uboot的反汇编文件

u-boot.bin 是最终烧录的二进制文件。

15. make 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

 

 

如对于以下,

x210_sd_config :    unconfig

    @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110

    @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

首先会先去执行make unconfig,将之前生成的配置文件都给删除掉。然后再执行后面的两行命令。

二、  uboot的配置

Makefile中,配置的命令如下:

x210_sd_config :    unconfig

    @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110

    @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

 

调用uboot根目录下的mkconfig,然后传递6个参数。

第一个参数 $1$(@:_config=), @表示x210_sd_config:_config=表示将@_config替换为空。所以第一个参数就是x210_sd

第二个参数 $2arm      ARCH

第三个参数 $3s5pc11x   CPU

第四个参数 $4x210       (BOARD) 

第五个参数 $5samsung    (VENDOR)

第六个参数 $6s5pc110     (SOC)

所以$# = 6

对于mkconfig,首先判断参数个数

APPEND=no   # Default: Create new config file

BOARD_NAME=""   # Name to print in make output

while [ $# -gt 0 ] ; do

    case "$1" in

    --) shift ; break ;;

    -a) shift ; APPEND=yes ;;

    -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;

    *)  break ;;

    esac

done

$#6,所以进入while循环,进行case判断,因为$1x210_sd,符合*,所以break跳出while循环。这里相当于while循环什么都没有做。

 

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

简略的if语句,判断${BOARD_NAME}是否为空,因为之前定义为空,所以执行后面一句,BOARD_NAME="$1",所以BOARD_NAME=x210_sd

 

[ $# -lt 4 ] && exit 1

[ $# -gt 6 ] && exit 1

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

输入参数个数如果小于4或者大于6,就直接退出(exit 1,也就是mkconfig脚本返回1)。否则向下执行,打印信息。

 

# Create link to architecture specific headers

if [ "$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 asm

else

    cd ./include

    rm -f asm

    ln -s asm-$2 asm

fi

 

rm -f asm-$2/arch

 

if [ -z "$6" -o "$6" = "NULL" ] ; then

    ln -s ${LNPREFIX}arch-$3 asm-$2/arch

else

    ln -s ${LNPREFIX}arch-$6 asm-$2/arch

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

33行到118行,都在创建一些符号链接。这些符号链接文件的存在就是整个配置过程的核心,这些符号链接文件(文件夹)的主要作用是给头文件包含等过程提供指向性连接。根本目的是让uboot具有可移植性。

Uboot可移植性的实现原理:uboot中有很多彼此平行的代码,各自属于各自不同的架构/CPU/开发板,在具体的一个开发板的编译时用符号连接的方式提供一个具体的名字的文件夹供编译时用。这样就可以在配置的过程中通过不同的配置时用不同的文件,就可以正确的包含正确的文件。

 

创建的符号链接:在本地编译条件下。

第一个:在include目录下创建asm符号链接,指向asm-$2也就是asm-arm

    cd ./include

    rm -f asm

    ln -s asm-$2 asm

 

rm -f asm-$2/arch

删除asm-arm下的arch文件。因为之后要创建这个文件。

第二个:在include/asm-arm下创建一个arch文件,指向include/asm-arm/arch-s5pc110

if [ -z "$6" -o "$6" = "NULL" ] ; then

    ln -s ${LNPREFIX}arch-$3 asm-$2/arch

else

    ln -s ${LNPREFIX}arch-$6 asm-$2/arch

fi

条件判断,$6为空或者$6NULL,条件真,而$6s5pc110,不为空,所以条件为假,执行第二个符号链接。创建了一个新的链接,asm-arm/arch,指向arch-s5pc110

这里${LNPREFIX}为空。此时是在include目录下,所以创建的符号链接是在include目录下。

 

第三个:

# 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

46行,使用cd ./include,所以当前目录为include,删除这个目录下的regs.h,然后创建一个符号链接regs.h,指向$6.h,也就是s5pc110.h。这个s5pc110.h中,定义了各个外设寄存器的结构体。

 

第四个:

删除asm-arm下的arch文件夹,也就是把刚刚创建的第二个符号链接给删除掉,然后创建一个符号链接asm-arm/arch,指向arch-s5pc11x

 

第五个:

if [ "$2" = "arm" ] ; then

    rm -f asm-$2/proc

    ln -s ${LNPREFIX}proc-armv asm-$2/proc

fi

  $2arm,条件符合,删除掉asm-arm下的proc。然后创建一个proc文件,指向include/asm/proc-armv

 

  总结:一共创建了4个符号链接(第二个被删除掉了)。这4个符号链接将来在写代码过程中,头文件包含是非常有用。比如,一个头文件包含可能是:#include<asm/xx.h>,这个时候就会在asm指向的符号链接地方去找xx.h

 

  创建include/config.mk参数文件。

# 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

往配置文件中写内容,主要是写入几个变量。因为这个时候目录是在include目录下,所以创建的文件config.mk也就是在include目录下。

主要是写入几个变量:

clip_image004[4]

创建这个文件,是为了让主Makefile来包含这个文件,得到几个参数的值。

 

Uboot的配置和编译过程的配合。

编译的时候需要ARCH=arm,CPU=s5pc11x等这些变量来指导编译,配置的时候就是为编译阶段提供这些变量。

但是为什么Makefile中不直接定义这些变量,而要在mkconfig脚本中创建config.mk文件然后又在Makefile中引入这个文件获取这些变量?这样的做法是为了以后好维护,好移植。

# 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

文件内容如下,这个文件时自动生成的。生成的文件,是在include目录下。

clip_image006[4]

  默认情况下,创建该文件,如果是make –a,就追加内容到该文件(前提是该文件已存在,但是该文件默认不存在)。功能就是包含一个头文件,configs/x210_sd.h,这个头文件是移植x210开发板时,对开发板的宏定义配置文件。这个文件时移植x210时最主要的文件。

  x210_sd.h文件会被用来生成一个autoconfig.mk文件,这个文件会被主Makefile引入,指导整个编译过程。里面的这些宏定义会影响对uboot中大部分.c文件中一些条件编译的选择。

 

exit 0

最后返回0,表示执行shell脚本成功。

   

    Uboot的整个配置过程,很多文件是有关联的(有时候这个文件是在另外一个文件中创建出来的,有时候这个文件被另外一个文件包含进去,有时候这个文件是由另外一个文件的内容生成决定的)。

  Uboot中配置和编译过程,所有的文件或者全局变量都是字符串形式的。意味着uboot的配置过程都是字符串匹配的,所以要注意大小写,不要写错。不然出错了很难排查。

三、  uboot的链接脚本

链接脚本是在config.mk中有定义:

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

 

位置是在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)

 

OUTPUT_ARCHarm),表示执行程序的架构是arm

ENTRY_start)用来指定整个程序的入口地址。所谓入口地址就是整个程序的开头地址,可以认为就是整个程序的第一句指令。

 

SECTIONS

{

    . = 0x00000000;

指定程序的链接起始地址有2种方法,一种是在链接脚本SECTION的开头使用. = 地址来指定,一种是在链接中ldflags使用-Ttext 地址来指定。后面一种的优先级高。

UbootMakefile使用第二种方法,使用TEXT_BASE指定链接的起始地址。因为最终uboot的链接地址就是TEXT_BASE

 

. = 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)

    }

指定text段的程序分布。首先起始地址要4字节对齐。然后按照链接脚本中规定的程序的顺序将text段放入到最终目标的text段中。第一个是start.o,然后是cpu_init.o…,这个段的顺序非常重要,如果弄错了,那么uboot基本是运行不起来的。

 

. = ALIGN(4);

    .rodata : { *(.rodata) }

    . = ALIGN(4);

    .data : { *(.data) }

    . = ALIGN(4);

    .got : { *(.got) }

    __u_boot_cmd_start = .;

    .u_boot_cmd : { *(.u_boot_cmd) }

    __u_boot_cmd_end = .;

    . = ALIGN(4);

    .mmudata : { *(.mmudata) }

    . = ALIGN(4);

    __bss_start = .;

    .bss : { *(.bss) }

    _end = .;

后面就是指定其他段。这里可以使用自定义段。比如uboot .u_boot_cmd就是自定义段,这段和uboot的命令有关的。

四、  u-boot.map文件解析

生成uboot这个可执行程序时,会生成u-boot.map文件,这个文件,就定义了可执行程序中,程序,标号,全局变量的地址分配。

1.    代码段(.text

    text段的起始地址是0xc3e0_0000,这个就是uboot的链接地址。这个地址放的是start.o的代码段,这个和链接脚本中指定的一致。

.text           0xc3e00000    0x2a1dc

 cpu/s5pc11x/start.o(.text)

 .text          0xc3e00000      0x6c0 cpu/s5pc11x/start.o

                0xc3e00010                _start

                0xc3e00050                _end_vect

                0xc3e00058                _armboot_start

                0xc3e0005c                _bss_start

                0xc3e00060                _bss_end

                0xc3e001fc                copy_from_nand

                0xc3e00248                theLastJump

                0xc3e00540                arm_cache_flush

                0xc3e00560                v7_flush_dcache_all

                0xc3e00600                disable_l2cache

                0xc3e00620                enable_l2cache

                0xc3e00640                set_l2cache_auxctrl

                0xc3e00660                set_l2cache_auxctrl_cycle

    在这个start.o中,有若干个标号,如对于标号_start,地址从0xc3e0_0010开始,到0xc3e0_004c。然后接着是_end_vect标号。查看反汇编,可以看到_start标号,就是从0xc3e0_0010开始的。

Disassembly of section .text:

 

c3e00000 <_start-0x10>:

c3e00000:   00002000    .word   0x00002000

    ...

 

c3e00010 <_start>:

c3e00010:   ea000013    b   c3e00064 <reset>

c3e00014:   e59ff014    ldr pc, [pc, #20]   ; c3e00030 <_undefined_instruction>

c3e00018:   e59ff014    ldr pc, [pc, #20]   ; c3e00034 <_software_interrupt>

c3e0001c:   e59ff014    ldr pc, [pc, #20]   ; c3e00038 <_prefetch_abort>

c3e00020:   e59ff014    ldr pc, [pc, #20]   ; c3e0003c <_data_abort>

c3e00024:   e59ff014    ldr pc, [pc, #20]   ; c3e00040 <_not_used>

c3e00028:   e59ff014    ldr pc, [pc, #20]   ; c3e00044 <_irq>

c3e0002c:   e59ff014    ldr pc, [pc, #20]   ; c3e00048 <_fiq>

 

    start.o代码段之后,就是cpu_inti.o的代码段。从0xc3e0_06c0地址开始。同样,也可以看各个标号的起始地址。

cpu/s5pc11x/s5pc110/cpu_init.o(.text)

 .text          0xc3e006c0      0x410 cpu/s5pc11x/s5pc110/cpu_init.o

                0xc3e006c0                mem_ctrl_asm_init

                0xc3e00a38                cleanDCache

                0xc3e00a44                cleanFlushDCache

                0xc3e00a50                cleanFlushCache

2.    只读段(.rodata

只读段的起始地址从0xc3e2_a1dcinterrupts.o的只读段是只读段的第一个,接着是board.o的只读段。

.rodata         0xc3e2a1dc    0x22b34

 *(.rodata)

 .rodata        0xc3e2a1dc       0x80 cpu/s5pc11x/libs5pc11x.a(interrupts.o)

 .rodata        0xc3e2a25c       0x40 lib_arm/libarm.a(board.o)

                0xc3e2a25c                version_string

 .rodata        0xc3e2a29c        0x7 net/libnet.a(net.o)

 *fill*         0xc3e2a2a3        0x1 00

 .rodata        0xc3e2a2a4       0xc4 common/libcommon.a(cmd_bootm.o)

 .rodata        0xc3e2a368       0x58 common/libcommon.a(cmd_mem.o)

 .rodata        0xc3e2a3c0       0x20 common/libcommon.a(xyzModem.o)

 .rodata        0xc3e2a3e0      0x200 common/libcommon.a(crc16.o)

 .rodata        0xc3e2a5e0      0x400 lib_generic/libgeneric.a(crc32.o)

 .rodata        0xc3e2a9e0       0x90 lib_generic/libgeneric.a(zlib.o)

 .rodata        0xc3e2aa70       0xac cpu/s5pc11x/libs5pc11x.a(usbd-otg-hs.o)

                0xc3e2aa70                config_full

                0xc3e2aa7c                config_full_total

                0xc3e2aa9c                config_high

                0xc3e2aaa8                config_high_total

                0xc3e2aac8                qualifier_desc

                0xc3e2aad4                string_desc0

对于usbd-otg-hs.o的只读段,还显示了只读段的变量,以及各个变量的起始地址。

3.    数据段(.data

    data段的起始地址从0xc3e53e38开始。对于board.o之前的代码,没有用到data段,所以地址均没有变化,都是0xc3e53e38

.data           0xc3e53e38     0x4d30

 *(.data)

 .data          0xc3e53e38        0x0 cpu/s5pc11x/start.o

 .data          0xc3e53e38        0x0 cpu/s5pc11x/s5pc110/cpu_init.o

 .data          0xc3e53e38        0x0 board/samsung/x210/lowlevel_init.o

 .data          0xc3e53e38        0x0 cpu/s5pc11x/onenand_cp.o

 .data          0xc3e53e38        0x0 cpu/s5pc11x/nand_cp.o

 .data          0xc3e53e38        0x0 cpu/s5pc11x/movi.o

 .data          0xc3e53e38        0x0 common/secure_boot.o

 .data          0xc3e53e38        0x0 common/ace_sha1.o

 .data          0xc3e53e38        0x0 cpu/s5pc11x/pmic.o

 .data          0xc3e53e38        0x0 cpu/s5pc11x/libs5pc11x.a(interrupts.o)

 .data          0xc3e53e38        0x0 cpu/s5pc11x/libs5pc11x.a(cpu.o)

 .data          0xc3e53e38        0x0 cpu/s5pc11x/libs5pc11x.a(setup_hsmmc.o)

 .data          0xc3e53e38        0x0 cpu/s5pc11x/libs5pc11x.a(serial.o)

 .data          0xc3e53e38        0x0 cpu/s5pc11x/s5pc110/libs5pc110.a(speed.o)

 .data          0xc3e53e38        0x0 lib_arm/libarm.a(_divsi3.o)

 .data          0xc3e53e38        0x0 lib_arm/libarm.a(_udivsi3.o)

 .data          0xc3e53e38       0x38 lib_arm/libarm.a(board.o)

                0xc3e53e38                init_sequence

                0xc3e53e6c                check_flash_flag

    board.o中,有定义两个全局变量,init_sequencecheck_flash_flag,因此这两个变量是要放到data段中的。变量init_sequence的起始地址是0xc3e5_3e38check_flash_flag的起始地址是0xc3e5_3e6c

4.    uboot命令段(.u_goot_cmd

uboot命令段,用来存放uboot的命令。命令段的起始地址是0xc3e58b68。存放的第一个命令是weiqi,然后是bdinfo,然后是go….

.u_boot_cmd     0xc3e58b68      0x660

 *(.u_boot_cmd)

 .u_boot_cmd    0xc3e58b68       0x18 common/libcommon.a(cmd_weiqi.o)

                0xc3e58b68                __u_boot_cmd_weiqi

 .u_boot_cmd    0xc3e58b80       0x18 common/libcommon.a(cmd_autoscript.o)

                0xc3e58b80                __u_boot_cmd_autoscr

 .u_boot_cmd    0xc3e58b98       0x18 common/libcommon.a(cmd_bdinfo.o)

                0xc3e58b98                __u_boot_cmd_bdinfo

 .u_boot_cmd    0xc3e58bb0       0x48 common/libcommon.a(cmd_boot.o)

                0xc3e58bb0                __u_boot_cmd_go

                0xc3e58bc8                __u_boot_cmd_reset

                0xc3e58be0                __u_boot_cmd_re

5.    bss

bss段的起始地址是0xc3e6_0000interrupt.o之前的代码,没有用到bss段,所以bss段的地址没有变化。

bss            0xc3e60000    0x44e18

 *(.bss)

 .bss           0xc3e60000        0x0 cpu/s5pc11x/start.o

 .bss           0xc3e60000        0x0 cpu/s5pc11x/s5pc110/cpu_init.o

 .bss           0xc3e60000        0x0 board/samsung/x210/lowlevel_init.o

 .bss           0xc3e60000        0x0 cpu/s5pc11x/onenand_cp.o

 .bss           0xc3e60000        0x0 cpu/s5pc11x/nand_cp.o

 .bss           0xc3e60000        0x0 cpu/s5pc11x/movi.o

 .bss           0xc3e60000        0x0 common/secure_boot.o

 .bss           0xc3e60000        0x0 common/ace_sha1.o

 .bss           0xc3e60000        0x0 cpu/s5pc11x/pmic.o

 .bss           0xc3e60000        0xc cpu/s5pc11x/libs5pc11x.a(interrupts.o)

                0xc3e60008                timer_load_val

     interrupt.o有用到bss段,这个bss段用于timer_load_val,从地址可以看出,这个变量占据了8个字节的大小。

 

     当然这个map文件中,还有其他的一些段,分析方法是一致的。分析这个map文件,可以加深对程序生成的相关知识,如段,标号等。