logo资料库

u-boot顶层makefile、mkconfig、config.mk文件分析.pdf

第1页 / 共19页
第2页 / 共19页
第3页 / 共19页
第4页 / 共19页
第5页 / 共19页
第6页 / 共19页
第7页 / 共19页
第8页 / 共19页
资料共19页,剩余部分请下载后查看
U-Boot-1.1.6 顶层 Makefile 文件分析 (针对 OK6410 开发板) (顶层 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 = 1 PATCHLEVEL = 1 SUBLEVEL = 6 EXTRAVERSION = 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 sh SHELL := $(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) endif endif 函数$( 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 LNDIR := $(SRCTREE) := $(OBJTREE) export TOPDIR SRCTREE OBJTREE MKCONFIG := $(SRCTREE)/mkconfig export MKCONFIG ifneq ($(OBJTREE),$(SRCTREE)) REMOTE_BUILD := 1
export REMOTE_BUILD endif # $(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 在上面内容中,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 文件中,将进行如下几点的工作:  确定开发板名称 BOARD_NAME APPEND=no # no 表示创建新的配置文件,yes 表示追加到配置文件中 BOARD_NAME="" # Name to print in make output TARGETS="" 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 [ "${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)。  检查参数合法性 [ $# -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 命令后串口终端输出的信息。  创建到平台/开发板相关的头文件的符号连接 # 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 cd ./include rm -f asm ln -s asm-$2 asm # 符号连接,即软链接 else fi ln -s ${LNPREFIX}arch-$3 asm-$2/arch ln -s ${LNPREFIX}arch-$6 asm-$2/arch 第一行代码判断源代码目录和目标文件目录是否一样,可以选择在其他目录下编译 U-BOOT,这可令源代码保持干净,可以同时使用不同的配置进行编译。OK6410 的 U-BOOT 移 植过的源代码是在源代码目录下编译的,所以源代码目录等于目标文件目录,所以条件不满 足,将执行 else 分支的代码。 在 else 分支的代码中,先进入 include 目录,删除 asm 文件(这是上一次配置时建立的 链接文件),然后再次建立 asm 文件,并令它链接向 asm-$2 目录,即 asm-arm 目录。此举的 作用为:在源码中常调用头文件,如#include ,对不同架构需要修 改”asm-XXX 架构”,先建立 asm 到 asm-arm 的符号链接后,以后包含头文件时直接包含 #include 即包含#include 。 1:rm -f asm-$2/arch 2:if [ -z "$6" -o "$6" = "NULL" ] ; then 3: 4:else 5: 6:fi 7:if [ "$2" = "arm" ] ; then 8: 9: 10:fi 第 1 行删除 include/asm-arm/arch 目录,对于命令./mkconfig smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256,$6 为 S3C6410,不为空,也不为 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 时直接进入链接文件指向的目录进行编译,而不必根据不同开发板来选 择不同目录。 注意: ln -s ${LNPREFIX}proc-armv asm-$2/proc rm -f asm-$2/proc
在第 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 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 当执行“./mkconfig smdk6410 arm s3c64xx smdk6410 samsung s3c6410 NAND ram256” 命令后,上面几行代码创建的 include/config.mk 文件内容如下: ARCH = arm CPU = s3c64xx BOARD = smdk6410 VENDOR = samsung SOC = s3c6410  指定开发板代码所在目录
(可选,如果待移植的 U-Boot 源代码中已经有了那些目录,就不需要下面的代码,比如 三星官方提供的 U-Boot 源代码。) # Assign board directory to BOARDIR variable if [ -z "$5" -o "$5" = "NULL" ] ; then BOARDDIR=$4 else BOARDDIR=$5/$4 fi 以上代码指定 board 目录下的一个目录为当前开发板专有代码的目录。若$5(VENDOR) 为 空则 BOARDDIR 设置 为 $4( BOARD ),否则 设置为 $5/$4 ( VENDOR/BOARD , 即 samsung/smdk6410)。在这里由于$5 不为空,即 BOARDDIR 被设置为 samsung/smdk6410 。  创建开发板相关的头文件 include/config.h # Append to existing config file echo >> config.h 1:if [ "$APPEND" = "yes" ] 2:then 3: 4:else 5: 6:fi 7:echo "/* Automatically generated - do not edit */" >>config.h 8:echo "#include " >>config.h 9:exit 0 # Create new config file > config.h “>” 和 “>>”为 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 文件中就有以上的内容了。 注意: 在第 7 行到 8 行之间,增加如下部分代码 for OK6410: case $7 in SD) echo "#define FORLINX_BOOT_SD" >> config.h SETMMU="no" ;; NAND) echo "#define FORLINX_BOOT_NAND" >> config.h SETMMU="yes" *) ;; ;; esac case $8 in ram128)
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 *) ;; ;; esac if [ "$9" = "hdmi" ] ; then echo "#define FORLINX_LCDOUT_HDMI" >> config.h fi 对于 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 >
分享到:
收藏