我们在编译U-Boot之前,需要根据当前使用的板子进行配置,例如make s5p_goni_config,接着才能进行编译make。下面首先分析配置阶段U-Boot做了哪些事情。
由于执行这些命令是在源码根目录下,所以需要到主Makefile中找一下s5p_goni_config这个目标,搜索结果显示没有匹配的信息,后来发现其实U-Boot设计者已经将配置选项设计成通用模式了:
%_config:: unconfig
@$(MKCONFIG) -A $(@:_config=)
上面的写法比较通用,因为U-Boot支持很多板卡,不同使用者会使用不同的配置,如make mini2440_config; make smdk2410_config; make zynq_config等等,假如每个板卡都在Makefiile中占用一个目标,那么整个Makefile显得很冗余,不如用脚本处理下。
回到上面的配置目标和依赖描述,显然s5p_goni_config是一个伪目标,依赖是unconfig,不知道为什么这里目标和依赖之间是两个冒号,小纠结。。。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
伪目标下面的命令总是要执行的:删除include/config.h include/config.mk include/autoconf.mk include/autoconf.mk.dep,以及各个板卡的临时配置信息文件config.tmp。因此,每次我们执行make s5p_goni_config之类的配置命令时,都会先清除之前的配置。接下来呢?接下就要执行@$(MKCONFIG) -A $(@:_config=)了,$(MKCONFIG) 是在主Makefile前面部分定义的变量:
MKCONFIG := $(SRCTREE)/mkconfig
mkconfig是源码根目录下的一个脚本文件,$(@:_config=)是makefile中变量替换的一种方式,表示把目标$@变量的_config部分替换为空,效果上就是删除_config后缀,因此我们就得到了下面的信息:
s5p_goni_config:: unconfig
mkconfig -A s5p_goni
接下来,我们的任务是分析mkconfig脚本,下面分析主要流程。
首先定义以下变量,概括地说,mkconfig脚本的主要功能就是根据用户传入的参数,获取以下各变量的值,然后再自动产生一些头文件。
1 APPEND=no 2 BOARD_NAME="" 3 TARGETS="" 4 5 6 arch="" 7 cpu="" 8 board="" 9 vendor="" 10 soc="" 11 options=""
下面是关键的一步。
1 if [ ( $# -eq 2 ) -a ( "$1" = "-A" ) ] ; then 2 # Automatic mode 3 line=`egrep -i "^[[:space:]]*${2}[[:space:]]" boards.cfg` || { 4 echo "make: *** No rule to make target \`$2_config'. Stop." >&2 5 exit 1 6 } 7 8 set ${line} 9 10 [ $# = 3 ] && set ${line} ${1} 11 elif [ "${MAKEFLAGS+set}${MAKELEVEL+set}" = "setset" ] ; then 12 # only warn when using a config target in the Makefile 13 cat <<-EOF 14 15 warning: Please migrate to boards.cfg. Failure to do so will 16 mean removal of your board in the next release. 17 18 EOF 19 sleep 5 20 fi
显然我们的参数个数和内容是符合分支判断要求的,因此执行那条文本数据处理命令,目的是在boards.cfg文件中根据s5p_goni匹配一行信息,这条信息放在line变量中,如果返回值为空字符串那么就报错退出,提示没有用户所说的这块板子的相关配置。那么boards.cfg文件中是什么内容呢?它是目前U-Boot支持板卡的信息汇总,通过这个文件,我们可以知道U-Boot支持哪些架构、哪些处理器、哪些SoC、哪些board,是一个很好的参考,它的每一个entry都是有格式要求的:
Target ARCH CPU Board name Vendor SoC Option
s5p_goni arm armv7 goni samsung s5pc1xx
zynq arm armv7 zynq xilinx zynq
mini2440 arm arm920t mini2440 friendlyARM s3c24x0
上面的格式很简单,不多说,如果将来想支持自己的板卡,那么加入自己的选项就好了。比如
tiny210 arm armv7 tiny210 frirndlyARM s5pc1xx
回到脚本分析,set ${line}这条语句不简单,我由于不知道这句话的含义,纠结了很久,后来做实验才明白这句话的作用:改变当前脚本的参数$# $1 $2 $3...,这些参数的内容完全由{line}决定。此时它的内容就是s5p_goni那一行,那么
$# = 6
$1 = s5p_goni $2 = arm $3 = armv7 $4 = goni $5 = samsung $6 = s5pc1xx
1 while [ $# -gt 0 ] ; do 2 case "$1" in 3 --) shift ; break ;; 4 -a) shift ; APPEND=yes ;; 5 -n) shift ; BOARD_NAME="${1%_config}" ; shift ;; 6 -t) shift ; TARGETS="`echo $1 | sed 's:_: :g'` ${TARGETS}" ; shift ;; 7 *) break ;; 8 esac 9 done 10 11 [ $# -lt 4 ] && exit 1 12 [ $# -gt 7 ] && exit 1 13 14 # Strip all options and/or _config suffixes 15 CONFIG_NAME="${1%_config}" 16 17 [ "${BOARD_NAME}" ] || BOARD_NAME="${1%_config}" 18 19 arch="$2" 20 cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $1}'` 21 spl_cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $2}'` 22 if [ "$4" = "-" ] ; then 23 board=${BOARD_NAME} 24 else 25 board="$4" 26 fi 27 [ $# -gt 4 ] && [ "$5" != "-" ] && vendor="$5" 28 [ $# -gt 5 ] && [ "$6" != "-" ] && soc="$6" 29 [ $# -gt 6 ] && [ "$7" != "-" ] && { 30 tmp="${7%:*}" 31 if [ "$tmp" ] ; then 32 CONFIG_NAME="$tmp" 33 fi 34 # Check if we only have a colon... 35 if [ "${tmp}" != "$7" ] ; then 36 options=${7#*:} 37 TARGETS="`echo ${options} | sed 's:,: :g'` ${TARGETS}" 38 fi 39 } 40 41 if [ "${ARCH}" -a "${ARCH}" != "${arch}" ]; then 42 echo "Failed: $ARCH=${ARCH}, should be '${arch}' for ${BOARD_NAME}" 1>&2 43 exit 1 44 fi 45 46 if [ "$options" ] ; then 47 echo "Configuring for ${BOARD_NAME} - Board: ${CONFIG_NAME}, Options: ${options}" 48 else 49 echo "Configuring for ${BOARD_NAME} board..." 50 fi
上面代码的while循环对于当前这个板卡是没有作用的,且明显参数个数是满足继续运行的要求的,接下来就开始赋值了
CONFIG_NAME = s5p_goni
BOARD_NAME = s5p_goni
arch = arm
cpu = armv7
spl_cpu =
board = goni
vendor = samsung
soc = s5pc1xx
这里我们没有第七个参数,因此相关的option置空,那么配置的时候屏幕会输出一句“Configuring for s5p_goni board...”。
重要的变量都得到了,下面开始第二项重要工作,建立相关软链接和头文件。
1 if [ "$SRCTREE" != "$OBJTREE" ] ; then 2 mkdir -p ${OBJTREE}/include 3 mkdir -p ${OBJTREE}/include2 4 cd ${OBJTREE}/include2 5 rm -f asm 6 ln -s ${SRCTREE}/arch/${arch}/include/asm asm 7 LNPREFIX=${SRCTREE}/arch/${arch}/include/asm/ 8 cd ../include 9 mkdir -p asm 10 else 11 cd ./include 12 rm -f asm 13 ln -s ../arch/${arch}/include/asm asm 14 fi
上面SRCTREE与OBJTREE是相同的,代表所有源码都在本地,再本地编译链接,得到目标,因此执行else分支:再当前shell运行的子进程中进入include文件夹下,删除其下面的asm(asm是一个软链接文件),接着为arch/arm/include/asm文件夹建立软链接asm。
1 rm -f asm/arch 2 3 if [ -z "${soc}" ] ; then 4 ln -s ${LNPREFIX}arch-${cpu} asm/arch 5 else 6 ln -s ${LNPREFIX}arch-${soc} asm/arch 7 fi 8 9 if [ "${arch}" = "arm" ] ; then 10 rm -f asm/proc 11 ln -s ${LNPREFIX}proc-armv asm/proc 12 fi
首先删除asm/arch,注意!此处的asm已经是软链接了,所以删除的是arch/arm/include/asm/arch(其实arch也是软链接文件)。soc变量长度非零,因此为arch/arm/include/asm/arch-s5pc1xx 建立软链接asm/arch。其实上面两段代码的作用就是建立asm和arch两个软链接,以后在C语言包含头文件时,头文件的路径写起来就比较方便。
1 ( echo "ARCH = ${arch}" 2 if [ ! -z "$spl_cpu" ] ; then 3 echo 'ifeq ($(CONFIG_SPL_BUILD),y)' 4 echo "CPU = ${spl_cpu}" 5 echo "else" 6 echo "CPU = ${cpu}" 7 echo "endif" 8 else 9 echo "CPU = ${cpu}" 10 fi 11 echo "BOARD = ${board}" 12 13 [ "${vendor}" ] && echo "VENDOR = ${vendor}" 14 [ "${soc}" ] && echo "SOC = ${soc}" 15 exit 0 ) > config.mk 16 17 # Assign board directory to BOARDIR variable 18 if [ -z "${vendor}" ] ; then 19 BOARDDIR=${board} 20 else 21 BOARDDIR=${vendor}/${board} 22 fi
向include/config.mk中写入一些信息:
ARCH = arm
CPU = armv7
BOARD = goni
VENDOR = samsung
SOC = s5pc1xx
打开include/config.mk发现其内容和上述一致。上面最后一句BOARDDIR=samsung/goni,其实这就提示我们,在boards.cfg文件中添加信息后,还要在board目录下建立vendor/board目录,两者都要有,mkdir -p vendor/board。
1 if [ "$APPEND" = "yes" ] # Append to existing config file 2 then 3 echo >> config.h 4 else 5 > config.h # Create new config file 6 fi 7 echo "/* Automatically generated - do not edit */" >>config.h 8 9 for i in ${TARGETS} ; do 10 i="`echo ${i} | sed '/=/ {s/=/ /;q; } ; { s/$/ 1/; }'`" 11 echo "#define CONFIG_${i}" >>config.h ; 12 done 13 14 cat << EOF >> config.h 15 #define CONFIG_BOARDDIR board/$BOARDDIR 16 #include <config_cmd_defaults.h> 17 #include <config_defaults.h> 18 #include <configs/${CONFIG_NAME}.h> 19 #include <asm/config.h> 20 EOF 21 22 exit 0
我们这块板子并没有append option,所以直接建立config.h文件,建立的方式比较简单:> config.h,第一次见到这种建立空白文件的方式,新技能get!接着在该文件的头部添加一句提示语。最后,向config.h写入一些信息,这里只是包含一些其他头文件:
#define CONFIG_BOARDDIR board/samsung/goni
#include <config_cmd_defaults.h>
#define <config_defaults.h>
#define <configs/s5p_goni.h> //这就对应当前板子的头文件,应该放在include/configs目录下。
#include <asm/config.h>
注意脚本中向文件写内容方式:
cat << EOF >> filename
line1
line2
....
EOF