OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4144|回复: 0

【正点原子Linux连载】第三十一章U-Boot顶层Makefile详解--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

[复制链接]

1118

主题

1129

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4671
金钱
4671
注册时间
2019-5-8
在线时间
1224 小时
发表于 2019-12-24 17:15:14 | 显示全部楼层 |阅读模式
本帖最后由 正点原子01 于 2019-12-25 15:24 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址::https://item.taobao.com/item.htm?id=603672744434
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
4)对正点原子Linux感兴趣的同学可以加群讨论:935446741
5)关注正点原子公众号,获取最新资料更新
QQ图片20191129104007.png
第三十一章U-Boot顶层Makefile详解


       上一章我们详细的讲解了uboot的使用方法,其实就是各种命令的使用,学会uboot使用以后就可以尝试移植uboot到自己的开发板上了,但是在移植之前需要我们得先分析一遍uboot的启动流程源码,得捋一下uboot的启动流程,否则移植的时候都不知道该修改那些文件。本章我们就来分析一下正点原子提供的uboot源码,重点是分析uboot启动流程,而不是整个uboot源码,uboot整个源码非常大,我们只看跟我们关心的部分即可。


31.1 U-Boot工程目录分析
       本书我们以EMMC版本的核心板为例讲解,为了方便,uboot启动源码分析就在Windows下进行,将正点原子提供的uboot源码进行解压,解压完成以后的目录如图31.1.1所示:
image002.png
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif
31.1.1未编译的uboot
       31.1.1是正点原子提供的未编译的uboot源码目录,我们在分析uboot源码之前一定要先在Ubuntu中编译一下uboot源码,因为编译过程会生成一些文件,而生成的这些恰恰是分析uboot源码不可或缺的文件。使用上一章创建的shell脚本来完成编译工作,命令如下:
  1. cd alientek_uboot                                              //进入正点原子uboot源码目录
  2. ./mx6ull_alientek_emmc.sh                                //编译uboot
  3. cd../                                                                 //返回上一级目录
  4. tar -vcjf alientek_uboot.tar.bz2 alientek_uboot      //压缩
复制代码

最终会生成一个名为alientek_uboot.tar.bz2的压缩包,将alientek_uboot.tar.bz2拷贝到windows系统中并解压,解压后的目录如图31.1.2所示:
image004.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image004.gif
31.1.2编译后的uboot源码文件
       对比图31.1.2和图31.1.1,可以看出编译后的uboot要比没编译之前多了好多文件,这些文件夹或文件的含义见表31.1.1所示:
807A6341-3D3A-4e9e-86CF-8CCC050337A0.png
表31.1.1uboot目录列表
       31.1.1中的很多文件夹和文件我们都不需要去关心,我们要关注的文件夹或文件如下:
       1、arch文件夹
       这个文件夹里面存放着和架构有关的文件,如图31.1.3所示:
image006.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg
图31.1.3arch文件夹
       从图31.1.3可以看出有很多架构,比如armavr32m68k等,我们现在用的是ARM芯片,所以只需要关心arm文件夹即可,打开arm文件夹里面内容如图31.1.4所示:
image008.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg
图31.1.4arm文件夹
       31.1.4只截取了一部分,还有一部分mach-xxx的文件夹。mach开头的文件夹是跟具体的设备有关的,比如“mach-exynos”就是跟三星的exyons系列CPU有关的文件。我们使用的是I.MX6ULL,所以要关注“imx-common”这个文件夹。另外“cpu”这个文件夹也是和cpu架构有关的,打开以后如图31.1.5所示:
image010.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image010.jpg
31.1.5 cpu文件夹
       从图31.1.5可以看出有多种ARM架构相关的文件夹,I.MX6ULL使用的Cortex-A7内核,Cortex-A7属于armv7,所以我们要关心“armv7”这个文件夹。cpu文件夹里面有个名为就“u-boot.lds”的链接脚本文件,这个就是ARM芯片所使用的u-boot链接脚本文件!armv7这个文件夹里面的文件都是跟ARMV7架构有关的,是我们分析uboot启动源码的时候需要重点关注的。
       2、board文件夹
       board文件夹就是和具体的板子有关的,打开此文件夹,里面全是不同的板子,毫无疑问!正点原子的开发板肯定也在里面(正点原子添加的)borad文件夹里面有个名为“freescale”的文件夹,如图31.1.6所示:
image012.jpg

file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg
31.1.6freescale文件夹
       所有使用freescale芯片的板子都放到此文件夹中,I.MX系列以前属于freescale,只是freescale后来被NXP收购了。打开此freescale文件夹,在里面找到和mx6u(I.MX6UL/ULL)有关的文件夹,如图31.1.7所示:
image014.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image014.jpg
31.1.7mx6u相关板子
       31.1.7中有5个文件夹,这5个文件夹对应5种板子,以“mx6ul”开头的表示使用I.MX6UL芯片的板子,以mx6ull开头的表示使用I.MX6ULL芯片的板子。mx6ullevkNXP官方的I.MX6ULL开发板,正点原子的ALPHA开发板就是在这个基础上开发的,因此mx6ullevk也是正点原子的开发板。我们后面移植uboot到时候就是参考的NXP官方的开发板,也就是要参考mx6ullevk这个文件夹来定义我们的板子。
       3、configs文件夹
       此文件夹为uboot配置文件,uboot是可配置的,但是你要是自己从头开始一个一个项目的配置,那就太麻烦了,因此一般半导体或者开发板厂商都会制作好一个配置文件。我们可以在这个做好的配置文件基础上来添加自己想要的功能,这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”,xxx表示开发板名字,这些defconfig文件都存放在configs文件夹,因此,NXP官方开发板和正点原子的开发板配置文件肯定也在这个文件夹中,如图31.1.8所示:
image016.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image016.gif
31.1.8正点原子开发板配置文件
       31.1.8中这6个文件就是正点原子I.MX6U-ALPHA开发板所对应的uboot默认配置文件。我们只关心mx6ull_14x14_ddr512_emmc_defconfigmx6ull_14x14_ddr256_nand_defconfig这两根文件,分别是正点原子I.MX6ULL EMMC核心板和NAND核心板的配置文件。使用“makexxx_defconfig”命令即可配置uboot,比如:
  1. makemx6ull_14x14_ddr512_emmc_defconfig
复制代码
       上述命令就是配置正点原子的I.MX6ULL EMMC核心板所使用的uboot
在编译uboot之前一定要使用defconfig来配置uboot!
       mx6ull_alientek_emmc.sh中就有下面这一句:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_
defconfig
       这个就是调用mx6ull_14x14_ddr512_emmc_defconfig来配置uboot,只是这个命令还带了一些其它参数而已。
       4、.u-boot.xxx_cmd文件
       .u-boot.xxx_cmd是一系列的文件,这些文件都是编译生成的,都是一些命令文件,比如文件.u-boot.bin.cmd,看名字应该是和u-boot.bin有关的,此文件的内容如下:
示例代码31.1.1 .u-boot.bin.cmd代码
  1. cmd_u-boot.bin:= cp u-boot-nodtb.bin u-boot.bin
复制代码
       .u-boot.bin.cmd里面定义了一个变量:cmd_u-boot.bin,此变量的值为“cp u-boot-nodtb.binu-boot.bin”,也就是拷贝一份u-boot-nodtb.bin文件,并且重命名为u-boot.bin,这个就是u-boot.bin的来源,来自于文件u-boot-nodtb.bin
那么u-boot-nodtb.bin是怎么来的呢?文件.u-boot-nodtb.bin.cmd就是用于生成u-boot.nodtb.bin的,此文件内容如下:
示例代码31.1.2.u-boot-nodtb.bin.cmd代码
  1. cmd_u-boot-nodtb.bin:= arm-linux-gnueabihf-objcopy --gap-fill=0xff  -j .text -j .secure_text -j .rodata -j .hash-j .data -j .got -j .got.plt -j .u_boot_list -j .rel.dyn -O binary  u-bootu-boot-nodtb.bin
复制代码
       这里用到了arm-linux-gnueabihf-objcopy,使用objcopyELF格式的u-boot文件转换为二进制的u-boot-nodtb.bin文件。
       文件u-bootELF格式的文件,文件.u-boot.cmd用于生成u-boot,文件内容如下:
示例代码31.1.3 .u-boot.cmd代码
  1. cmd_u-boot:=arm-linux-gnueabihf-ld.bfd   -pie  --gc-sections -Bstatic -Ttext 0x87800000 -ou-boot -T u-boot.lds arch/arm/cpu/armv7/start.o --start-group  arch/arm/cpu/built-in.o  arch/arm/cpu/armv7/built-in.o  arch/arm/imx-common/built-in.o  arch/arm/lib/built-in.o  board/freescale/common/built-in.o board/freescale/mx6ull_alientek_emmc/built-in.o  cmd/built-in.o  common/built-in.o  disk/built-in.o  drivers/built-in.o  drivers/dma/built-in.o  drivers/gpio/built-in.o  drivers/i2c/built-in.o  drivers/mmc/built-in.o  drivers/mtd/built-in.o  drivers/mtd/onenand/built-in.o  drivers/mtd/spi/built-in.o  drivers/net/built-in.o  drivers/net/phy/built-in.o  drivers/pci/built-in.o  drivers/power/built-in.o  drivers/power/battery/built-in.o  drivers/power/fuel_gauge/built-in.o  drivers/power/mfd/built-in.o  drivers/power/pmic/built-in.o  drivers/power/regulator/built-in.o  drivers/serial/built-in.o  drivers/spi/built-in.o  drivers/usb/dwc3/built-in.o  drivers/usb/emul/built-in.o  drivers/usb/eth/built-in.o  drivers/usb/gadget/built-in.o  drivers/usb/gadget/udc/built-in.o  drivers/usb/host/built-in.o  drivers/usb/musb-new/built-in.o  drivers/usb/musb/built-in.o  drivers/usb/phy/built-in.o  drivers/usb/ulpi/built-in.o  fs/built-in.o lib/built-in.o net/built-in.o  test/built-in.o  test/dm/built-in.o --end-grouparch/arm/lib/eabi_compat.o  -L/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4-lgcc -Map u-boot.map
复制代码
       .u-boot.cmd使用到了arm-linux-gnueabihf-ld.bfd,也就是链接工具,使用ld.bfd将各个built-in.o文件链接在以前就形成了u-boot文件。uboot在编译的时候会将同一个目录中的所有.c文件都编译在一起,并命名为built-in.o,相当于将众多的.c文件对应的.o文件集合在一起,这个就是u-boot文件的来源。
       如果我们要用NXP提供的MFGTools工具向开发板烧写uboot,此时烧写的是u-boot.imx文件,而不是u-boot.bin文件。u-boot.imx是在u-boot.bin文件的头部添加了IVTDCD等信息。这个工作是由文件.u-boot.imx.cmd来完成的,此文件内容如下:
示例代码31.1.4 .u-boot.imx.cmd代码
  1. cmd_u-boot.imx:= ./tools/mkimage -nboard/freescale/mx6ull_alientek_emmc/imximage.cfg.cfgtmp -T imximage -e0x87800000 -d u-boot.bin u-boot.imx
复制代码
       可以看出,这里用到了工具tools/mkimage,而IVTDCD等数据保存在了文件board/freescale/mx6ullevk/imximage-ddr512.cfg.cfgtmp(如果是NAND核心板的话就是imximage-ddr256.cfg.cfgtmp),工具mkimage就是读取文件imximage-ddr512.cfg.cfgtmp里面的信息,然后将其添加到文件u-boot.bin的头部,最终生成u-boot.imx
       文件.u-boot.lds.cmd就是用于生成u-boot.lds链接脚本的,由于.u-boot.lds.cmd文件内容太多,这里就不列出来了。uboot根目录下的u-boot.lds链接脚本就是来源于arch/arm/cpu/u-boot.lds文件。
       还有一些其它的.u-boot.lds.xxx.cmd文件,大家自行分析一下,关于.u-boot.lds.xxx.cmd文件就讲解到这里。
       5、Makefile文件
       这个是顶层Makefile文件,Makefile是支持嵌套的,也就是顶层Makefile可以调用子目录中的Makefile文件。Makefile嵌套在大项目中很常见,一般大项目里面所有的源代码都不会放到同一个目录中,各个功能模块的源代码都是分开的,各自存放在各自的目录中。每个功能模块目录下都有一个Makefile,这个Makefile只处理本模块的编译链接工作,这样所有的编译链接工作就不用全部放到一个Makefile中,可以使得Makefile变得简洁明了。
       uboot源码根目录下的Makefile是顶层Makefile,他会调用其它的模块的Makefile文件,比如drivers/adc/Makefile。当然了,顶层Makefile要做的工作可远不止调用子目录Makefile这么简单,关于顶层Makefile的内容我们稍后会有详细的讲解。
       6、u-boot.xxx文件
       u-boot.xxx同样也是一系列文件,包括u-bootu-boot.binu-boot.cfgu-boot.imxu-boot.ldsu-boot.mapu-boot.srecu-boot.symu-boot-nodtb.bin,这些文件的含义如下:
       u-boot:编译出来的ELF格式的uboot镜像文件。
       u-boot.bin:编译出来的二进制格式的uboot可执行镜像文件。
       u-boot.cfguboot的另外一种配置文件。
       u-boot.imxu-boot.bin添加头部信息以后的文件,NXPCPU专用文件。
       u-boot.lds:链接脚本。
       u-boot.mapuboot映射文件,通过查看此文件可以知道某个函数被链接到了哪个地址上。
       u-boot.srecS-Record格式的镜像文件。
       u-boot.symuboot符号文件。
       u-boot-nodtb.bin:和u-boot.bin一样,u-boot.bin就是u-boot-nodtb.bin的复制文件。
       7、.config文件
       uboot配置文件,使用命令“make xxx_defconfig”配置uboot以后就会自动生成,.config内容如下:
示例代码31.1.5 .config代码
  1. #
  2.   # Automatically generated file; DO NOT EDIT.
  3.   # U-Boot 2016.03 Configuration
  4.   #
  5.   CONFIG_CREATE_ARCH_SYMLINK=y
  6.   CONFIG_HAVE_GENERIC_BOARD=y
  7.   CONFIG_SYS_GENERIC_BOARD=y
  8.   # CONFIG_ARC is not set
  9.    CONFIG_ARM=y
  10. # CONFIG_AVR32 is not set
  11. # CONFIG_BLACKFIN is not set
  12. # CONFIG_M68K is not set
  13. # CONFIG_MICROBLAZE is not set
  14. # CONFIG_MIPS is not set
  15. # CONFIG_NDS32 is not set
  16. # CONFIG_NIOS2 is not set
  17. # CONFIG_OPENRISC is not set
  18. # CONFIG_PPC is not set
  19. # CONFIG_SANDBOX is not set
  20. # CONFIG_SH is not set
  21. # CONFIG_SPARC is not set
  22. # CONFIG_X86 is not set
  23. CONFIG_SYS_ARCH="arm"
  24. CONFIG_SYS_CPU="armv7"
  25. CONFIG_SYS_SOC="mx6"
  26. CONFIG_SYS_VENDOR="freescale"
  27. CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
  28. CONFIG_SYS_CONFIG_NAME="mx6ull_alientek_emmc"

  29. ......

  30. #
  31. # Boot commands
  32. #
  33. CONFIG_CMD_BOOTD=y
  34. CONFIG_CMD_BOOTM=y
  35. CONFIG_CMD_ELF=y
  36. CONFIG_CMD_GO=y
  37. CONFIG_CMD_RUN=y
  38. CONFIG_CMD_IMI=y
  39. CONFIG_CMD_IMLS=y
  40. CONFIG_CMD_XIMG=y

  41. #
  42. # Environment commands
  43. #
  44. CONFIG_CMD_EXPORTENV=y
  45. CONFIG_CMD_IMPORTENV=y
  46. CONFIG_CMD_EDITENV=y
  47. CONFIG_CMD_SAVEENV=y
  48. CONFIG_CMD_ENV_EXISTS=y

  49. ......

  50. #
  51. # Library routines
  52. #
  53. # CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set
  54. CONFIG_HAVE_PRIVATE_LIBGCC=y
  55. # CONFIG_USE_PRIVATE_LIBGCC is not set
  56. CONFIG_SYS_HZ=1000
  57. # CONFIG_USE_TINY_PRINTF is not set
  58. CONFIG_REGEX=y
复制代码
       可以看出.config文件中都是以“CONFIG_”开始的配置项,这些配置项就是Makefile中的变量,因此后面都跟有相应的值,uboot的顶层Makefile或子Makefile会调用这些变量值。在.config中会有大量的变量值为‘y’,这些为‘y’的变量一般用于控制某项功能是否使能,为‘y’的话就表示功能使能,比如:
  1. CONFIG_CMD_BOOTD=y
复制代码
       如果使能了bootd这个命令的话,CONFIG_CMD_BOOTM就为‘y’。在cmd/Makefile中有如下代码:
示例代码31.1.6 cmd/Makefile代码
  1. ifndefCONFIG_SPL_BUILD
  2. # core command
  3. obj-y += boot.o
  4. obj-$(CONFIG_CMD_BOOTM)+= bootm.o
  5. obj-y += help.o
  6. obj-y += version.o
复制代码
    在示例代码31.1.6中,有如下所示一行代码:
  1. obj-$(CONFIG_CMD_BOOTM) += bootm.o
复制代码
       CONFIG_CMD_BOOTM=y,将其展开就是:
  1. obj-y += bootm.o
复制代码
     也就是给obj-y追加了一个“bootm.o”,obj-y包含着所有要编译的文件对应的.o文件,这里表示需要编译文件cmd/bootm.c。相当于通过“CONFIG_CMD_BOOTD=y”来使能bootm这个命令,进而编译cmd/bootm.c这个文件,这个文件实现了命令bootm。在ubootLinux内核中都是采用这种方法来选择使能某个功能,编译对应的源码文件。
       8、README
       README文件描述了uboot的详细信息,包括uboot该如何编译、uboot中各文件夹的含义、相应的命令等等。建议大家详细的阅读此文件,可以进一步增加对uboot的认识。
       关于uboot根目录中的文件和文件夹的含义就讲解到这里,接下来就要开始分析uboot的启动流程了。
31.2 VScode工程创建
       先在Ubuntu下编译一下uboot,然后将编译后的uboot文件夹复制到windows下,并创建VScode工程。打开VScode,选择:文件->打开文件夹,选中uboot文件夹,如图31.2.1所示:
image018.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image018.gif
31.2.1选择uboot源码文件夹
       打开uboot目录以后,VSCode界面如图31.2.2所示:
image020.jpg

file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image020.jpg
31.2.2 VScode界面
       点击“文件->将工作区另存为”,打开保存工作区对话框,将工作区保存到uboot源码根目录下,设置文件名为“uboot”,如图31.2.3所示:
image022.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image022.gif
31.2.3保存工作区
       保存成功以后就会在uboot源码根目录下存在一个名为uboot.code-workspace的文件。这样一个完整的VSCode工程就建立起来了。但是这个VSCode工程包含了uboot的所有文件, uboot中有些文件是不需要的,比如arch目录下是各种架构的文件夹,如图31.2.4所示:
image024.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image024.jpg
31.2.4arch目录
在arch目录下,我们只需要arm文件夹,所以需要将其它的目录从VSCode中给屏蔽掉,比如将arch/avr32这个目录给屏蔽掉。
VSCode上建名为“.vscode”的文件夹,如图31.2.5所示:
image026.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image026.gif
31.2.5新建.vscode文件夹
       输入新建文件夹的名字,完成以后如图31.2.6所示。
image028.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image028.jpg
31.2.6新建的.vscode文件夹
       .vscode文件夹中新建一个名为“settings.json”的文件,然后在settings.json中输入如下内容:
示例代码31.2.1settings.json文件代码
  1. {
  2. "search.exclude":{
  3. "**/node_modules": true,
  4. "**/bower_components": true,
  5. },
  6. "files.exclude":{
  7. "**/.git": true,
  8. "**/.svn": true,
  9. "**/.hg": true,
  10. "**/CVS": true,
  11. "**/.DS_Store": true,
  12. }
  13. }
复制代码
       结果如图31.2.7所示:
image030.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image030.jpg
31.2.7 settings.json文件默认内容
       其中"search.exclude"里面是需要在搜索结果中排除的文件或者文件夹,"files.exclude"是左侧工程目录中需要排除的文件或者文件夹。我们需要将arch/avr32文件夹下的所有文件从搜索结果和左侧的工程目录中都排除掉,因此在"search.exclude""files.exclude"中输入如图31.2.8所示内容:
image032.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image032.jpg
31.2.8 排除arch/avr32目录
       此时再看一下左侧的工程目录,发现arch目录下没有avr32这个文件夹了,说明avr32这个文件夹被排除掉了,如图31.2.9所示:
image034.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image034.jpg
31.2.9 arch/avr32目录排除
       我们只是在"search.exclude""files.exclude"中加入了:arch/avr32": true,冒号前面的是要排除的文件或者文件夹,冒号后面为是否将文件排除,true表示排除,false表示不排除。用这种方法即可将不需要的文件,或者文件夹排除掉,对于本章我们分析uboot而言,在"search.exclude""files.exclude"中需要输入的完成的内容如下:
示例代码31.2.2 settings.json文件代码
  1. "**/*.o":true,
  2. "**/*.su":true,
  3. "**/*.cmd":true,
  4. "arch/arc":true,
  5. "arch/avr32":true,
  6. "arch/blackfin":true,
  7. "arch/m68k":true,
  8. "arch/microblaze":true,
  9. "arch/mips":true,
  10. "arch/nds32":true,
  11. "arch/nios2":true,
  12. "arch/openrisc":true,
  13. "arch/powerpc":true,
  14. "arch/sandbox":true,
  15. "arch/sh":true,
  16. "arch/sparc":true,
  17. "arch/x86":true,
  18. "arch/arm/mach*":true,
  19. "arch/arm/cpu/arm11*":true,
  20. "arch/arm/cpu/arm720t":true,
  21. "arch/arm/cpu/arm9*":true,
复制代码
       上述代码用到了通配符“*”,比如“**/*.o”表示所有.o结尾的文件。“configs/[a-l]*”表示configs目录下所有以‘a’~‘l’开头的文件或者文件夹。上述配置只是排除了一部分文件夹,大家在实际的使用中可以根据自己的实际需求来选择将哪些文件或者文件夹排除掉。排除以后我们的工程就会清爽很多,搜索的时候也不会跳出很多文件了。
31.3 U-Boot顶层Makefile分析
       在阅读uboot源码之前,肯定是要先看一下顶层Makefile,分析gcc版本代码的时候一定是先从顶层Makefile开始的,然后再是子Makefile,这样通过层层分析Makefile即可了解整个工程的组织结构。顶层Makefile也就是uboot根目录下的Makefile文件,由于顶层Makefile文件内容比较多,所以我们将其分开来看。
31.3.1 版本号
顶层Makefile一开始是版本号,内容如下(为了方便分析,顶层Makefile代码段前段行号采用Makefile中的行号,因为uboot会更新,因此行号可能会与你所看的顶层Makefile有所不同):
示例代码31.3.1.1 顶层Makefile代码
  1. VERSION =2016
  2. PATCHLEVEL =03
  3. SUBLEVEL =
  4. EXTRAVERSION =
  5. NAME =
复制代码
VERSION是主版本号,PATCHLEVEL是补丁版本号,SUBLEVEL是次版本号,这三个一起构成了uboot的版本号,比如当前的uboot版本号就是“2016.03”。EXTRAVERSION是附加版本信息,NAME是和名字有关的,一般不使用这两个。
31.3.2 MAKEFLAGS变量
make是支持递归调用的,也就是在Makefile中使用“make”命令来执行其他的Makefile文件,一般都是子目录中的Makefile文件。假如在当前目录下存在一个“subdir”子目录,这个子目录中又有其对应的Makefile文件,那么这个工程在编译的时候其主目录中的Makefile就可以调用子目录中的Makefile,以此来完成所有子目录的编译。主目录的Makefile可以使用如下代码来编译这个子目录:
  1. $(MAKE) -C subdir
复制代码
       $(MAKE)就是调用“make”命令,-C指定子目录。有时候我们需要向子make传递变量,这个时候使用“export”来导出要传递给子make的变量即可,如果不希望哪个变量传递给子make的话就使用“unexport”来声明不导出:
  1. export VARIABLE ……                      // 导出变量给子make 。
  2. unexport VARIABLE……                  //不导出变量给子make。
复制代码
       有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。在uboot的主Makefile中有如下代码:
示例代码31.3.2.1 顶层Makefile代码
  1. MAKEFLAGS +=-rR --include-dir=$(CURDIR)
复制代码
上述代码使用“+=”来给变量MAKEFLAGS追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“--include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。
31.3.3 命令输出
uboot默认编译是不会在终端中显示完整的命令,都是短命令,如图31.3.3所示:
image036.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image036.jpg
31.3.3.1 终端短命令输出
       在终端中输出短命令虽然看起来很清爽,但是不利于分析uboot的编译过程。可以通过设置变量“V=1“来实现完整的命令输出,这个在调试uboot的时候很有用,结果如图31.3.3.2所示:
image038.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image038.jpg
31.3.3.2 终端完整命令输出
顶层Makefile中控制命令输出的代码如下:
  1. <blockquote> ifeq ("$(origin V)","command line")
复制代码
示例代码31.3.3.1 顶层Makefile代码

       上述代码中先使用ifeq来判断"$(origin V)""command line"是否相等。这里用到了Makefile中的函数originorigin和其他的函数不一样,它不操作变量的值,origin用于告诉你变量是哪来的,语法为:
  1. $(origin <variable>)
复制代码
       variable是变量名,o函数的返回值就是变量来源,因此$(origin V)就是变量V的来源。如果变量V是在命令行定义的那么它的来源就是"commandline",这样"$(originV)""commandline"就相等了。当这两个相等的时候变量KBUILD_VERBOSE就等于V的值,比如在命令行中输入“V=1“的话那么KBUILD_VERBOSE=1。如果没有在命令行输入V的话KBUILD_VERBOSE=0
第80行判断KBUILD_VERBOSE是否为1,如果KBUILD_VERBOSE为1的话变量quiet和Q都为空,如果KBUILD_VERBOSE=0的话变量quiet为“quiet_“,变量Q为“@”,综上所述:
V=1的话:
  1. KBUILD_VERBOSE=1
  2. quiet= 空。
  3. Q=   空。
复制代码
V=0或者命令行不定义V的话:
  1. KBUILD_VERBOSE=0
  2. quiet= quiet_。
  3. Q=   @。
复制代码
       Makefile中会用到变量quietQ来控制编译的时候是否在终端输出完整的命令,在顶层Makefile中有很多如下所示的命令:
  1. $(Q)$(MAKE) $(build)=tools
复制代码
       如果V=0的话上述命令展开就是“@make$(build)=tools”,make在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。当V=1的时候Q就为空,上述命令就是“make$(build)=tools”,因此在make执行的过程,命令会被完整的输出在终端上。
       有些命令会有两个版本,比如:
  1. quiet_cmd_sym ?= SYM     $@
  2. cmd_sym ?= $(OBJDUMP) -t [        DISCUZ_CODE_11644        ]lt;> $@
复制代码
       sym命令分为“quiet_cmd_sym”和“cmd_sym”两个版本,这两个命令的功能都是一样的,区别在于make执行的时候输出的命令不同。quiet_cmd_xxx命令输出信息少,也就是短命令,而cmd_xxx命令输出信息多,也就是完整的命令。
如果变量quiet为空的话,整个命令都会输出。
       如果变量quiet为“quiet_”的话,仅输出短版本。
       如果变量quiet为“silent_”的话,整个命令都不会输出。
31.3.4 静默输出
上一小节讲了,设置V=0或者在命令行中不定义V的话,编译uboot的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译uboot的时候不需要输出命令,这个时候就可以使用uboot的静默输出功能。编译的时候使用“make-s”即可实现静默输出,顶层Makefile中相应的代码如下:
示例代码31.3.4.1 顶层Makefile代码
  1.   # If the user is running make -s (silent mode), suppress echoingof
  2.   # commands

  3.   ifneq ($(filter 4.%,$(MAKE_VERSION)),)  # make-4
  4.   ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
  5.     quiet=silent_
  6.   endif
  7. else                   # make-3.8x
  8.   ifneq ($(filter s%-s%,$(MAKEFLAGS)),)
  9.     quiet=silent_
  10.   endif
  11.   endif

  12. export quiet QKBUILD_VERBOSE
复制代码
       91行判断当前正在使用的编译器版本号是否为4.x,判断$(filter4.%,$(MAKE_VERSION))和“”()是否相等,如果不相等的话就成立,执行里面的语句。也就是说$(filter4.%,$(MAKE_VERSION))不为空的话条件就成立,这里用到了Makefile中的filter函数,这是个过滤函数,函数格式如下:
  1. $(filter <pattern...>,<text>)
复制代码
       filter函数表示以pattern模式过滤text字符串中的单词,仅保留符合模式pattern的单词,可以有多个模式。函数返回值就是符合pattern的字符串。因此$(filter 4.%,$(MAKE_VERSION))的含义就是在字符串“MAKE_VERSION”中找出符合“4.%”的字符(%为通配符)MAKE_VERSION是编译器的版本号,我们当前使用的交叉编译器版本号为4.9.4,所以肯定可以找出“4.%”。因此$(filter 4.%,$(MAKE_VERSION))不为空,条件成立,执行92~94行的语句。
92行也是一个判断语句,如果$(filter %s ,$(firstwordx$(MAKEFLAGS)))不为空的话条件成立,变量quiet等于“silent_”。这里也用到了函数filter,在$(firstword x$(MAKEFLAGS)))中过滤出符合“%s”的单词。到了函数firstword,函数firstword是获取首单词,函数格式如下:
  1. $(firstword <text>)
复制代码
firstword函数用于取出text字符串中的第一个单词,函数的返回值就是获取到的单词。当使用“make-s”编译的时候,“-s”会作为MAKEFLAGS变量的一部分传递给Makefile。在顶层Makfile中添加如图31.3.4.1所示的代码:
image040.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image040.gif
31.3.4.1 顶层Makefile添加代码
       31.3.4.1中的两行代码用于输出$(firstword x$(MAKEFLAGS))的结果,最后修改文件mx6ull_alientek_emmc.sh,在里面加入“-s”选项,结果如图31.3.4.2所示:
image042.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image042.gif
31.3.4.2 加入-s选项
       修改完成以后执行mx6ull_alientek_emmc.sh,结果如图31.3.4.3所示:
image044.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image044.gif
31.3.4.3 修改顶层Makefile后的执行结果
       从图31.3.4.3可以看出第一个单词是“xrRs”,将$(filter %s ,$(firstwordx$(MAKEFLAGS)))展开就是$(filter%s, xrRs),而$(filter %s,xrRs)的返回值肯定不为空,条件成立,quiet=silent_
101       使用export导出变量quietQKBUILD_VERBOSE
31.3.5 设置编译结果输出目录
uboot可以将编译出来的目标文件输出到单独的目录中,在make的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到out目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定O参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定O参数。顶层Makefile中相关的代码如下:
示例代码31.3.5.1 顶层Makefile代码
  1. # kbuild supportssaving output files in a separate directory.
  2. # To locate outputfiles in a separate directory two syntaxes are supported.
  3. # In both casesthe working directory must be the root of the kernel src.
  4. # 1) O=
  5. # Use "make O=dir/to/store/output/files/"
  6. #
  7. # 2) Set KBUILD_OUTPUT
  8. # Set theenvironment variable KBUILD_OUTPUT to point to the directory
  9. # where the outputfiles shall be placed.
  10. # exportKBUILD_OUTPUT=dir/to/store/output/files/
  11. # make
  12. #
  13. # The O= assignment takesprecedence over the KBUILD_OUTPUT environment
  14. # variable.

  15. # KBUILD_SRC isset on invocation of make in OBJ directory
  16. # KBUILD_SRC isnot intended to be used by the regular user (for now)
  17. ifeq ($(KBUILD_SRC),)

  18. # OK, Make called indirectory where kernel src resides
  19. # Do we want tolocate output files in a separate directory?
  20. ifeq ("$(originO)","commandline")
  21.    KBUILD_OUTPUT := $(O)
  22. endif

  23. # That's ourdefault target when none is given on the command line
  24. PHONY := _all
  25. _all:

  26. # Cancel implicitrules on top Makefile
  27. $(CURDIR)/Makefile Makefile:;

  28. ifneq ($(KBUILD_OUTPUT),)
  29. # Invoke a secondmake in the output directory, passing relevant variables
  30. # check that theoutput directory actually exists
  31. saved-output := $(KBUILD_OUTPUT)
  32. KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT)&& cd $(KBUILD_OUTPUT) \
  33. &&/bin/pwd)
  34. ...
  35. endif # ifneq ($(KBUILD_OUTPUT),)
  36. endif # ifeq ($(KBUILD_SRC),)
复制代码
       124行判断“O”是否来自于命令行,如果来自命令行的话条件成立,KBUILD_OUTPUT就为$(O),因此变量KBUILD_OUTPUT就是输出目录。
       135行判断KBUILD_OUTPUT是否为空。
       139行调用mkdir命令,创建KBUILD_OUTPUT目录,并且将创建成功以后的绝对路径赋值给KBUILD_OUTPUT。至此,通过O指定的输出目录就存在了。
31.3.6 代码检查
       uboot支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件,顶层Makefile中的代码如下:
示例代码31.3.6.1 顶层Makefile代码
  1. # Call a sourcecode checker (by default,"sparse") as part of the
  2. # C compilation.
  3. #
  4. # Use 'make C=1' to enable checkingof only re-compiledfiles.
  5. # Use 'make C=2' to enable checkingof *all* source files, regardless
  6. # of whether theyare re-compiled ornot.
  7. #
  8. # See the file "Documentation/sparse.txt"for more details, including
  9. # where to get the"sparse" utility.

  10. ifeq ("$(originC)","commandline")
  11.    KBUILD_CHECKSRC = $(C)
  12. endif
  13. ifndefKBUILD_CHECKSRC
  14.    KBUILD_CHECKSRC =0
  15. endif
复制代码
       176行判断C是否来源于命令行,如果C来源于命令行,那就将C赋值给变量KBUILD_CHECKSRC,如果命令行没有C的话KBUILD_CHECKSRC就为0
31.3.7 模块编译
在uboot中允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。顶层Makefile中的代码如下:
示例代码31.3.7.1 顶层Makefile代码
  1. # Use make M=dir to specifydirectory of external module to build
  2. # Old syntax make ... SUBDIRS=$PWD is stillsupported
  3. # Setting theenvironment variable KBUILD_EXTMOD take precedence
  4. ifdef SUBDIRS
  5.    KBUILD_EXTMOD ?= $(SUBDIRS)
  6. endif

  7. ifeq ("$(originM)","commandline")
  8.    KBUILD_EXTMOD := $(M)
  9. endif

  10. # If building anexternal module we donot care about the all: rule
  11. # but instead _alldepend on modules
  12. PHONY += all
  13. ifeq ($(KBUILD_EXTMOD),)
  14. _all: all
  15. else
  16. _all: modules
  17. endif

  18. ifeq ($(KBUILD_SRC),)
  19.          # building in the source tree
  20.          srctree :=.
  21. else
  22.          ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
  23.                  # building in a subdirectoryof the source tree
  24.                  srctree :=..
  25. else
  26.                  srctree := $(KBUILD_SRC)
  27.          endif
  28. endif
  29. objtree     :=.
  30. src     := $(srctree)
  31. obj     := $(objtree)

  32. VPATH       := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))

  33. export srctree objtree VPATH
复制代码
第186行判断是否定义了SUBDIRS,如果定义了SUBDIRS,变量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法“make SUBIDRS=dir”
       190行判断是否在命令行定义了M,如果定义了的话KBUILD_EXTMOD=$(M)
       197行判断KBUILD_EXTMOD时为空,如果为空的话目标_all依赖all,因此要先编译出all。否则的话默认目标_all依赖modules,要先编译出modules,也就是编译模块。一般情况下我们不会在uboot中编译模块,所以此处会编译all这个目标。
       203行判断KBUILD_SRC是否为空,如果为空的话就设置变量srctree为当前目录,即srctree为“.”,一般不设置KBUILD_SRC
214行设置变量objtree为当前目录。
215216行分别设置变量srcobj,都为当前目录。
       218行设置VPATH
       220行导出变量scrtreeobjtreeVPATH
31.3.8 获取主机架构和系统
接下来顶层Makefile会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下:
示例代码31.3.8.1 顶层Makefile代码
  1. HOSTARCH := $(shell uname -m | \
  2.      sed -e s/i.86/x86/ \
  3. -e s/sun4u/sparc64/ \
  4.          -e s/arm.*/arm/ \
  5. -e s/sa110/arm/ \
  6. -e s/ppc64/powerpc/ \
  7. -e s/ppc/powerpc/ \
  8. -e s/macppc/powerpc/\
  9.          -e s/sh.*/sh/)

  10. HOSTOS := $(shell uname -s | tr '[:upper:]''[:lower:]'| \
  11.          sed -e 's/\(cygwin\).*/cygwin/')

  12. export  HOSTARCH HOSTOS
复制代码
       227行定义了一个变量HOSTARCH,用于保存主机架构,这里调用shell命令“uname-m”获取架构名称,结果如图31.3.8.1所示:
image046.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image046.jpg
31.3.8.1 uname-m命令
       从图31.3.8.1可以看出当前电脑主机架构为“x86_64”,shell中的“|”表示管道,意思是将左边的输出作为右边的输入,sed -e是替换命令,“sed -es/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”,其他的“sed -s”命令同理。对于我的电脑而言,HOSTARCH=x86_64
       237行定义了变量HOSTOS,此变量用于保存主机OS的值,先使用shell命令“name-s”来获取主机OS,结果如图31.3.8.2所示:
image048.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image048.jpg
31.3.8.2 uname-s命令
       从图31.3.8.2可以看出此时的主机OS为“Linux”,使用管道将“Linux”作为后面“tr '[:upper:]' '[:lower:]'”的输入,“tr '[:upper:]' '[:lower:]'”表示将所有的大写字母替换为小写字母,因此得到“linux”。最后同样使用管道,将“linux”作为“sed -e 's/\(cygwin\).*/cygwin/'”的输入,用于将cygwin.*替换为cygwin。因此,HOSTOS=linux
       240行导出HOSTARCH=x86_64HOSTOS=linux
31.3.9 设置目标架构、交叉编译器和配置文件
编译uboot的时候需要设置目标板架构和交叉编译器,“make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置ARCH和CROSS_COMPILE,在顶层Makefile中代码如下:
示例代码31.3.9.1 顶层Makefile代码
  1. # set default to nothing for native builds
  2. ifeq ($(HOSTARCH),$(ARCH))
  3. CROSS_COMPILE ?=
  4. endif

  5. KCONFIG_CONFIG  ?=.config
  6. export KCONFIG_CONFIG
复制代码
       245行判断HOSTARCHARCH这两个变量是否相等,主机架构(变量HOSTARCH)x86_64,而我们编译的是ARM版本uboot,肯定不相等,所以CROS_COMPILE=arm-linux-gnueabihf-。从示例代码31.3.9.1可以看出,每次编译uboot的时候都要在make命令后面设置ARCHCROS_COMPILE,使用起来很麻烦,可以直接修改顶层Makefile,在里面加入ARCHCROSS_COMPILE的定义,如图31.3.9.1所示:
image050.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image050.jpg
31.3.9.1 定义ARCHCROSS_COMPILE
按照图31.3.9.1所示,直接在顶层Makefile里面定义ARCH和CROSS_COMPILE,这样就不用每次编译的时候都要在make命令后面定义ARCH和CROSS_COMPILE。
第249行定义变量KCONFIG_CONFIG,uboot是可以配置的,这里设置配置文件为.config,.config默认是没有的,需要使用命令“makexxx_defconfig”对uboot进行配置,配置完成以后就会在uboot根目录下生成.config。默认情况下.config和xxx_defconfig内容是一样的,因为.config就是从xxx_defconfig复制过来的。如果后续自行调整了uboot的一些配置参数,那么这些新的配置参数就添加到了.config中,而不是xxx_defconfig。相当于xxx_defconfig只是一些初始配置,而.config里面的才是实时有效的配置。
31.3.10 调用scripts/Kbuild.include
       Makefile会调用文件scripts/Kbuild.include这个文件,顶层Makefile中代码如下:
示例代码31.3.10.1 顶层Makefile代码
  1. # We need somegeneric definitions (donottry to remakethe file).
  2. scripts/Kbuild.include:;
  3. include scripts/Kbuild.include
复制代码
       示例代码31.3.10.1中使用“include”包含了文件scripts/Kbuild.include,此文件里面定义了很多变量,如图31.3.10.1所示:
image052.jpg

file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image052.jpg
31.3.10.1 Kbuild.include文件
       uboot的编译过程中会用到scripts/Kbuild.include中的这些变量,后面用到的时候再分析。
31.3.11 交叉编译工具变量设置
上面我们只是设置了CROSS_COMPILE的名字,但是交叉编译器其他的工具还没有设置,顶层Makefile中相关代码如下:
示例代码31.3.11.1 顶层Makefile代码
  1. # Make variables (CC, etc...)

  2. AS              = $(CROSS_COMPILE)as
  3. # Always use GNU ld
  4. ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2>/dev/null),)
  5. LD              = $(CROSS_COMPILE)ld.bfd
  6. else
  7. LD              =            $(CROSS_COMPILE)ld
  8. endif
  9. CC              =           $(CROSS_COMPILE)gcc
  10. CPP             =         $(CC)-E
  11. AR              =           $(CROSS_COMPILE)ar
  12. NM              =          $(CROSS_COMPILE)nm
  13. LDR             =         $(CROSS_COMPILE)ldr
  14. STRIP             =      $(CROSS_COMPILE)strip
  15. OBJCOPY           = $(CROSS_COMPILE)objcopy
  16. OBJDUMP          = $(CROSS_COMPILE)objdump
复制代码
31.3.12 导出其他变量
接下来在顶层Makefile会导出很多变量,代码如下:
示例代码31.3.12.1 顶层Makefile代码
  1. export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION
  2. export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
  3. export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC
  4. export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
  5. export MAKE AWK PERL PYTHON
  6. export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS
  7. export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS
  8. export KBUILD_CFLAGS KBUILD_AFLAGS
复制代码
       这些变量中大部分都已经在前面定义了,我们重点来看一下下面这几个变量:
  1. ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
复制代码
       7个变量在顶层Makefile是找不到的,说明这7个变量是在其他文件里面定义的,先来看一下这7个变量都是什么内容,在顶层Makefile中输入如图31.3.12.1所示的内容:
image054.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image054.gif
31.3.12.1 输出变量值
       修改好顶层Makefile以后执行如下命令:
  1. make ARCH=armCROSS_COMPILE=arm-linux-gnueabihf- mytest
复制代码
       结果如图31.3.12.2所示:
image056.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image056.jpg
31.3.12.2 变量结果
       从图31.3.12.2可以看到这7个变量的值,这7个变量是从哪里来的呢?在uboot根目录下有个文件叫做config.mk,这7个变量就是在config.mk里面定义的,打开config.mk内容如下:
示例代码31.3.12.2 config.mk代码
  1.   #
  2.   # (C) Copyright 2000-2013
  3.   # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  4.   #
  5.   # SPDX-License-Identifier:   GPL-2.0+
  6.   #
  7.   #########################################################################

  8.   # This file is included from ./Makefile and spl/Makefile.
  9. # Clean the state to avoid the same flags added twice.
  10. #
  11. # (Tegra needs different flags for SPL.
  12. #  That's the reason why this file must be included from spl/Makefile too.
  13. #  If we did not have Tegra SoCs, build system would be much simpler...)
  14. PLATFORM_RELFLAGS :=
  15. PLATFORM_CPPFLAGS :=
  16. PLATFORM_LDFLAGS :=
  17. LDFLAGS :=
  18. LDFLAGS_FINAL :=
  19. OBJCOPYFLAGS :=
  20. # clear VENDOR for tcsh
  21. VENDOR :=
  22. #########################################################################

  23. ARCH := $(CONFIG_SYS_ARCH:"%"=%)
  24. CPU := $(CONFIG_SYS_CPU:"%"=%)
  25. ifdef CONFIG_SPL_BUILD
  26. ifdef CONFIG_TEGRA
  27. CPU := arm720t
  28. endif
  29. endif
  30. BOARD := $(CONFIG_SYS_BOARD:"%"=%)
  31. ifneq ($(CONFIG_SYS_VENDOR),)
  32. VENDOR := $(CONFIG_SYS_VENDOR:"%"=%)
  33. endif
  34. ifneq ($(CONFIG_SYS_SOC),)
  35. SOC := $(CONFIG_SYS_SOC:"%"=%)
  36. endif

  37. # Some architecture config.mk files need to know what CPUDIR is set to,
  38. # so calculate CPUDIR before including ARCH/SOC/CPU config.mk files.
  39. # Check if arch/$ARCH/cpu/$CPU exists, otherwise assume arch/$ARCH/cpu contains
  40. # CPU-specific code.
  41. CPUDIR=arch/$(ARCH)/cpu$(if $(CPU),/$(CPU),)

  42. sinclude $(srctree)/arch/$(ARCH)/config.mk   
  43. sinclude $(srctree)/$(CPUDIR)/config.mk     

  44. ifdef    SOC
  45. sinclude $(srctree)/$(CPUDIR)/$(SOC)/config.mk  
  46. endif
  47. ifneq ($(BOARD),)
  48. ifdef    VENDOR
  49. BOARDDIR = $(VENDOR)/$(BOARD)
  50. else
  51. BOARDDIR = $(BOARD)
  52. endif
  53. endif
  54. ifdef    BOARD
  55. sinclude $(srctree)/board/$(BOARDDIR)/config.mk  # include board specific rules
  56. endif

  57. ifdef FTRACE
  58. PLATFORM_CPPFLAGS +=-finstrument-functions -DFTRACE
  59. endif

  60. # Allow use of stdint.h if available
  61. ifneq ($(USE_STDINT),)
  62. PLATFORM_CPPFLAGS +=-DCONFIG_USE_STDINT
  63. endif

  64. #########################################################################

  65. RELFLAGS := $(PLATFORM_RELFLAGS)

  66. PLATFORM_CPPFLAGS += $(RELFLAGS)
  67. PLATFORM_CPPFLAGS +=-pipe

  68. LDFLAGS += $(PLATFORM_LDFLAGS)
  69. LDFLAGS_FINAL +=-Bstatic

  70. export PLATFORM_CPPFLAGS
  71. export RELFLAGS
  72. export LDFLAGS_FINAL
复制代码
       25行定义变量ARCH,值为$(CONFIG_SYS_ARCH:"%"=%),也就是提取CONFIG_SYS_ARCH里面双引号“”之间的内容。比如CONFIG_SYS_ARCH=arm”的话,ARCH=arm
       26行定义变量CPU,值为$(CONFIG_SYS_CPU:"%"=%)
       32行定义变量BOARD,值为(CONFIG_SYS_BOARD:"%"=%)
       34行定义变量VENDOR,值为$(CONFIG_SYS_VENDOR:"%"=%)
       37行定义变量SOC,值为$(CONFIG_SYS_SOC:"%"=%)
       44行定义变量CPUDIR,值为arch/$(ARCH)/cpu$(if$(CPU),/$(CPU),)
       46sincludeinclude的功能类似,在Makefile中都是读取指定文件内容,这里读取文件$(srctree)/arch/$(ARCH)/config.mk的内容。sinclude读取的文件如果不存在的话不会报错。
       47行读取文件$(srctree)/$(CPUDIR)/config.mk的内容。
       50行读取文件$(srctree)/$(CPUDIR)/$(SOC)/config.mk的内容。
       54行定义变量BOARDDIR,如果定义了VENDOR那么BOARDDIR=$(VENDOR)/$(BOARD),否则的BOARDDIR=$(BOARD)
       60行读取文件$(srctree)/board/$(BOARDDIR)/config.mk
       接下来需要找到CONFIG_SYS_ARCHCONFIG_SYS_CPUCONFIG_SYS_BOARDCONFIG_SYS_VENDORCONFIG_SYS_SOC5个变量的值。这5个变量在uboot根目录下的.config文件中有定义,定义如下:
示例代码31.3.12.3 .config文件代码
  1. CONFIG_SYS_ARCH="arm"
  2. CONFIG_SYS_CPU="armv7"
  3. CONFIG_SYS_SOC="mx6"
  4. CONFIG_SYS_VENDOR="freescale"
  5. CONFIG_SYS_BOARD="mx6ullevk"
  6. CONFIG_SYS_CONFIG_NAME="mx6ullevk"
复制代码
根据示例代码31.3.12.3可知:
  1. ARCH = arm
  2. CPU = armv7
  3. BOARD = mx6ullevk
  4. VENDOR = freescale
  5. SOC = mx6
  6. CPUDIR = arch/arm/cpu/armv7
  7. BOARDDIR = freescale/mx6ullevk
  8.        在config.mk中读取的文件有:
  9. arch/arm/config.mk
  10. arch/arm/cpu/armv7/config.mk
  11. arch/arm/cpu/armv7/mx6/config.mk (此文件不存在)
  12. board/ freescale/mx6ullevk/config.mk (此文件不存在)
复制代码
31.3.13 make xxx_defconfig过程
在编译uboot之前要使用“makexxx_defconfig”命令来配置uboot,那么这个配置过程是如何运行的呢?在顶层Makefile中有如下代码:
示例代码31.3.13.1 顶层Makefile代码段
  1. # To make sure we donot include .config for any of the *config targets
  2. # catch them early,and hand them over to scripts/kconfig/Makefile
  3. # It is allowed to specify more targets when calling make, including
  4. # mixing *config targets and build targets.
  5. # For example 'make oldconfig all'.
  6. # Detect when mixed targets is specified,and make a second invocation
  7. # of make so .config is not included in thiscase either (for*config).

  8. version_h := include/generated/version_autogenerated.h
  9. timestamp_h := include/generated/timestamp_autogenerated.h

  10. no-dot-config-targets := clean clobber mrproper distclean \
  11.               help %docs check% coccicheck \
  12.               ubootversion backup

  13. config-targets :=0
  14. mixed-targets  :=0
  15. dot-config     :=1

  16. ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
  17.      ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
  18.          dot-config :=0
  19.      endif
  20. endif

  21. ifeq ($(KBUILD_EXTMOD),)
  22.          ifneq ($(filter config %config,$(MAKECMDGOALS)),)
  23.                  config-targets :=1
  24.                  ifneq ($(words $(MAKECMDGOALS)),1)
  25.                          mixed-targets :=1
  26.                  endif
  27.          endif
  28. endif

  29. ifeq ($(mixed-targets),1)
  30. # ================================================================
  31. # We're called with mixed targets (*config and build targets).
  32. # Handle them one by one.

  33. PHONY += $(MAKECMDGOALS) __build_one_by_one

  34. $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
  35.      @:

  36. __build_one_by_one:
  37.      $(Q)set -e; \
  38. for i in $(MAKECMDGOALS);do \
  39.          $(MAKE)-f $(srctree)/Makefile $i; \
  40.      done

  41. else
  42. ifeq ($(config-targets),1)
  43. # ================================================================
  44. # *config targets only - make sure prerequisites are updated,and descend
  45. # in scripts/kconfig to make the *config target

  46. KBUILD_DEFCONFIG := sandbox_defconfig
  47. export KBUILD_DEFCONFIG KBUILD_KCONFIG

  48. config: scripts_basic outputmakefile FORCE
  49.      $(Q)$(MAKE) $(build)=scripts/kconfig $@

  50. %config: scripts_basic outputmakefile FORCE
  51.      $(Q)$(MAKE) $(build)=scripts/kconfig $@

  52. else
  53. #==================================================================
  54. # Build targets only -this includes vmlinux, arch specific targets, clean
  55. # targets and others. In general all targets except *config targets.

  56. ifeq ($(dot-config),1)
  57. # Read in config
  58. -include include/config/auto.conf
  59. ......
复制代码
       422行定义了变量version_h,这变量保存版本号文件,此文件是自动生成的。文件include/generated/version_autogenerated.h内容如图31.3.13.1所示:
image058.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image058.jpg
31.3.13.1 版本号文件
       423行定义了变量timestamp_h,此变量保存时间戳文件,此文件也是自动生成的。文件include/generated/timestamp_autogenerated.h内容如图31.3.13.2所示:
image060.jpg

file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image060.jpg
31.3.13.2 时间戳文件
       425行定义了变量no-dot-config-targets
       429行定义了变量config-targets,初始值为0
       430行定义了变量mixed-targets,初始值为0
       431行定义了变量dot-config,初始值为1
       433行将MAKECMDGOALS中不符合no-dot-config-targets的部分过滤掉,剩下的如果不为空的话条件就成立。MAKECMDGOALSmake的一个环境变量,这个变量会保存你所指定的终极目标列表,比如执行“make mx6ull_alientek_emmc_defconfig”,那么MAKECMDGOALS就为mx6ull_alientek_emmc_defconfig。很明显过滤后为空,所以条件不成立,变量dot-config依旧为1
       439行判断KBUILD_EXTMOD是否为空,如果KBUILD_EXTMOD为空的话条件成立,经过前面的分析,我们知道KBUILD_EXTMOD为空,所以条件成立。
       440行将MAKECMDGOALS中不符合“config”和“%config”的部分过滤掉,如果剩下的部分不为空条件就成立,很明显此处条件成立,变量config-targets=1
       442行统计MAKECMDGOALS中的单词个数,如果不为1的话条件成立。此处调用Makefile中的words函数来统计单词个数,words函数格式如下:
  1. $(words <text>)
复制代码
       很明显,MAKECMDGOALS的单词个数是1个,所以条件不成立,mixed-targets继续为0。综上所述,这些变量值如下:
  1. config-targets =1
  2. mixed-targets = 0
  3. dot-config = 1
复制代码
       448行如果变量mixed-targets1的话条件成立,很明显,条件不成立。
       465行如果变量config-targets1的话条件成立,很明显,条件成立,执行这个分支。
       473行,没有目标与之匹配,所以不执行。
       476行,有目标与之匹配,当输入“makexxx_defconfig”的时候就会匹配到%config目标,目标“%config”依赖于scripts_basicoutputmakefileFORCEFORCE在顶层Makefile1610行有如下定义:
示例代码31.3.13.2 顶层Makefile代码段
  1. PHONY += FORCE
  2. FORCE:
复制代码
       可以看出FORCE是没有规则和依赖的,所以每次都会重新生成FORCE。当FORCE作为其他目标的依赖时,由于FORCE总是被更新过的,因此依赖所在的规则总是会执行的。
依赖scripts_basic和outputmakefile在顶层Makefile中的内容如下:
示例代码31.3.13.3 顶层Makefile代码段
  1. # Basic helpers built in scripts/
  2. PHONY += scripts_basic
  3. scripts_basic:
  4.      $(Q)$(MAKE) $(build)=scripts/basic
  5.      $(Q)rm -f .tmp_quiet_recordmcount

  6. # To avoid any implicit rule to kick in, define an empty command.
  7. scripts/basic/%: scripts_basic ;

  8. PHONY += outputmakefile
  9. # outputmakefile generates a Makefile in the output directory,if
  10. # using a separate output directory. This allows convenient use of
  11. # make in the output directory.
  12. outputmakefile:
  13. ifneq ($(KBUILD_SRC),)
  14.      $(Q)ln -fsn $(srctree) source
  15.      $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
  16.          $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
  17. endif
复制代码
第408行,判断KBUILD_SRC是否为空,只有变量KBUILD_SRC不为空的时候outputmakefile才有意义,经过我们前面的分析KBUILD_SRC为空,所以outputmakefile无效。只有scripts_basic是有效的。
       396~398行是scripts_basic的规则,其对应的命令用到了变量QMAKEbuild,其中:
  1. Q=@或为空
  2. MAKE=make
复制代码
       变量build是在scripts/Kbuild.include文件中有定义,定义如下:
示例代码31.3.13.3 Kbuild.include代码段
  1. ###
  2. # Shorthand for $(Q)$(MAKE)-f scripts/Makefile.build obj=
  3. # Usage:
  4. # $(Q)$(MAKE) $(build)=dir
  5. build :=-f $(srctree)/scripts/Makefile.build obj
复制代码
       从示例代码31.3.13.3可以看出build=-f $(srctree)/scripts/Makefile.buildobj,经过前面的分析可知,变量srctree”.”,因此:
  1. build=-f ./scripts/Makefile.build obj
复制代码
       scripts_basic展开以后如下:
  1. scripts_basic:
  2.        @make-f ./scripts/Makefile.build obj=scripts/basic     //也可以没有@,视配置而定
  3.        @rm -f. tmp_quiet_recordmcount                                   //也可以没有@
复制代码
       scripts_basic会调用文件./scripts/Makefile.build,这个我们后面在分析。
       接着回到示例代码31.3.13.1中的%config处,内容如下:
  1. %config: scripts_basic outputmakefile FORCE
  2.        $(Q)$(MAKE)$(build)=scripts/kconfig $@
复制代码
       将命令展开就是:
  1. @make -f ./scripts/Makefile.build obj=scripts/kconfigxxx_defconfig
复制代码
       同样也跟文件./scripts/Makefile.build有关,我们后面再分析此文件。使用如下命令配置uboot,并观察其配置过程:
  1. make mx6ull_14x14_ddr512_emmc_defconfig V=1
复制代码
       配置过程如图31.3.13.1所示:
image062.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image062.gif
31.3.13.1 uboot配置过程
       从图31.3.13.1可以看出,我们的分析是正确的,接下来就要结合下面两行命令重点分析一下文件scripts/Makefile.build
       scripts_basic目标对应的命令
  1. @make -f ./scripts/Makefile.buildobj=scripts/basic
复制代码
       %config目标对应的命令
  1. @make -f ./scripts/Makefile.build obj=scripts/kconfigxxx_defconfig
复制代码
31.3.14 Makefile.build脚本分析
从上一小节可知,“makexxx_defconfig“配置uboot的时候如下两行命令会执行脚本scripts/Makefile.build:
  1. @make -f ./scripts/Makefile.buildobj=scripts/basic
  2. @make -f ./scripts/Makefile.build obj=scripts/kconfigxxx_defconfig
复制代码
依次来分析一下:
1、scripts_basic目标对应的命令
       scripts_basic目标对应的命令为:@make -f./scripts/Makefile.build obj=scripts/basic。打开文件scripts/Makefile.build,有如下代码:
示例代码31.3.14.1 Makefile.build代码段
  1. # Modified for U-Boot
  2. prefix := tpl
  3. src := $(patsubst $(prefix)/%,%,$(obj))
  4. ifeq ($(obj),$(src))
  5. prefix := spl
  6. src := $(patsubst $(prefix)/%,%,$(obj))
  7. ifeq ($(obj),$(src))
  8. prefix :=.
  9. endif
  10. endif
复制代码
       9行定义了变量prefix值为tpl
       10行定义了变量src,这里用到了函数patsubst,此行代码展开后为:
  1. $(patsubst tpl/%,%, scripts/basic)
复制代码
patsubst是替换函数,格式如下:
  1. $(patsubst<pattern>,<replacement>,<text>)
复制代码
       此函数用于在text中查找符合pattern的部分,如果匹配的话就用replacement替换掉。pattenr是可以包含通配符“%”,如果replacement中也包含通配符“%”,那么replacement中的这个“%”将是pattern中的那个“%”所代表的字符串。函数的返回值为替换后的字符串。因此,第10行就是在“scripts/basic”中查找符合“tpl/%”的部分,然后将“tpl/”取消掉,但是“scripts/basic”没有“tpl/”,所以src= scripts/basic
       11行判断变量objsrc是否相等,相等的话条件成立,很明显,此处条件成立。
       12行和第9行一样,只是这里处理的是“spl”,“scripts/basic”里面也没有“spl/”,所以src继续为scripts/basic
       15行因为变量objsrc相等,所以prefix=.
       继续分析scripts/Makefile.build,有如下代码:
示例代码31.3.14.2 Makefile.build代码段
  1. # The filename Kbuild has precedence over Makefile
  2. kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
  3. kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
  4. include $(kbuild-file)
复制代码
       kbuild-dir展开后为:
  1. $(if $(filter /%, scripts/basic),  scripts/basic, ./scripts/basic),
复制代码
因为没有以“/”为开头的单词,所以$(filter/%, scripts/basic)的结果为空,kbuild-dir=./scripts/basic。
       kbuild-file展开后为:
  1. $(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild,./scripts/basic/Makefile)
复制代码
       因为scrpts/basic目录中没有Kbuild这个文件,所以kbuild-file= ./scripts/basic/Makefile。最后将59行展开,即:
include./scripts/basic/Makefile
       也就是读取scripts/basic下面的Makefile文件。
       继续分析scripts/Makefile.build,如下代码:
示例代码31.3.14.3 Makefile.build代码段
  1. __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
  2.       $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
  3.       $(subdir-ym) $(always)
  4.      @:
复制代码
       __build是默认目标,因为命令“@make -f./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标:__build。在顶层Makefile中,KBUILD_BUILTIN1KBUILD_MODULES0,因此展开后目标__build为:
__build(builtin-target) $(lib-target)$(extra-y)) $(subdir-ym) $(always)
       @:
       可以看出目标__build5个依赖:builtin-targetlib-targetextra-ysubdir-ymalways。这5个依赖的具体内容我们就不通过源码来分析了,直接在scripts/Makefile.build中输入图31.3.14.1所示内容,将这5个变量的值打印出来:
image064.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image064.gif
31.3.14.1 输出变量
       执行如下命令:
  1. make mx6ull_14x14_ddr512_emmc_defconfig V=1
复制代码
       结果如图31.3.14.2所示:
image066.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image066.gif
31.3.14.2 输出结果
       从上图可以看出,只有always有效,因此__build最终为:
  1. __build: scripts/basic/fixdep
  2.        @:
复制代码
       __build依赖于scripts/basic/fixdep,所以要先scripts/basic/fixdep.c编译,生成fixdep,前面已经读取了scripts/basic/Makefile文件。
       综上所述,scripts_basic目标的作用就是编译出scripts/basic/fixdep这个软件。
2、%config目标对应的命令
       %config目标对应的命令为:@make -f./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig,各个变量值如下:
  1. src= scripts/kconfig
  2. kbuild-dir =./scripts/kconfig
  3. kbuild-file =./scripts/kconfig/Makefile
  4. include ./scripts/kconfig/Makefile
复制代码
可以看出,Makefilke.build会读取scripts/kconfig/Makefile中的内容,此文件有如下所示内容:
示例代码31.3.14.4 scripts/kconfig/Makefile代码段
  1. %_defconfig: $(obj)/conf
  2.      $(Q)[        DISCUZ_CODE_11686        ]lt; $(silent)--defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

  3. # Added for U-Boot (backwardcompatibility)
  4. %_config:%_defconfig
  5.      @:
复制代码
       目标%_defconfig刚好和我们输入的xxx_defconfig匹配,所以会执行这条规则。依赖为$(obj)/conf,展开后就是scripts/kconfig/conf。接下来就是检查并生成依赖scripts/kconfig/confconf是主机软件,到这里我们就打住,不要纠结conf是怎么编译出来的,否则就越陷越深,太绕了,像conf这种主机所使用的工具类软件我们一般不关心它是如何编译产生的。如果一定要看是conf是怎么生成的,可以输入如下命令重新配置uboot,在重新配置uboot的过程中就会输出conf编译信息。
  1. make distclean
  2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-mx6ull_14x14_ddr512_emmc_
  3. defconfig  V=1
复制代码
       结果如图31.3.14.3所示:
image068.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image068.gif
31.3.14.3 编译过程
       得到scripts/kconfig/conf以后就要执行目标%_defconfig的命令:
  1. $(Q)[        DISCUZ_CODE_11688        ]lt; $(silent)--defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
复制代码
       相关的变量值如下:
  1. silent=-s或为空
  2. SRCARCH=..
  3. Kconfig=Kconfig
复制代码
将其展开就是:
  1. @ scripts/kconfig/conf--defconfig=arch/../configs/xxx_defconfig Kconfig
复制代码
       上述命令用到了xxx_defconfig文件,比如mx6ull_alientek_emmc_defconfig。这里会将mx6ull_alientek_emmc_defconfig中的配置输出到.config文件中,最终生成uboot根目录下的.config文件。
       这个就是命令makexxx_defconfig执行流程,总结一下如图31.3.14.4所示:
image070.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image070.gif
31.3.14.4 makexxx_defconfig执行流程图
至此,makexxx_defconfig就分析完了,接下来就要分析一下u-boot.bin是怎么生成的了。
31.3.15 make过程
配置好uboot以后就可以直接make编译了,因为没有指明目标,所以会使用默认目标,主Makefile中的默认目标如下:
示例代码31.3.15.1 顶层Makefile代码段
  1. # That's ourdefault target when none is given on the command line
  2. PHONY := _all
  3. _all:
复制代码
       目标_all又依赖于all,如下所示:
示例代码31.3.15.2 顶层Makefile代码段
  1. 194 # If building an external module we donot care about the all: rule
  2. 195 # but instead _all depend on modules
  3. 196 PHONY += all
  4. 197 ifeq ($(KBUILD_EXTMOD),)
  5. 198 _all: all
  6. 199else
  7. 200 _all: modules
  8. 201 endif
复制代码
       如果KBUILD_EXTMOD为空的话_all依赖于all。这里不编译模块,所以KBUILD_EXTMOD肯定为空,_all的依赖就是all。在主Makefileall目标规则如下:
示例代码31.3.15.2 顶层Makefile代码段
  1. all:        $(ALL-y)
  2. ifneq ($(CONFIG_SYS_GENERIC_BOARD),y)
  3.      @echo "===================== WARNING ======================"
  4.      @echo "Please convert this board to generic board."
  5.      @echo "Otherwise it will be removed by the end of 2014."
  6.      @echo "See doc/README.generic-board for further information"
  7.      @echo "===================================================="
  8. endif
  9. ifeq ($(CONFIG_DM_I2C_COMPAT),y)
  10.      @echo "===================== WARNING ======================"
  11.      @echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
  12.      @echo "(possibly in a subsequent patch in your series)"
  13.      @echo "before sending patches to the mailing list."
  14.      @echo "===================================================="
  15. endif
  16.        从802行可以看出,all目标依赖$(ALL-y),而在顶层Makefile中,ALL-y如下:
复制代码
示例代码31.3.15.3 顶层Makefile代码段
  1. # Always appendALL so that arch config.mk's can add custom ones
  2. ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfgbinary_size_check

  3. ALL-$(CONFIG_ONENAND_U_BOOT)+= u-boot-onenand.bin
  4. ifeq ($(CONFIG_SPL_FSL_PBL),y)
  5. ALL-$(CONFIG_RAMBOOT_PBL)+= u-boot-with-spl-pbl.bin
  6. else
  7. ifneq ($(CONFIG_SECURE_BOOT), y)
  8. # For Secure BootThe Image needs to be signedand Header must also
  9. # be included. So The image hasto be built explicitly
  10. ALL-$(CONFIG_RAMBOOT_PBL)+= u-boot.pbl
  11. endif
  12. endif
  13. ALL-$(CONFIG_SPL)+= spl/u-boot-spl.bin
  14. ALL-$(CONFIG_SPL_FRAMEWORK)+= u-boot.img
  15. ALL-$(CONFIG_TPL)+= tpl/u-boot-tpl.bin
  16. ALL-$(CONFIG_OF_SEPARATE)+= u-boot.dtb
  17. ifeq ($(CONFIG_SPL_FRAMEWORK),y)
  18. ALL-$(CONFIG_OF_SEPARATE)+= u-boot-dtb.img
  19. endif
  20. ALL-$(CONFIG_OF_HOSTFILE)+= u-boot.dtb
  21. ifneq ($(CONFIG_SPL_TARGET),)
  22. ALL-$(CONFIG_SPL)+= $(CONFIG_SPL_TARGET:"%"=%)
  23. endif
  24. ALL-$(CONFIG_REMAKE_ELF)+= u-boot.elf
  25. ALL-$(CONFIG_EFI_APP)+= u-boot-app.efi
  26. ALL-$(CONFIG_EFI_STUB)+= u-boot-payload.efi

  27. ifneq ($(BUILD_ROM),)
  28. ALL-$(CONFIG_X86_RESET_VECTOR)+= u-boot.rom
  29. endif

  30. # enable combinedSPL/u-boot/dtb rules for tegra
  31. ifeq ($(CONFIG_TEGRA)$(CONFIG_SPL),yy)
  32. ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin
  33. ALL-$(CONFIG_OF_SEPARATE)+= u-boot-dtb-tegra.bin
  34. endif

  35. # Add optionalbuild target if defined in board/cpu/soc headers
  36. ifneq ($(CONFIG_BUILD_TARGET),)
  37. ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)
  38. endif
复制代码
从示例代码代码31.3.15.3可以看出,ALL-y包含u-boot.srec、u-boot.bin、u-boot.sym、System.map、u-boot.cfg 和binary_size_check这几个文件。根据uboot的配置情况也可能包含其他的文件,比如:
  1. ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin
复制代码
       CONFIG_ONENAND_U_BOOT就是uboot中跟ONENAND配置有关的,如果我们使能了ONENAND,那么在.config配置文件中就会有“CONFIG_ONENAND_U_BOOT=y”这一句。相当于CONFIG_ONENAND_U_BOOT是个变量,这个变量的值为“y”,所以展开以后就是:
  1. ALL-y += u-boot-onenand.bin
复制代码
       这个就是.config里面的配置参数的含义,这些参数其实都是变量,后面跟着变量值,会在顶层Makefile或者其他Makefile中调用这些变量。
       ALL-y里面有个u-boot.bin,这个就是我们最终需要的uboot二进制可执行文件,所作的所有工作就是为了它。在顶层Makefile中找到u-boot.bin目标对应的规则,如下所示:
示例代码31.3.15.4 顶层Makefile代码段
  1. ifeq ($(CONFIG_OF_SEPARATE),y)
  2. u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
  3.      $(call if_changed,cat)

  4. u-boot.bin: u-boot-dtb.bin FORCE
  5.      $(call if_changed,copy)
  6. else
  7. u-boot.bin: u-boot-nodtb.bin FORCE
  8.      $(call if_changed,copy)
  9. endif
复制代码
     825行判断CONFIG_OF_SEPARATE是否等于y,如果相等,那条件就成立,在.config中搜索“CONFIG_OF_SEPARAT”,没有找到,说明条件不成立。
       832行就是目标u-boot.bin的规则,目标u-boot.bin依赖于u-boot-nodtb.bin,命令为$(call if_changed,copy),这里调用了if_changedif_changed是一个函数,这个函数在scripts/Kbuild.include中有定义,而顶层Makefile中会包含scripts/Kbuild.include文件,这个前面已经说过了。
       if_changedKbuild.include中的定义如下:
示例代码31.3.15.5 Kbuild.include代码段
  1. ###
  2. # if_changed     - execute command if any prerequisite is newer than
  3. #                   target,or command line has changed
  4. # if_changed_dep - as if_changed, but uses fixdep toreveal
  5. #   dependencies including used config symbols
  6. # if_changed_rule - as if_changed butexecute rule instead
  7. # SeeDocumentation/kbuild/makefiles.txt for more info

  8. ifneq ($(KBUILD_NOCMDDEP),1)
  9. # Check if both arguments hassame arguments. Result is empty string if equal.
  10. # User may overridethis check using make KBUILD_NOCMDDEP=1
  11. arg-check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \
  12.                      $(filter-out $(cmd_$@),   $(cmd_$(1))))
  13. else
  14. arg-check = $(if $(strip $(cmd_$@)),,1)
  15. endif

  16. # Replace >[        DISCUZ_CODE_11698        ]lt; with >[        DISCUZ_CODE_76        ]lt; to preserve $ whenreloading the .cmd file
  17. # (needed for make)
  18. # Replace >#< with >\#< to avoid startinga comment in the .cmd file
  19. # (needed for make)
  20. # Replace >'< with >'\''< to be able toenclose the whole string in '...'
  21. # (needed for the shell)
  22. make-cmd = $(call escsq,$(subst \#,\\\#,$(subst $,$,$(cmd_$(1)))))

  23. # Find anyprerequisites that is newer than target or that does not exist.
  24. # PHONY targetsskipped in both cases.
  25. any-prereq = $(filter-out $(PHONY),$?) $(filter-out $(PHONY) $(wildcard $^),$^)

  26. # Execute command if command haschanged or prerequisite(s) are updated.
  27. #
  28. if_changed = $(if $(strip $(any-prereq) $(arg-check)), \   
  29.      @set -e;                                                    \
  30.      $(echo-cmd) $(cmd_$(1));                                  \
  31.      printf '%s\n''cmd_$@ := $(make-cmd)'> $(dot-target).cmd)
复制代码
       227行为 if_changed的描述,根据描述,在一些先决条件比目标新的时候,或者命令行有改变的时候,if_changed就会执行一些命令。
       257行就是函数if_changedif_changed函数引用的变量比较多,也比较绕,我们只需要知道它可以从u-boot-nodtb.bin生成u-boot.bin就行了。
       既然u-boot.bin依赖于u-boot-nodtb.bin,那么肯定要先生成u-boot-nodtb.bin文件,顶层Makefile中相关代码如下:
示例代码31.3.15.6 顶层Makefile代码段
  1. u-boot-nodtb.bin: u-boot FORCE
  2.      $(call if_changed,objcopy)
  3.      $(call DO_STATIC_RELA,[        DISCUZ_CODE_11699        ]lt;,$@,$(CONFIG_SYS_TEXT_BASE))
  4.      $(BOARD_SIZE_CHECK)
复制代码
       目标u-boot-nodtb.bin又依赖于u-boot,顶层Makefileu-boot相关规则如下:
示例代码31.3.15.7 顶层Makefile代码段
  1. u-boot:    $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
  2.     $(call if_changed,u-boot__)
  3. ifeq ($(CONFIG_KALLSYMS),y)
  4.     $(call cmd,smap)
  5.     $(call cmd,u-boot__) common/system_map.o
  6. endif
复制代码
       目标u-boot依赖于u-boot_initu-boot-mainu-boot.ldsu-boot_initu-boot-main是两个变量,在顶层Makefile中有定义,值如下:
示例代码31.3.15.8 顶层Makefile代码段
  1. u-boot-init := $(head-y)
  2. u-boot-main := $(libs-y)
复制代码
       $(head-y)CPU架构有关,我们使用的是ARM芯片,所以head-yarch/arm/Makefile中被指定为:
  1. head-y := arch/arm/cpu/$(CPU)/start.o
复制代码
       根据31.3.12小节的分析,我们知道CPU=armv7,因此head-y展开以后就是:
  1. head-y := arch/arm/cpu/armv7/start.o
复制代码
       因此:
  1. u-boot-init= arch/arm/cpu/armv7/start.o
复制代码
       $(libs-y)在顶层Makefile中被定义为uboot所有子目录下build-in.o的集合,代码如下:
示例代码31.3.15.9 顶层Makefile代码段
  1. libs-y += lib/
  2. libs-$(HAVE_VENDOR_COMMON_LIB)+= board/$(VENDOR)/common/
  3. libs-$(CONFIG_OF_EMBED)+= dts/
  4. libs-y += fs/
  5. libs-y += net/
  6. libs-y += disk/
  7. libs-y += drivers/
  8. libs-y += drivers/dma/
  9. libs-y += drivers/gpio/
  10. libs-y += drivers/i2c/
  11. ...
  12. libs-y += cmd/
  13. libs-y += common/
  14. libs-$(CONFIG_API)+= api/
  15. libs-$(CONFIG_HAS_POST)+= post/
  16. libs-y += test/
  17. libs-y += test/dm/
  18. libs-$(CONFIG_UT_ENV)+= test/env/

  19. libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)

  20. libs-y := $(sort $(libs-y))

  21. u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples

  22. u-boot-alldirs  := $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-))))

  23. libs-y      := $(patsubst %/, %/built-in.o, $(libs-y))
复制代码
       从上面的代码可以看出,libs-y都是uboot各子目录的集合,最后:
  1. libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
复制代码
       这里调用了函数patsubst,将libs-y中的“/”替换为”/built-in.o”,比如“drivers/dma/”就变为了“drivers/dma/built-in.o”,相当于将libs-y改为所有子目录中built-in.o文件的集合。那么u-boot-main就等于所有子目录中built-in.o的集合。  
       这个规则就相当于将以u-boot.lds为链接脚本,将arch/arm/cpu/armv7/start.o和各个子目录下的built-in.o链接在一起生成u-boot
       u-boot.lds的规则如下:
示例代码31.3.15.10 顶层Makefile代码段
  1. u-boot.lds: $(LDSCRIPT) prepare FORCE
  2.     $(call if_changed_dep,cpp_lds)
复制代码
       接下来的重点就是各子目录下的built-in.o是怎么生成的,以drivers/gpio/built-in.o为例,在drivers/gpio/目录下会有个名为.built-in.o.cmd的文件,此文件内容如下:
示例代码31.3.15.11 drivers/gpio/.built-in.o.cmd代码
  1. 1 cmd_drivers/gpio/built-in.o :=  arm-linux-gnueabihf-ld.bfd    -r -o drivers/gpio/built-in.o drivers/gpio/mxc_gpio.o
复制代码
       从命令“cmd_drivers/gpio/built-in.o”可以看出,drivers/gpio/built-in.o这个文件是使用ld命令由文件drivers/gpio/mxc_gpio.o生成而来的,mxc_gpio.omxc_gpio.c编译生成的.o文件,这个是NXPI.MX系列的GPIO驱动文件。这里用到了ld的“-r”参数,参数含义如下:
       -r –relocateable: 产生可重定向的输出,比如,产生一个输出文件它可再次作为‘ld’的输入,这经常被叫做“部分链接”,当我们需要将几个小的.o文件链接成为一个.o文件的时候,需要使用此选项。
       最终将各个子目录中的built-in.o文件链接在一起就形成了u-boot,使用如下命令编译uboot就可以看到链接的过程:
  1. make      ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-mx6ull_14x14_ddr512_emmc_
  2. defconfigV=1
  3. make      ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-V=1
复制代码
       编译的时候会有如图31.3.15.1所示内容输出:
image072.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image072.jpg
31.3.15.1 编译内容输出
       将其整理一下,内容如下:
  1. arm-linux-gnueabihf-ld.bfd   -pie --gc-sections -Bstatic -Ttext 0x87800000 \
  2. -o u-boot -T u-boot.lds \
  3. arch/arm/cpu/armv7/start.o \
  4. --start-group arch/arm/cpu/built-in.o \
  5. arch/arm/cpu/armv7/built-in.o \
  6. arch/arm/imx-common/built-in.o \
  7. arch/arm/lib/built-in.o \
  8. board/freescale/common/built-in.o \
  9. board/freescale/mx6ull_alientek_emmc/built-in.o \
  10. cmd/built-in.o \
  11. common/built-in.o \
  12. disk/built-in.o \
  13. drivers/built-in.o \
  14. drivers/dma/built-in.o  \
  15. drivers/gpio/built-in.o  \
  16. ……
  17. drivers/spi/built-in.o  \
  18. drivers/usb/dwc3/built-in.o  \
  19. drivers/usb/emul/built-in.o  \
  20. drivers/usb/eth/built-in.o  \
  21. drivers/usb/gadget/built-in.o  \
  22. drivers/usb/gadget/udc/built-in.o  \
  23. drivers/usb/host/built-in.o  \
  24. drivers/usb/musb-new/built-in.o  \
  25. drivers/usb/musb/built-in.o  \
  26. drivers/usb/phy/built-in.o  \
  27. drivers/usb/ulpi/built-in.o  \
  28. fs/built-in.o \
  29. lib/built-in.o \
  30. net/built-in.o \
  31. test/built-in.o \
  32. test/dm/built-in.o \
  33. --end-group arch/arm/lib/eabi_compat.o  \
  34. -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4-lgcc -Map u-boot.map
复制代码

       可以看出最终是用arm-linux-gnueabihf-ld.bfd命令将arch/arm/cpu/armv7/start.o和其他众多的built_in.o链接在一起,形成u-boot
目标all除了u-boot.bin以外还有其他的依赖,比如u-boot.srec 、u-boot.sym 、System.map、u-boot.cfg和binary_size_check等等,这些依赖的生成方法和u-boot.bin很类似,大家自行查看一下顶层Makefile,我们就不详细的讲解了。
总结一下“make”命令的流程,如图31.3.15.2所示:
image074.gif
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image074.gif
31.3.15.2 make命令流程
       31.3.15.2就是“make”命令的执行流程,关于uboot的顶层Makefile就分析到这里,重点是“makexxx_defconfig”和“make”这两个命令的执行流程:
       makexxx_defconfig用于配置uboot,这个命令最主要的目的就是生成.config文件。
       make:用于编译uboot,这个命令的主要工作就是生成二进制的u-boot.bin文件和其他的一些与uboot有关的文件,比如u-boot.imx等等。
       关于uboot的顶层Makefile就分析到这里,有些内容我们没有详细、深入的去研究,因为我们的重点是使用uboot,而不是uboot的研究者,我们要做的是缕清uboot的流程。至于更具体的实现,有兴趣的可以参考一下其他资料。



正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 03:19

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表