目录
2.7、体系架构 ARCH、CPU、BOARD、VENDOR、SOC
2.8、autoconf.mk、autoconf.mk.dep
uboot 是什么不用多说了,是用于引导系统启动的 bootloader,配置完基本硬件后,把代码 Copy 到内存,引导内核启动;
为了支持多种不同的体系架构引导启动,以及各式各样的流程,uboot 源码中包含了几乎所有常见的 CPU 体系架构,包括 ARM,X86,MIPS,PowerPC;现在基于的这款 Exynos 4412 是 ARM Cortex-A9 Qual Core 的四核 CPU 环境来分析 uboot;
既然是彻底分析,那么就从编译部分开始吧;目录结构这些暂时就不多说了,网上到处都有说,其实,从编译的视角来分析目录结构,也会对目录结构理解得更加深刻和透彻;
那么我们从一个已经移植完毕的 u-boot 的编译开始分析,拿着放大镜看看他是怎么编译出来的;
首先还是看 uboot 目录下的 Makefile,这个是整个 uboot 编译的入口;
关于 Makefile 部分的语法和用法,参考 《GNU Makefile》一文;
1、编译方式
按照标准的方式,编译一个 u-boot,不是直接 make,而是需要先:
make xxx_config
然后再:
make all
可以参考 u-boot 目录下的 README 文件,搜索关键字:“Building the Software”,可以看到:
U-Boot is intended to be simple to build. After installing the sources you must configure U-Boot for one specific board type. This is done by typing:
make NAME_config
where "NAME_config" is the name of one of the existing configurations; see the main Makefile for supported names.
Note: for some board special configuration names may exist; check if additional information is available from the board vendor; for instance, the TQM823L systems are available without (standard) or with LCD support. You can select such additional "features"
when choosing the configuration, i. e.make TQM823L_config
- will configure for a plain TQM823L, i. e. no LCD supportmake TQM823L_LCD_config
- will configure for a TQM823L with U-Boot console on LCDetc.
Finally, type "make all", and you should get some working U-Boot images ready for download to / installation on your system:
- "u-boot.bin" is a raw binary image
- "u-boot" is an image in ELF binary format
- "u-boot.srec" is in Motorola S-Record format
很多关于 u-boot 的东西,都在这个 README 中有描述,我们可以好好参考一下这个文档,包括如何移植一个自己的 u-boot,以及 u-boot 中的一些 CONFIG 的含义,等等;
由于现在是在谈编译,所以暂时看这部分;
我拿到的这个 u-boot 下,有一个编译脚本 build_uboot.sh:
#!/bin/sh
if [ -z $1 ]
then
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "Please use correct make config.for example make SCP_1GDDR for SCP 1G DDR CoreBoard linux,android OS"
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
exit 0
fi
if [ "$1" = "SCP_1GDDR" ] || [ "$1" = "SCP_2GDDR" ] || [ "$1" = "SCP_1GDDR_Ubuntu" ] || [ "$1" = "SCP_2GDDR_Ubuntu" ]
then
sec_path="../CodeSign4SecureBoot_SCP/"
CoreBoard_type="SCP"
elif [ "$1" = "POP_1GDDR" ] || [ "$1" = "POP_1GDDR_Ubuntu" ]
then
sec_path="../CodeSign4SecureBoot_POP/"
CoreBoard_type="POP"
elif [ "$1" = "POP_2GDDR" ] || [ "$1" = "POP_2GDDR_Ubuntu" ]
then
sec_path="../CodeSign4SecureBoot_POP/"
CoreBoard_type="POP2G"
else
echo "make config error,please use correct params......"
exit 0
fi
CPU_JOB_NUM=$(grep processor /proc/cpuinfo | awk '{field=$NF};END{print field+1}')
ROOT_DIR=$(pwd)
CUR_DIR=${ROOT_DIR##*/}
#clean
make distclean
#rm link file
rm ${ROOT_DIR}/board/samsung/smdkc210/lowlevel_init.S
rm ${ROOT_DIR}/cpu/arm_cortexa9/s5pc210/cpu_init.S
case "$1" in
clean)
echo make clean
make mrproper
;;
*)
if [ ! -d $sec_path ]
then
echo "**********************************************"
echo "[ERR]please get the CodeSign4SecureBoot first"
echo "**********************************************"
return
fi
if [ "$1" = "SCP_1GDDR" ]
then
make itop_4412_android_config_scp_1GDDR
elif [ "$1" = "SCP_2GDDR" ]
then
make itop_4412_android_config_scp_2GDDR
elif [ "$1" = "POP_1GDDR" ]
then
make itop_4412_android_config_pop_1GDDR
elif [ "$1" = "POP_2GDDR" ]
then
make itop_4412_android_config_pop_2GDDR
elif [ "$1" = "SCP_1GDDR_Ubuntu" ]
then
make itop_4412_ubuntu_config_scp_1GDDR
elif [ "$1" = "SCP_2GDDR_Ubuntu" ]
then
make itop_4412_ubuntu_config_scp_2GDDR
elif [ "$1" = "POP_1GDDR_Ubuntu" ]
then
make itop_4412_ubuntu_config_pop_1GDDR
elif [ "$1" = "POP_2GDDR_Ubuntu" ]
then
make itop_4412_ubuntu_config_pop_2GDDR
fi
make -j$CPU_JOB_NUM
if [ ! -f checksum_bl2_14k.bin ]
then
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "There are some error(s) while building uboot, please use command make to check."
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
exit 0
fi
cp -rf checksum_bl2_14k.bin $sec_path
cp -rf u-boot.bin $sec_path
rm checksum_bl2_14k.bin
cd $sec_path
#./codesigner_v21 -v2.1 checksum_bl2_14k.bin BL2.bin.signed.4412 Exynos4412_V21.prv -STAGE2
# gernerate the uboot bin file support trust zone
#cat E4412.S.BL1.SSCR.EVT1.1.bin E4412.BL2.TZ.SSCR.EVT1.1.bin all00_padding.bin u-boot.bin E4412.TZ.SSCR.EVT1.1.bin > u-boot-iTOP-4412.bin
if [ "$CoreBoard_type" = "SCP" ]
then
cat E4412_N.bl1.SCP2G.bin bl2.bin all00_padding.bin u-boot.bin tzsw_SMDK4412_SCP_2GB.bin > u-boot-iTOP-4412.bin
elif [ "$CoreBoard_type" = "POP" ]
then
cat E4412.S.BL1.SSCR.EVT1.1.bin E4412.BL2.TZ.SSCR.EVT1.1.bin all00_padding.bin u-boot.bin E4412.TZ.SSCR.EVT1.1.bin > u-boot-iTOP-4412.bin
elif [ "$CoreBoard_type" = "POP2G" ]
then
cat bl2.bin u-boot.bin E4412.TZ.SSCR.EVT1.1.bin > u-boot-iTOP-4412.bin
else
echo "make uboot image error......"
fi
mv u-boot-iTOP-4412.bin $ROOT_DIR
rm checksum_bl2_14k.bin
#rm BL2.bin.signed.4412
rm u-boot.bin
echo
echo
;;
esac
编译的时候,输入
./build_uboot.sh SCP_1GDDR
略去一些其他和 u-boot 无关的因素后,其实关键的就是这两句:
make itop_4412_android_config_scp_1GDDR
make -j$CPU_JOB_NUM
-j$CPU_JOB_NUM 这个多线程编译的核心数目,通过前面 Shell 中找到 CPU 核心数,然后 -j;
后面这两行命令展开分析;
2、变量、文件分析
我们暂时不直接分析上面两个命令产生的后果,因为需要仔细的了解他们,首先需要看明白 Makefile 里面的一些变量,这样可以为后面仔细分析打下基础;
打开 u-boot 的 Makefile 逐行进行分析;
2.1、版本号 U_BOOT_VERSION
VERSION = 2010
PATCHLEVEL = 03
SUBLEVEL =
EXTRAVERSION =
ifneq "$(SUBLEVEL)" ""
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
else
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)
endif
定义了 u-boot 的版本号:
U_BOOT_VERSION 的值,由这几个的值拼接而来:
VERSION:主板本号
PATCHLEVEL:Patch 版本号
SUBLEVEL:子版本号
EXTRAVERSION:附加的版本信息
2.2、主机信息 HOSTARCH、HOSTOS
通过 Makefile 中使用 $(shell ) 函数,来执行 shell 命令,获取主机的 CPU 架构和主机操作系统,以及 Shell 环境:
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/')
# Set shell to bash if possible, otherwise fall back to sh
SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi; fi)
export HOSTARCH HOSTOS SHELL
使用 export 将这三个变量导出,使得这三个变量,在下一级的 Makefile 也生效;
2.3、静默编译 XECHO
通过获取 make 后面的参数,来查看是否启用静默模式编译,静默模式指的是编译的时候,不向控制台输出编译内容:
#########################################################################
# Allow for silent builds
ifeq (,$(findstring s,$(MAKEFLAGS)))
XECHO = echo
else
XECHO = :
endif
$(MAKEFIAGS) 代表了在敲入 make 的时候,带入的参数,比如 make -s;-s 的意思就是静默编译的意思;
这里如果没有使用了静默编译,那么 XECHO 就为 echo,即编译阶段,调用 $(XECHO)打印信息;
2.4、目标文件目录 BUILD_DIR
编译 u-boot 的时候,我们可以指定最终生成目标文件的目录,以避免目标文件污染当前目录,在 Makefile 中有:
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
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),)
首先使用 ifeq ("$(origin O)", "command line") 来判断是否在终端有通过如下方式,指定输出目标文件的目录,比如:
make O=/tmp/build all
这就代表了这个 O 存在了,而且来自于 command line(origin 这个函数,在 Makefile 中用于查找这个变量的来源,后面跟了 “command line”,意思是查看这个变量是否来源于命令行);
当然,也可以通过直接赋值 BUILD_DIR 的方式,来指定 build 的目录:
export BUILD_DIR=/tmp/build
make
u-boot 使用 O=xxxx,这个 O 的意思,就是 Output 的缩写而已,你也可以把它定义为 OUT,或者其他名字,都行,相应的,修改 Makefile 就行;
下面,我们假设在 make 的时候,没有指定目标文件输出的目录;
2.5、目录 TOPDIR、SRCTREE、OBJTREE
写一个 Makefile,一般来讲,都要定义一些类似 Top 目录,源码目录,头文件目录和中间文件目录,等等;
u-boot 也一样:
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
如果 BUILD_DIR 不为空,那么就以 BUILD_DIR 的值赋给 OBJTREE,否则使用 $(CURDIR) 赋值给 OBJTREE;
$(CURDIR) 是 Makefile 的默认变量,可以直接引用,指的是当前的目录;
假设,我们编译的时候,不指定 BUILD_DIR,那么:
$(OBJTREE):为当前目录,即,u-boot 的 Toplevel 目录;
$(SRCTREE):为当前目录,即,u-boot 的 Toplevel 目录;
$(TOPDIR):为当前目录,即,u-boot 的 Toplevel 目录;
使用 export 导出这三个参数;
2.5.1、src、obj 目录
在定义完 TOPDIR、SRCTREE、OBJTREE 后,立马定定义了 src 和 obj 这两个变量:
# $(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
可以看到,如果目标文件的目录和当前源码目录不一样的话,obj 变量的值就是 $(OBJTREE) 多了一个 “/”,src 也是 $(SRCTREE) 多了一个 “/”,也就是代表了下级目录而已;
如果目标文件的目录和当前源码目录一样,那 obj 和 src 为空;
2.6、生成配置的文件 MKCONFIG
继续往下读 Makefile,会发现如下
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
定义了一个变量 MKCONFIG,他指向了 u-boot 目录下的 mkconfig 文件,并使用 export 导出这个变量;
这破玩意,要记住他,并且好好的记住它:
mkconfig 他是用来生成另外的文件的;
我们先不看他的内容,稍后里面会看得到;
2.7、体系架构 ARCH、CPU、BOARD、VENDOR、SOC
继续看 Makefile:
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
# Include autoconf.mk before config.mk so that the config options are available
# to all top level build files. We need the dummy all: target to prevent the
# dependency target in autoconf.mk.dep from being the default.
all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
首先使用 ifeq 来判断 config.mk 这个文件是否存在,那么这个文件该不该存在呢?显然是应该存在的,那么看官可以看下,include 下面有没有这个 config.mk;
结论是:没有;
因为这个 include/config.mk,是生成的,怎么生成的呢?就是之前的第一条指令:
make itop_4412_android_config_scp_1GDDR
我们在 Makefile 中可以找到这个 itop_4412_android_config_scp_1GDDR 目标:
itop_4412_android_config_scp_1GDDR: unconfig
@$(MKCONFIG) $(@:_config=) arm arm_cortexa9 smdkc210 samsung s5pc210 SCP_1GDDR
这个目标依赖一个 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
好了,先来分析一下这个 itop_4412_android_config_scp_1GDDR 的规则;首先会 unconfig 去删除 include 下($(obj) 为空 )的诸多文件;
然后执行:
@$(MKCONFIG) $(@:_config=) arm arm_cortexa9 smdkc210 samsung s5pc210 SCP_1GDDR
之前我们知道,这个 $(MKCONFIG) 其实就等于源码目录下的 mkconfig;前面加一个@,代表这个过程,不打印出来;
$(@)的意思是目标文件集,而输入的文件集就是 itop_4412_android_config_scp_1GDDR,后面的 :_config= 指的是用目标的字符串 _config 之前的字符串代替它,并且后面的 = 意思是省略其后面的字段。
所以 $(@:_config=) 代表的是 itop_4412_android 。
所以,翻译过来就是:
./mkconfig itop_4412_android arm arm_cortexa9 smdkc210 samsung s5pc210 SCP_1GDDR
相当于执行 mkconfig,后面带了一串参数;
2.7.1、mkconfig
终于轮到打开这个文件了,先提前说好,这个文件是用来执行的,可以理解为一个 Shell 脚本;
#!/bin/sh -e
# Script to create header files and links to configure
# U-Boot for a specific board.
#
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
#
# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
#
APPEND=no # Default: Create new config file
#BOARD_NAME="" # Name to print in make output
TARGETS=""
echo "CoreBoard is $7...... "
if [ "$7" = "SCP_1GDDR" ] || [ "$7" = "SCP_2GDDR" ] || [ "$7" = "POP_1GDDR" ] || [ "$7" = "POP_2GDDR" ]
then
BOARD_NAME="itop_4412_android"
echo "CoreBoard OS is android or linux...... "
elif [ "$7" = "SCP_1GDDR_Ubuntu" ] || [ "$7" = "SCP_2GDDR_Ubuntu" ] || [ "$7" = "POP_1GDDR_Ubuntu" ] || [ "$7" = "POP_2GDDR_Ubuntu" ]
then
BOARD_NAME="itop_4412_ubuntu"
echo "CoreBoard OS is Ubuntu...... "
else
echo "unknown coreboard type and os type......"
fi
既然是 Shell 脚本,而且传入了很多参数,这里我们先把参数列举一下:
- $0:mkconfig
- $1:itop_4412_android
- $2:arm
- $3:arm_cortexa9
- $4:smdkc210
- $5:samsung
- $6:s5pc210
- $7:SCP_1GDDR
接着看这个 mkconfig:
while [ $# -gt 0 ] ; do
case "$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 ;;
esac
done
首先这个 while [ $# -gt 0 ] 判断如果入参的个数是否大于 0(不包含 $0),如果是,那么进入下面的 case;
判断入参的第一个,也就是 $1 的值是否为:
--
-a
-t
如果是,那么执行响应的操作,这里,我们的入参 $1 为 itop_4412_android,不是这些个玩意;
接下来为:
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
[ $# -lt 4 ] && exit 1
[ $# -gt 7 ] && exit 1
if [ "${ARCH}" -a "${ARCH}" != "$2" ]; then
echo "Failed: \$ARCH=${ARCH}, should be '$2' for ${BOARD_NAME}" 1>&2
exit 1
fi
echo "Configuring for ${BOARD_NAME} board..."
首先判断参数 BOARD_NAME 是否为空,为空的话则将输入参数$1赋值给 BOARD_NAME;
然后判断参数的个数,如果小于 4 或者大于 7,那么直接退出;
然后判断 ARCH 这个是否有被预先定义,如果被定义了,却不等于输入的 $2 那么报错;
接着看:
#
# 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
这部分是创建软链接的,先判断 SRCTREE 和 OBJTREE 是否相等,之前我们知道,在顶层的 Makefile 中,我们是已经定义了这两个玩意,结论是,如果不指定编译目录,那么这两个玩意是相等的;
所以我们只需要看后面的 else 那部分;进入 include 下面,删除 asm 目录,并将 asm-$2 软连接到 asm,由于我们的 $2 为 arm,所以呢,就是:
asm-arm -> asm
为啥这样做呢,为了做更好的抽象,所有的平台都这样做的话,在代码里面就可以访问一样的路径:#include<asm/xxx.h>
接着看:
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
if [ "$2" = "arm" ] ; then
rm -f asm-$2/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi
删除了 asm-arm/arch 目录;
判断 $6 是否为空(此刻为 s5pc210),显然不为空,所以进入 else 分支:
arch-s5pc210 链接到 asm-arch/arch
然后链接 asm-arm/proc:
继续看脚本:
#
# 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
# Assign board directory to BOARDIR variable
if [ -z "$5" -o "$5" = "NULL" ] ; then
BOARDDIR=$4
else
BOARDDIR=$5/$4
fi
使用 echo 将一些信息添加到 config.mk;
注意:include 下的这个 config.mk 本来是没有的,是通过这个 mkconfig 来生成的
这些信息包括了 ARCH、CPU、BOARD、VENDOR、SOC;这些就是对应在使用这个 mkconfig 时候的入参;所以生成后的 include/config.mk 是如下内容:
继续看脚本:
if [ "$7" = "SCP_1GDDR" ] || [ "$7" = "SCP_2GDDR" ] || [ "$7" = "SCP_1GDDR_Ubuntu" ] || [ "$7" = "SCP_2GDDR_Ubuntu" ]
then
echo "CORE = SCP" >> config.mk
ln -sf ${SRCTREE}/board/samsung/smdkc210/lowlevel_init_SCP.S ${SRCTREE}/board/samsung/smdkc210/lowlevel_init.S
ln -sf ${SRCTREE}/cpu/arm_cortexa9/s5pc210/cpu_init_SCP.S ${SRCTREE}/cpu/arm_cortexa9/s5pc210/cpu_init.S
elif [ "$7" = "POP_1GDDR" ] || [ "$7" = "POP_2GDDR" ] || [ "$7" = "POP_1GDDR_Ubuntu" ] || [ "$7" = "POP_2GDDR_Ubuntu" ]
then
echo "CORE = POP" >> config.mk
ln -sf ${SRCTREE}/board/samsung/smdkc210/lowlevel_init_POP.S ${SRCTREE}/board/samsung/smdkc210/lowlevel_init.S
ln -sf ${SRCTREE}/cpu/arm_cortexa9/s5pc210/cpu_init_POP.S ${SRCTREE}/cpu/arm_cortexa9/s5pc210/cpu_init.S
else
echo "make config error,please use correct params......"
exit 0
fi
这里根据入参,住了两个事情:
赋值了 CORE 这个变量到 config.mk
由于 SCP 和 POP 不一样,但是呢,最后代码里面编译的时候使用到的都是叫做:
lowlevel_init.S 和 cpu_init.S
所以,这里做了软链接,来加以区分;
接着看:
#
# 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
for i in ${TARGETS} ; do
echo "#define CONFIG_MK_${i} 1" >>config.h ;
done
#add by dg for all itop4412 type boards
[ "$7" ] && [ "$7" != "NULL" ] && echo "#define CONFIG_$7" >> config.h
cat << EOF >> config.h
#define CONFIG_BOARDDIR board/$BOARDDIR
#include <config_defaults.h>
#include <configs/$BOARD_NAME.h>
#include <asm/config.h>
EOF
exit 0
判断 APPEND 是否为 yes,这个意思就是是否往已经存在了的 config.h 里面追加东西;
我们的 u-boot 本身在 include 下面是没有这个 config.h 的,而且在这个 mkconfig 脚本中,第一行已经将这个 APPEND 设置为了 no;
这里就是直接创建了一个 include/config.h 文件,往这个文件里面增加了很多东西:
2.7.2、小结
这里简单的做一个小结,在 make itop_4412_android_config_scp_1GDDR 之后啊,会调用 mkconfig,去生成
include/config.mk
include/config.h
还有一些软链接被设置准备好;
2.8、autoconf.mk、autoconf.mk.dep
继续读 Makefile 会发现如下内容:
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk)) # Include autoconf.mk before config.mk so that the config options are available # to all top level build files. We need the dummy all: target to prevent the # dependency target in autoconf.mk.dep from being the default. all: sinclude $(obj)include/autoconf.mk.dep sinclude $(obj)include/autoconf.mk # load ARCH, BOARD, and CPU configuration include $(obj)include/config.mk export ARCH CPU BOARD VENDOR SOC
我们知道,include/config.mk 已经在我们 make 配置的时候,生成了;但是呢,这两行需要仔细看看:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
sinclude 和 -include 是一个意思,代表着如果包含文件失败,忽略错误;
但是实际情况是,这个 autoconf.mk.dep 和 autoconf.mk 的确在源码目录下没有,那么这个 sinclude 真的会失败吗?
其实不然,这个 autoconf.mk.dep 和 autoconf.mk 在 Makefile 中,也是一个目标,我们在 Makefile 中可以搜索这两个玩意,可以在后面发现如下:
#
# Auto-generate the autoconf.mk file (which is included by all makefiles)
#
# This target actually generates 2 files; autoconf.mk and autoconf.mk.dep.
# the dep file is only include in this top level makefile to determine when
# to regenerate the autoconf.mk file.
$(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 $(HOSTCFLAGS) $(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 > $@.tmp && \
mv $@.tmp $@
#########################################################################
我们看他的注释便知:
1、autoconf.mk 这玩意是生成的;
2、autoconf.mk 这玩意要被所有的 Makefile 包含;
autoconf.mk 依赖 include/config.h 文件(前一步生成的)
编译选项 “-dM” 的作用是输出include/common.h中定义的所有宏。根据上面的规则,编译器提取include/common.h中定义的宏,然后输出给tools/scripts/define2mk.sed脚本处理,处理的结果就是include/autoconf.mk文件。其中tools/scripts/define2mk.sed脚本的主要完成了在include/common.h中查找和处理以“CONFIG_”开头的宏定义的功能;
include/common.h 文件包含了 include/config.h文件;
而 include/config.h文件又包含了config_defaults.h,configs/itop_4412_android.h,asm/config.h文件。
因此include/autoconf.mk实质上就是 config_defaults.h、configs/itop_4412_android.h、asm/config.h 三个文件中“CONFIG_”开头的有效的宏定义的集合。
而 autoconf.mk.dep 是 autoconf.mk 的依赖,我们打开 autoconf.mk 看看:
autoconf.mk.dep 内容为:
此刻,我们回到之前 Makefile 包含 autoconf.mk 和 autoconf.mk.dep 的地方;
ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
# Include autoconf.mk before config.mk so that the config options are available
# to all top level build files. We need the dummy all: target to prevent the
# dependency target in autoconf.mk.dep from being the default.
all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
# load ARCH, BOARD, and CPU configuration
include $(obj)include/config.mk
export ARCH CPU BOARD VENDOR SOC
这里,autoconfig.mk、config.mk 都被 include 了
解释一下 Makefile 的 include,是将被 include 的文件内容直接全部原地展开;
所以各种 CONFIG_ 都被 include 进来了;并且把 ARCH、CPU 等都包含了进来,并且呢,export 了这些;
2.9、CROSS_COMPILE
CROSS_COMPILE 交叉编译器,这个必须要指定,没什么好说的:
ifeq ($(ARCH),arm)
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
endif
export CROSS_COMPILE
2.10、源码下的 config.mk
之前我们说了一下,执行 make xxx_config 会调用 mkconfig 去生成一个 include/config.mk 文件,我们读 Makefile 的时候,发现如下内容:
# load other configuration
include $(TOPDIR)/config.mk
#########################################################################
# U-Boot objects....order is important (i.e. start must be first)
Makefile 还包括了其源码目录下的 config.mk,虽然是同名,但是我们要把这个 config.mk 与 include/config.mk 区别开来,我们知道,Makefile 的 include 是会直接把后面被包含的内容之间原地展开的,所以这里相当于展开了源码目录下面的 config.mk,那么我们来看看这个里面又有什么:
ifneq ($(OBJTREE),$(SRCTREE))
ifeq ($(CURDIR),$(SRCTREE))
dir :=
else
dir := $(subst $(SRCTREE)/,,$(CURDIR))
endif
obj := $(if $(dir),$(OBJTREE)/$(dir)/,$(OBJTREE)/)
src := $(if $(dir),$(SRCTREE)/$(dir)/,$(SRCTREE)/)
$(shell mkdir -p $(obj))
else
obj :=
src :=
endif
首先判断 OBJTREE 和 SRCTREE 是否相等,这里,我们默认让他们相等,所以会走到最下面那个 else,即 obj 和 src 为空;
注意:这个 obj 和 src 是和顶层 Makefile 一样的那个 obj 和 src,这里使用 ":=" 的赋值方式,可以覆盖之前的对 obj 和 src 的值;
接着看:
# clean the slate ...
PLATFORM_RELFLAGS =
PLATFORM_CPPFLAGS =
PLATFORM_LDFLAGS =
清除了这几个变量;
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
其他的内容,有兴趣的可以仔细看看;
2.11、OBJS
接着看 Makefile,接着定义了一些 .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)/resetvec.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
OBJS := $(addprefix $(obj),$(OBJS))
这里还 专门由一个说明:
U-Boot objects....order is important (i.e. start must be first)
最先放在前面的是 cpu/arm_cortexa9/start.o,我们的 CPU 是 arm,所以那些 ifeq 都不满足,直接到最后一行,这个 addprefix 是加前缀的意思,obj 为空,所以就当没有;
2.12、LIBS
再看 Makefile,定义了这个 LIBS
LIBS = lib_generic/libgeneric.a
LIBS += lib_generic/lzma/liblzma.a
LIBS += lib_generic/lzo/liblzo.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 fs/yaffs2/libyaffs2.a \
fs/ubifs/libubifs.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/fpga/libfpga.a
LIBS += drivers/gpio/libgpio.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/onenand/libonenand.a
LIBS += drivers/mtd/ubi/libubi.a
LIBS += drivers/mtd/spi/libspi_flash.a
LIBS += drivers/net/libnet.a
LIBS += drivers/net/phy/libphy.a
LIBS += drivers/pci/libpci.a
LIBS += drivers/pcmcia/libpcmcia.a
LIBS += drivers/power/libpower.a
LIBS += drivers/spi/libspi.a
ifeq ($(CPU),mpc83xx)
LIBS += drivers/qe/qe.a
endif
ifeq ($(CPU),mpc85xx)
LIBS += drivers/qe/qe.a
LIBS += cpu/mpc8xxx/ddr/libddr.a
LIBS += cpu/mpc8xxx/lib8xxx.a
endif
ifeq ($(CPU),mpc86xx)
LIBS += cpu/mpc8xxx/ddr/libddr.a
LIBS += cpu/mpc8xxx/lib8xxx.a
endif
LIBS += drivers/rtc/librtc.a
LIBS += drivers/serial/libserial.a
LIBS += drivers/twserial/libtws.a
LIBS += drivers/usb/gadget/libusb_gadget.a
LIBS += drivers/usb/host/libusb_host.a
LIBS += drivers/usb/musb/libusb_musb.a
LIBS += drivers/usb/phy/libusb_phy.a
LIBS += drivers/video/libvideo.a
LIBS += drivers/watchdog/libwatchdog.a
LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a
LIBS += api/libapi.a
LIBS += post/libpost.a
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS) $(TIMESTAMP_FILE) $(VERSION_FILE)
这些个 .a 静态库文件,是由各个子目录进行编译出来的,目前暂时还没有被编译出来,要进入到各个子目录去使用 Makefile 进行编译,我们打个比方:
第一个
LIBS = lib_generic/libgeneric.a
我们进入到 lib_generic,打开它的 Makefile:
include $(TOPDIR)/config.mk
LIB = $(obj)libgeneric.a
COBJS-$(CONFIG_ADDR_MAP) += addr_map.o
COBJS-$(CONFIG_BZIP2) += bzlib.o
COBJS-$(CONFIG_BZIP2) += bzlib_crctable.o
COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o
COBJS-$(CONFIG_BZIP2) += bzlib_randtable.o
COBJS-$(CONFIG_BZIP2) += bzlib_huffman.o
COBJS-$(CONFIG_USB_TTY) += circbuf.o
COBJS-y += crc16.o
COBJS-y += crc32.o
COBJS-y += ctype.o
COBJS-y += display_options.o
COBJS-y += div64.o
COBJS-$(CONFIG_GZIP) += gunzip.o
COBJS-$(CONFIG_LMB) += lmb.o
COBJS-y += ldiv.o
COBJS-$(CONFIG_MD5) += md5.o
COBJS-y += net_utils.o
COBJS-$(CONFIG_SHA1) += sha1.o
COBJS-$(CONFIG_SHA256) += sha256.o
COBJS-y += string.o
COBJS-y += strmhz.o
COBJS-y += time.o
COBJS-y += vsprintf.o
COBJS-$(CONFIG_ZLIB) += zlib.o
COBJS-$(CONFIG_RBTREE) += rbtree.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
$(LIB): $(obj).depend $(OBJS)
$(AR) $(ARFLAGS) $@ $(OBJS)
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
首先,它包含了顶层的 config.mk 文件,定义了这个 Makefile 的目标文件:
LIB = $(obj)libgeneric.a
然后定义了一堆的:
COBJS-$(CONFIG_XXX) += xxx.o
这种东西,在我们的配置中,如果使用了某个配置,我们会往这个配置写 y,也就是:
CONFIG_XXX = y
所以,目标依赖的都是一个叫做 $(COBJS-y) 的东西;
这个 $(AR) 是被定义在 config.mk 中的 AR:
AR = $(CROSS_COMPILE)ar
这个 $(ARFLAGS) 也是config.mk 中的:
ifneq (,$(findstring s,$(MAKEFLAGS)))
ARFLAGS = cr
else
ARFLAGS = crv
endif
这样就用 ar 这个工具,以 .o 作为输入,生成了 .a 了;其他的 .a 类似,不再多说;
3、make all
前面看的差不多了,现在来看最后的 make all 了;
# Always append ALL so that arch config.mk's can add custom ones
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
@#./mkuboot
@split -b 14336 u-boot.bin bl2
@+make -C sdfuse_q/
@#cp u-boot.bin u-boot-4212.bin
@#cp u-boot.bin u-boot-4412.bin
@#./sdfuse_q/add_sign
@./sdfuse_q/chksum
@./sdfuse_q/add_padding
@rm bl2a*
@echo
$(obj)u-boot.ldr: $(obj)u-boot
$(CREATE_LDR_ENV)
$(LDR) -T $(CONFIG_BFIN_CPU) -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.imx: $(obj)u-boot.bin
$(obj)tools/mkimage -n $(IMX_CONFIG) -T imximage \
-e $(TEXT_BASE) -d $< $@
$(obj)u-boot.kwb: $(obj)u-boot.bin
$(obj)tools/mkimage -n $(KWD_CONFIG) -T kwbimage \
-a $(TEXT_BASE) -e $(TEXT_BASE) -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 $< > $@
可以看到,最后我们 make all 的时候,依赖生成 3 个目标:
- u-boot.srec
- u-boot.bin
- System.map
第一个 u-boot.srec 从 u-boot 的 README 中看到,是一个说明摩托罗拉的格式,不管他;
第二个 u-boot.bin 是纯二进制文件,也就是烧写到板端的文件;
第三个是 map 文件;
可以发现,要生成 u-boot.srec 和 u-boot.bin,都依赖一个叫 u-boot 的文件,使用 objcopy 来得到,这个 u-boot 是 elf 文件;
GEN_UBOOT = \
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
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y)
smap=`$(call SYSTEM_MAP,u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
-c common/system_map.c -o $(obj)common/system_map.o
$(GEN_UBOOT) $(obj)common/system_map.o
endif
可以看到,u-boot 这个终极大 boss,依赖很多东西,在生成这个目标之前,需要先把 u-boot 的依赖全部生成(各种 .o 和 .a),最后,GEN_UBOOT 这个就是用于生成 u-boot 的命令了,将所有的文件(.o 和 .a)全部打包,生成 elf 格式的 u-boot;
u-boot 的依赖生成被定义到了紧接着的下面:
$(OBJS): depend
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
$(LIBS): depend $(SUBDIRS)
$(MAKE) -C $(dir $(subst $(obj),,$@))
$(LIBBOARD): depend $(LIBS)
$(MAKE) -C $(dir $(subst $(obj),,$@))
$(SUBDIRS): depend
$(MAKE) -C $@ all
$(LDSCRIPT): depend
$(MAKE) -C $(dir $@) $(notdir $@)
..............
..............
其中,$(MAKE) 其实就是 make,make -C 的意思是进入到文件夹下面去执行子 Makefile,说白了,就是去编译那些子文件夹;
当然,最好分析的方法,可以将 Makefile 和编译过程中的打印相结合,这样的话,会更加直观;