完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:正点原子Linux开发板
2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》 关注官方微信号公众号,获取更多资料:正点原子 第三十一章U-Boot顶层Makefile详解 上一章我们详细的讲解了uboot的使用方法,其实就是各种命令的使用,学会uboot使用以后就可以尝试移植uboot到自己的开发板上了,但是在移植之前需要我们得先分析一遍uboot的启动流程源码,得捋一下uboot的启动流程,否则移植的时候都不知道该修改那些文件。本章我们就来分析一下正点原子提供的uboot源码,重点是分析uboot启动流程,而不是整个uboot源码,uboot整个源码非常大,我们只看跟我们关心的部分即可。 31.1 U-Boot工程目录分析本书我们以EMMC版本的核心板为例讲解,为了方便,uboot启动源码分析就在Windows下进行,将正点原子提供的uboot源码进行解压,解压完成以后的目录如图31.1.1所示: 图31.1.1未编译的uboot 图31.1.1是正点原子提供的未编译的uboot源码目录,我们在分析uboot源码之前一定要先在Ubuntu中编译一下uboot源码,因为编译过程会生成一些文件,而生成的这些恰恰是分析uboot源码不可或缺的文件。使用上一章创建的shell脚本来完成编译工作,命令如下: cd alientek_uboot //进入正点原子uboot源码目录 ./mx6ull_alientek_emmc.sh //编译uboot cd../ //返回上一级目录 tar -vcjf alientek_uboot.tar.bz2 alientek_uboot //压缩 最终会生成一个名为alientek_uboot.tar.bz2的压缩包,将alientek_uboot.tar.bz2拷贝到windows系统中并解压,解压后的目录如图31.1.2所示: 图31.1.2编译后的uboot源码文件 对比图31.1.2和图31.1.1,可以看出编译后的uboot要比没编译之前多了好多文件,这些文件夹或文件的含义见表31.1.1所示: 表31.1.1uboot目录列表 表31.1.1中的很多文件夹和文件我们都不需要去关心,我们要关注的文件夹或文件如下: 1、arch文件夹 这个文件夹里面存放着和架构有关的文件,如图31.1.3所示: 图31.1.3arch文件夹 从图31.1.3可以看出有很多架构,比如arm、avr32、m68k等,我们现在用的是ARM芯片,所以只需要关心arm文件夹即可,打开arm文件夹里面内容如图31.1.4所示: 图31.1.4arm文件夹 图31.1.4只截取了一部分,还有一部分mach-xxx的文件夹。mach开头的文件夹是跟具体的设备有关的,比如“mach-exynos”就是跟三星的exyons系列CPU有关的文件。我们使用的是I.MX6ULL,所以要关注“imx-common”这个文件夹。另外“cpu”这个文件夹也是和cpu架构有关的,打开以后如图31.1.5所示: 图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所示: 图31.1.6freescale文件夹 所有使用freescale芯片的板子都放到此文件夹中,I.MX系列以前属于freescale,只是freescale后来被NXP收购了。打开此freescale文件夹,在里面找到和mx6u(I.MX6UL/ULL)有关的文件夹,如图31.1.7所示: 图31.1.7mx6u相关板子 图31.1.7中有5个文件夹,这5个文件夹对应5种板子,以“mx6ul”开头的表示使用I.MX6UL芯片的板子,以mx6ull开头的表示使用I.MX6ULL芯片的板子。mx6ullevk是NXP官方的I.MX6ULL开发板,正点原子的ALPHA开发板就是在这个基础上开发的,因此mx6ullevk也是正点原子的开发板。我们后面移植uboot到时候就是参考的NXP官方的开发板,也就是要参考mx6ullevk这个文件夹来定义我们的板子。 3、configs文件夹 此文件夹为uboot配置文件,uboot是可配置的,但是你要是自己从头开始一个一个项目的配置,那就太麻烦了,因此一般半导体或者开发板厂商都会制作好一个配置文件。我们可以在这个做好的配置文件基础上来添加自己想要的功能,这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”,xxx表示开发板名字,这些defconfig文件都存放在configs文件夹,因此,NXP官方开发板和正点原子的开发板配置文件肯定也在这个文件夹中,如图31.1.8所示: 图31.1.8正点原子开发板配置文件 图31.1.8中这6个文件就是正点原子I.MX6U-ALPHA开发板所对应的uboot默认配置文件。我们只关心mx6ull_14x14_ddr512_emmc_defconfig和mx6ull_14x14_ddr256_nand_defconfig这两根文件,分别是正点原子I.MX6ULL EMMC核心板和NAND核心板的配置文件。使用“makexxx_defconfig”命令即可配置uboot,比如: 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.bin u-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-boot u-boot-nodtb.bin 这里用到了arm-linux-gnueabihf-objcopy,使用objcopy将ELF格式的u-boot文件转换为二进制的u-boot-nodtb.bin文件。 文件u-boot是ELF格式的文件,文件.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 -o u-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/u***/dwc3/built-in.o drivers/u***/emul/built-in.o drivers/u***/eth/built-in.o drivers/u***/gadget/built-in.o drivers/u***/gadget/udc/built-in.o drivers/u***/host/built-in.o drivers/u***/mu***-new/built-in.o drivers/u***/mu***/built-in.o drivers/u***/phy/built-in.o drivers/u***/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-group arch/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文件的头部添加了IVT、DCD等信息。这个工作是由文件.u-boot.imx.cmd来完成的,此文件内容如下: 示例代码31.1.4 .u-boot.imx.cmd代码 1 cmd_u-boot.imx:= ./tools/mkimage -n board/freescale/mx6ull_alientek_emmc/imximage.cfg.cfgtmp -T imximage -e 0x87800000 -d u-boot.bin u-boot.imx 可以看出,这里用到了工具tools/mkimage,而IVT、DCD等数据保存在了文件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-boot、u-boot.bin、u-boot.cfg、u-boot.imx、u-boot.lds、u-boot.map、u-boot.srec、u-boot.sym和u-boot-nodtb.bin,这些文件的含义如下: u-boot:编译出来的ELF格式的uboot镜像文件。 u-boot.bin:编译出来的二进制格式的uboot可执行镜像文件。 u-boot.cfg:uboot的另外一种配置文件。 u-boot.imx:u-boot.bin添加头部信息以后的文件,NXP的CPU专用文件。 u-boot.lds:链接脚本。 u-boot.map:uboot映射文件,通过查看此文件可以知道某个函数被链接到了哪个地址上。 u-boot.srec:S-Record格式的镜像文件。 u-boot.sym:uboot符号文件。 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 32 # 33 # Boot commands 34 # 35 CONFIG_CMD_BOOTD=y 36 CONFIG_CMD_BOOTM=y 37 CONFIG_CMD_ELF=y 38 CONFIG_CMD_GO=y 39 CONFIG_CMD_RUN=y 40 CONFIG_CMD_IMI=y 41 CONFIG_CMD_IMLS=y 42 CONFIG_CMD_XIMG=y 43 44 # 45 # Environment commands 46 # 47 CONFIG_CMD_EXPORTENV=y 48 CONFIG_CMD_IMPORTENV=y 49 CONFIG_CMD_EDITENV=y 50 CONFIG_CMD_SAVEENV=y 51 CONFIG_CMD_ENV_EXISTS=y 52 53...... 54 55 # 56 # Library routines 57 # 58 # CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED is not set 59 CONFIG_HAVE_PRIVATE_LIBGCC=y 60 # CONFIG_USE_PRIVATE_LIBGCC is not set 61 CONFIG_SYS_HZ=1000 62 # CONFIG_USE_TINY_PRINTF is not set 63 CONFIG_REGEX=y 可以看出.config文件中都是以“CONFIG_”开始的配置项,这些配置项就是Makefile中的变量,因此后面都跟有相应的值,uboot的顶层Makefile或子Makefile会调用这些变量值。在.config中会有大量的变量值为‘y’,这些为‘y’的变量一般用于控制某项功能是否使能,为‘y’的话就表示功能使能,比如: CONFIG_CMD_BOOTD=y 如果使能了bootd这个命令的话,CONFIG_CMD_BOOTM就为‘y’。在cmd/Makefile中有如下代码: 示例代码31.1.6 cmd/Makefile代码 1 ifndef CONFIG_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中,有如下所示一行代码: obj-$(CONFIG_CMD_BOOTM) += bootm.o CONFIG_CMD_BOOTM=y,将其展开就是: obj-y += bootm.o 也就是给obj-y追加了一个“bootm.o”,obj-y包含着所有要编译的文件对应的.o文件,这里表示需要编译文件cmd/bootm.c。相当于通过“CONFIG_CMD_BOOTD=y”来使能bootm这个命令,进而编译cmd/bootm.c这个文件,这个文件实现了命令bootm。在uboot和Linux内核中都是采用这种方法来选择使能某个功能,编译对应的源码文件。 8、README README文件描述了uboot的详细信息,包括uboot该如何编译、uboot中各文件夹的含义、相应的命令等等。建议大家详细的阅读此文件,可以进一步增加对uboot的认识。 关于uboot根目录中的文件和文件夹的含义就讲解到这里,接下来就要开始分析uboot的启动流程了。 31.2 VScode工程创建先在Ubuntu下编译一下uboot,然后将编译后的uboot文件夹复制到windows下,并创建VScode工程。打开VScode,选择:文件->打开文件夹…,选中uboot文件夹,如图31.2.1所示: 图31.2.1选择uboot源码文件夹 打开uboot目录以后,VSCode界面如图31.2.2所示: 图31.2.2 VScode界面 点击“文件->将工作区另存为…”,打开保存工作区对话框,将工作区保存到uboot源码根目录下,设置文件名为“uboot”,如图31.2.3所示: 图31.2.3保存工作区 保存成功以后就会在uboot源码根目录下存在一个名为uboot.code-workspace的文件。这样一个完整的VSCode工程就建立起来了。但是这个VSCode工程包含了uboot的所有文件, uboot中有些文件是不需要的,比如arch目录下是各种架构的文件夹,如图31.2.4所示: 图31.2.4arch目录 在arch目录下,我们只需要arm文件夹,所以需要将其它的目录从VSCode中给屏蔽掉,比如将arch/avr32这个目录给屏蔽掉。 在VSCode上建名为“.vscode”的文件夹,如图31.2.5所示: 图31.2.5新建.vscode文件夹 输入新建文件夹的名字,完成以后如图31.2.6所示。 图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所示: 图31.2.7 settings.json文件默认内容 其中"search.exclude"里面是需要在搜索结果中排除的文件或者文件夹,"files.exclude"是左侧工程目录中需要排除的文件或者文件夹。我们需要将arch/avr32文件夹下的所有文件从搜索结果和左侧的工程目录中都排除掉,因此在"search.exclude"和"files.exclude"中输入如图31.2.8所示内容: 图31.2.8 排除arch/avr32目录 此时再看一下左侧的工程目录,发现arch目录下没有avr32这个文件夹了,说明avr32这个文件夹被排除掉了,如图31.2.9所示: 图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, 22"arch/arm/cpu/armv7m":true, 23"arch/arm/cpu/armv8":true, 24"arch/arm/cpu/pxa":true, 25"arch/arm/cpu/sa1100":true, 26"board/[a-e]*":true, 27"board/[g-z]*":true, 28"board/[0-9]*":true, 29"board/[A-Z]*":true, 30"board/fir*":true, 31"board/freescale/b*":true, 32"board/freescale/l*":true, 33"board/freescale/m5*":true, 34"board/freescale/mp*":true, 35"board/freescale/c29*":true, 36"board/freescale/cor*":true, 37"board/freescale/mx7*":true, 38"board/freescale/mx2*":true, 39"board/freescale/mx3*":true, 40"board/freescale/mx5*":true, 41"board/freescale/p*":true, 42"board/freescale/q*":true, 43"board/freescale/t*":true, 44"board/freescale/v*":true, 45"configs/[a-l]*":true, 46"configs/[n-z]*":true, 47"configs/[A-Z]*":true, 48"configs/M[a-z]*":true, 49"configs/M[A-Z]*":true, 50"configs/M[0-9]*":true, 51"configs/m[a-w]*":true, 52"configs/m[0-9]*":true, 53"configs/[0-9]*":true, 54"include/configs/[a-l]*":true, 55"include/configs/[n-z]*":true, 56 "include/configs/[A-Z]*":true, 57"include/configs/m[a-w]*":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代码 5 VERSION =2016 6 PATCHLEVEL =03 7 SUBLEVEL = 8 EXTRAVERSION = 9 NAME = VERSION是主版本号,PATCHLEVEL是补丁版本号,SUBLEVEL是次版本号,这三个一起构成了uboot的版本号,比如当前的uboot版本号就是“2016.03”。EXTRAVERSION是附加版本信息,NAME是和名字有关的,一般不使用这两个。 31.3.2 MAKEFLAGS变量make是支持递归调用的,也就是在Makefile中使用“make”命令来执行其他的Makefile文件,一般都是子目录中的Makefile文件。假如在当前目录下存在一个“subdir”子目录,这个子目录中又有其对应的Makefile文件,那么这个工程在编译的时候其主目录中的Makefile就可以调用子目录中的Makefile,以此来完成所有子目录的编译。主目录的Makefile可以使用如下代码来编译这个子目录: $(MAKE) -C subdir $(MAKE)就是调用“make”命令,-C指定子目录。有时候我们需要向子make传递变量,这个时候使用“export”来导出要传递给子make的变量即可,如果不希望哪个变量传递给子make的话就使用“unexport”来声明不导出: export VARIABLE …… //导出变量给子make 。 unexport VARIABLE…… //不导出变量给子make。 有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。在uboot的主Makefile中有如下代码: 示例代码31.3.2.1 顶层Makefile代码 20 MAKEFLAGS +=-rR --include-dir=$(CURDIR) 上述代码使用“+=”来给变量MAKEFLAGS追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“--include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。 31.3.3 命令输出uboot默认编译是不会在终端中显示完整的命令,都是短命令,如图31.3.3所示: 图31.3.3.1 终端短命令输出 在终端中输出短命令虽然看起来很清爽,但是不利于分析uboot的编译过程。可以通过设置变量“V=1“来实现完整的命令输出,这个在调试uboot的时候很有用,结果如图31.3.3.2所示: 图31.3.3.2 终端完整命令输出 顶层Makefile中控制命令输出的代码如下: 示例代码31.3.3.1 顶层Makefile代码 73 ifeq ("$(origin V)","command line") 74 KBUILD_VERBOSE = $(V) 75 endif 76 ifndef KBUILD_VERBOSE 77 KBUILD_VERBOSE =0 78 endif 79 80 ifeq ($(KBUILD_VERBOSE),1) 81 quiet = 82 Q = 83else 84 quiet=quiet_ 85 Q = @ 86 endif 上述代码中先使用ifeq来判断"$(origin V)"和"command line"是否相等。这里用到了Makefile中的函数origin,origin和其他的函数不一样,它不操作变量的值,origin用于告诉你变量是哪来的,语法为: $(origin variable是变量名,o函数的返回值就是变量来源,因此$(origin V)就是变量V的来源。如果变量V是在命令行定义的那么它的来源就是"command line",这样"$(origin V)"和"command line"就相等了。当这两个相等的时候变量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的话: KBUILD_VERBOSE=1 quiet= 空。 Q= 空。 V=0或者命令行不定义V的话: KBUILD_VERBOSE=0 quiet= quiet_。 Q= @。 Makefile中会用到变量quiet和Q来控制编译的时候是否在终端输出完整的命令,在顶层Makefile中有很多如下所示的命令: $(Q)$(MAKE) $(build)=tools 如果V=0的话上述命令展开就是“@make$(build)=tools”,make在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。当V=1的时候Q就为空,上述命令就是“make$(build)=tools”,因此在make执行的过程,命令会被完整的输出在终端上。 有些命令会有两个版本,比如: quiet_cmd_sym ?= SYM $@ cmd_sym ?= $(OBJDUMP) -t $<> $@ 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代码 88 # If the user is running make -s (silent mode), suppress echoing of 89 # commands 90 91 ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 92 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) 93 quiet=silent_ 94 endif 95else # make-3.8x 96 ifneq ($(filter s%-s%,$(MAKEFLAGS)),) 97 quiet=silent_ 98 endif 99 endif 100 101 export quiet Q KBUILD_VERBOSE 第91行判断当前正在使用的编译器版本号是否为4.x,判断$(filter 4.%,$(MAKE_VERSION))和“”(空)是否相等,如果不相等的话就成立,执行里面的语句。也就是说$(filter 4.%,$(MAKE_VERSION))不为空的话条件就成立,这里用到了Makefile中的filter函数,这是个过滤函数,函数格式如下: $(filter 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 ,$(firstword x$(MAKEFLAGS)))不为空的话条件成立,变量quiet等于“silent_”。这里也用到了函数filter,在$(firstword x$(MAKEFLAGS)))中过滤出符合“%s”的单词。到了函数firstword,函数firstword是获取首单词,函数格式如下: $(firstword firstword函数用于取出text字符串中的第一个单词,函数的返回值就是获取到的单词。当使用“make-s”编译的时候,“-s”会作为MAKEFLAGS变量的一部分传递给Makefile。在顶层Makfile中添加如图31.3.4.1所示的代码: 图31.3.4.1 顶层Makefile添加代码 图31.3.4.1中的两行代码用于输出$(firstword x$(MAKEFLAGS))的结果,最后修改文件mx6ull_alientek_emmc.sh,在里面加入“-s”选项,结果如图31.3.4.2所示: 图31.3.4.2 加入-s选项 修改完成以后执行mx6ull_alientek_emmc.sh,结果如图31.3.4.3所示: 图31.3.4.3 修改顶层Makefile后的执行结果 从图31.3.4.3可以看出第一个单词是“xrRs”,将$(filter %s ,$(firstword x$(MAKEFLAGS)))展开就是$(filter %s, xrRs),而$(filter %s, xrRs)的返回值肯定不为空,条件成立,quiet=silent_。 第101行 使用export导出变量quiet、Q和KBUILD_VERBOSE。 31.3.5 设置编译结果输出目录uboot可以将编译出来的目标文件输出到单独的目录中,在make的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到out目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定O参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定O参数。顶层Makefile中相关的代码如下: 示例代码31.3.5.1 顶层Makefile代码 103 # kbuild supports saving output files in a separate directory. 104 # To locate output files in a separate directory two syntaxes are supported. 105 # In both cases the working directory must be the root of the kernel src. 106 # 1) O= 107 # Use "make O=dir/to/store/output/files/" 108 # 109 # 2) Set KBUILD_OUTPUT 110 # Set the environment variable KBUILD_OUTPUT to point to the directory 111 # where the output files shall be placed. 112 # export KBUILD_OUTPUT=dir/to/store/output/files/ 113 # make 114 # 115 # The O= assignment takes precedence over the KBUILD_OUTPUT environment 116 # variable. 117 118 # KBUILD_SRC is set on invocation of make in OBJ directory 119 # KBUILD_SRC is not intended to be used by the regular user (for now) 120 ifeq ($(KBUILD_SRC),) 121 122 # OK, Make called in directory where kernel src resides 123 # Do we want to locate output files in a separate directory? 124 ifeq ("$(origin O)","command line") 125 KBUILD_OUTPUT := $(O) 126 endif 127 128 # That's our default target when none is given on the command line 129 PHONY := _all 130 _all: 131 132 # Cancel implicit rules on top Makefile 133 $(CURDIR)/Makefile Makefile:; 134 135 ifneq ($(KBUILD_OUTPUT),) 136 # Invoke a second make in the output directory, passing relevant variables 137 # check that the output directory actually exists 138 saved-output := $(KBUILD_OUTPUT) 139 KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT)&& cd $(KBUILD_OUTPUT) 140&&/bin/pwd) ...... 155 endif # ifneq ($(KBUILD_OUTPUT),) 156 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代码 166 # Call a source code checker (by default,"sparse") as part of the 167 # C compilation. 168 # 169 # Use 'make C=1' to enable checking of only re-compiled files. 170 # Use 'make C=2' to enable checking of *all* source files, regardless 171 # of whether they are re-compiled or not. 172 # 173 # See the file "Documentation/sparse.txt"for more details, including 174 # where to get the "sparse" utility. 175 176 ifeq ("$(origin C)","command line") 177 KBUILD_CHECKSRC = $(C) 178 endif 179 ifndef KBUILD_CHECKSRC 180 KBUILD_CHECKSRC =0 181 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代码 183 # Use make M=dir to specify directory of external module to build 184 # Old syntax make ... SUBDIRS=$PWD is still supported 185 # Setting the environment variable KBUILD_EXTMOD take precedence 186 ifdef SUBDIRS 187 KBUILD_EXTMOD ?= $(SUBDIRS) 188 endif 189 190 ifeq ("$(origin M)","command line") 191 KBUILD_EXTMOD := $(M) 192 endif 193 194 # If building an external module we donot care about the all: rule 195 # but instead _all depend on modules 196 PHONY += all 197 ifeq ($(KBUILD_EXTMOD),) 198 _all: all 199else 200 _all: modules 201 endif 202 203 ifeq ($(KBUILD_SRC),) 204 # building in the source tree 205 srctree :=. 206else 207 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR))) 208 # building in a subdirectory of the source tree 209 srctree :=.. 210else 211 srctree := $(KBUILD_SRC) 212 endif 213 endif 214 objtree :=. 215 src := $(srctree) 216 obj := $(objtree) 217 218 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) 219 220export 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为当前目录。 第215和216行分别设置变量src和obj,都为当前目录。 第218行设置VPATH。 第220行导出变量scrtree、objtree和VPATH。 31.3.8 获取主机架构和系统接下来顶层Makefile会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下: 示例代码31.3.8.1 顶层Makefile代码 227 HOSTARCH := $(shell uname -m | 228 sed -e s/i.86/x86/ 229-e s/sun4u/sparc64/ 230 -e s/arm.*/arm/ 231-e s/sa110/arm/ 232-e s/ppc64/powerpc/ 233-e s/ppc/powerpc/ 234-e s/macppc/powerpc/ 235 -e s/sh.*/sh/) 236 237 HOSTOS := $(shell uname -s | tr '[:upper:]''[:lower:]'| 238 sed -e 's/(cygwin).*/cygwin/') 239 240export HOSTARCH HOSTOS 第227行定义了一个变量HOSTARCH,用于保存主机架构,这里调用shell命令“uname-m”获取架构名称,结果如图31.3.8.1所示: 图31.3.8.1 uname-m命令 从图31.3.8.1可以看出当前电脑主机架构为“x86_64”,shell中的“|”表示管道,意思是将左边的输出作为右边的输入,sed -e是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”,其他的“sed -s”命令同理。对于我的电脑而言,HOSTARCH=x86_64。 第237行定义了变量HOSTOS,此变量用于保存主机OS的值,先使用shell命令“name-s”来获取主机OS,结果如图31.3.8.2所示: 图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_64,HOSTOS=linux。 31.3.9 设置目标架构、交叉编译器和配置文件编译uboot的时候需要设置目标板架构和交叉编译器,“make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置ARCH和CROSS_COMPILE,在顶层Makefile中代码如下: 示例代码31.3.9.1 顶层Makefile代码 244 # set default to nothing for native builds 245 ifeq ($(HOSTARCH),$(ARCH)) 246 CROSS_COMPILE ?= 247 endif 248 249 KCONFIG_CONFIG ?=.config 250export KCONFIG_CONFIG 第245行判断HOSTARCH和ARCH这两个变量是否相等,主机架构(变量HOSTARCH)是x86_64,而我们编译的是ARM版本uboot,肯定不相等,所以CROS_COMPILE= arm-linux-gnueabihf-。从示例代码31.3.9.1可以看出,每次编译uboot的时候都要在make命令后面设置ARCH和CROS_COMPILE,使用起来很麻烦,可以直接修改顶层Makefile,在里面加入ARCH和CROSS_COMPILE的定义,如图31.3.9.1所示: 图31.3.9.1 定义ARCH和CROSS_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代码 327 # We need some generic definitions (donottry to remake the file). 328 scripts/Kbuild.include:; 329 include scripts/Kbuild.include 示例代码31.3.10.1中使用“include”包含了文件scripts/Kbuild.include,此文件里面定义了很多变量,如图31.3.10.1所示: 图31.3.10.1 Kbuild.include文件 在uboot的编译过程中会用到scripts/Kbuild.include中的这些变量,后面用到的时候再分析。 |
|
相关推荐
|
|
2105 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
1941 浏览 3 评论
4537 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
2084 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
2599 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 02:20 , Processed in 0.599840 second(s), Total 36, Slave 28 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号