U-Boot顶层Makefile详解

65 篇文章 12 订阅

一、U_Boot工程目录分析

我们以 EMMC 版本的核心板为例讲解,为了方便,uboot 启动源码分析就在 Windows下进行。

1.打包编译好的uboot

将编译后的uboot进行打包

tar -vcf aef_uboot.tar.bz2 aef_uboot

在这里插入图片描述

拷贝到windows下并解压
在这里插入图片描述

2.目录介绍

解压后的uboot源码目录介绍如下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

很多文件夹和文件我们都不需要去关心。

1.arch

这个文件夹里面存放着和架构有关的文件

在这里插入图片描述
可以看出有很多架构,比如 arm、avr32、m68k 等,我们现在用的是 ARM 芯片,所以只需要关心 arm 文件夹即可,打开 arm 文件夹里面内容如图所示:
在这里插入图片描述
只截取了一部分,还有一部分 mach-xxx 的文件夹。mach 开头的文件夹是跟具体的设备有关的,比如“mach-exynos”就是跟三星的 exyons 系列 CPU 有关的文件。我们使用的是 I.MX6ULL,所以要关注“imx-common”这个文件夹。另外“cpu”这个文件夹也是和 cpu 架构有关的,打开以后如图所示:
在这里插入图片描述
可以看出有多种 ARM 架构相关的文件夹,I.MX6ULL 使用的 Cortex-A7 内核,Cortex-A7 属于 armv7,所以我们要关心“armv7”这个文件夹。cpu 文件夹里面有个名为“u-boot.lds”的链接脚本文件,这个就是 ARM 芯片所使用的 u-boot 链接脚本文件!armv7 这个文件夹里面的文件都是跟 ARMV7 架构有关的,是我们分析 uboot 启动源码的时候需要重点关注的。

2.board

board 文件夹就是和具体的板子有关的,打开此文件夹,里面全是不同的板子,毫无疑问正点原子的开发板肯定也在里面(正点原子添加的),borad 文件夹里面有个名为“freescale”的文件夹,如图所示:
在这里插入图片描述
所有使用 freescale 芯片的板子都放到此文件夹中,I.MX 系列以前属于 freescale,只是freescale 后来被 NXP 收购了。打开此 freescale 文件夹,在里面找到和 mx6u(I.MX6UL/ULL)有关的文件夹,如图 所示

在这里插入图片描述
有 5 个文件夹,这 5 个文件夹对应 5 种板子,以“mx6ul”开头的表示使用
I.MX6UL 芯片的板子,以 mx6ull 开头的表示使用 I.MX6ULL 芯片的板子。mx6ullevk 是 NXP官方的I.MX6ULL开发板,正点原子的ALPHA开发板就是在这个基础上开发的,因此mx6ullevk是正点原子的开发板

3.configs

此文件夹为 uboot 配置文件,uboot 是可配置的,但是你要是自己从头开始一个一个项目的配置,那就太麻烦了,因此一般半导体或者开发板厂商都会制作好一个配置文件。我们可以在这个做好的配置文件基础上来添加自己想要的功能,这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”,xxx 表示开发板名字,这些 defconfig 文件都存放在 configs文件夹,因此,NXP 官方开发板和正点原子的开发板配置文件肯定也在这个文件夹中,如图
在这里插入图片描述
这 6 个文件就是正点原子 I.MX6U-ALPHA 开发板所对应的 uboot 默认配置文件。
我们只关心 mx6ull_14x14_ddr512_emmc_defconfig

mx6ull_14x14_ddr256_nand_defconfig
这两个文件,分别是正点原子 I.MX6ULL EMMC 核心板和 NAND 核心板的配置文件。使用“make xxx_defconfig”命令即可配置 uboot,比如:

make mx6ull_14x14_ddr512_emmc_defconfig

.u-boot.xxx_cmd 是一系列的文件,这些文件都是编译生成的,都是一些命令文件,比如文件.u-boot.bin.cmd,看名字应该是和 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。

4.Makefile

这个是顶层 Makefile 文件,Makefile 是支持嵌套的,也就是顶层 Makefile 可以调用子目录中的 Makefile 文件。Makefile 嵌套在大项目中很常见,一般大项目里面所有的源代码都不会放到同一个目录中,各个功能模块的源代码都是分开的,各自存放在各自的目录中。每个功能模块目录下都有一个 Makefile,这个 Makefile 只处理本模块的编译链接工作,这样所有的编译链接工作就不用全部放到一个 Makefile 中,可以使得 Makefile 变得简洁明了。

uboot 源码根目录下的 Makefile 是顶层 Makefile,他会调用其它的模块的 Makefile 文件,比如 drivers/adc/Makefile。当然了,顶层 Makefile 要做的工作可远不止调用子目录 Makefile 这么简单,关于顶层 Makefile 的内容我们稍后会有详细的讲解。

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 的复制文件。

5. .config

uboot 配置文件,使用命令“make xxx_defconfig”配置 uboot 以后就会自动生成,
.config 文件中都是以“CONFIG_”开始的配置项,这些配置项就是 Makefile 中的变量,因此后面都跟有相应的值,uboot 的顶层 Makefile 或子 Makefile 会调用这些变量值。

6.README

README 文件描述了 uboot 的详细信息,包括 uboot 该如何编译、uboot 中各文件夹的含义、相应的命令等等。建议大家详细的阅读此文件,可以进一步增加对 uboot 的认识。

二、VSCode工程创建

1.新建工程

打开 VScode,选择:文件->打开文件夹…,选中 uboot 文件夹
在这里插入图片描述
点击“文件->将工作区另存为…”,打开保存工作区对话框,将工作区保存到 uboot 源码根目录下,设置文件名为“uboot”,如图 所示
在这里插入图片描述
保存成功以后就会在 uboot 源码根目录下存在一个名为 uboot.code-workspace 的文件。这样一个完整的VSCode工程就建立起来了。但是这个VSCode工程包含了uboot的所有文件, uboot中有些文件是不需要的,比如 arch 目录下是各种架构的文件夹,

2.屏蔽不需要文件

在 arch 目录下,我们只需要 arm 文件夹,所以需要将其它的目录从 VSCode 中给屏蔽掉,比如将 arch/avr32 这个目录给屏蔽掉。
在 VSCode 上建名为“.vscode”的文件夹,
在.vscode 文件夹中新建一个名为“settings.json”的文件,
在这里插入图片描述
然后在 settings.json 中输入如下内容:
在这里插入图片描述
其中"search.exclude"里面是需要在搜索结果中排除的文件或者文件夹,"files.exclude"是左侧工程目录中需要排除的文件或者文件夹。

我们需要将 arch/avr32 文件夹下的所有文件从搜索结果和左侧的工程目录中都排除掉,因此在"search.exclude"和"files.exclude"中输入如图

在这里插入图片描述
保存一下 settings.json 文件,然后再看一下左侧的工程目录,发现 arch 目录下没有 avr32 这个文件夹了,说明 avr32 这个文件夹被排除掉了
在这里插入图片描述
最终set里面的代码为

{
     "search.exclude": {
        "**/node_modules": true,
        "**/bower_components": true,
        "**/*.o":true,
        "**/*.su":true,
        "**/*.cmd":true,
        "arch/arc":true,
        "arch/avr32":true,
        "arch/blackfin":true,
        "arch/m68k":true,
        "arch/microblaze":true,
        "arch/mips":true,
        "arch/nds32":true,
        "arch/nios2":true,
        "arch/openrisc":true,
        "arch/powerpc":true,
        "arch/sandbox":true,
        "arch/sh":true,
        "arch/sparc":true,
        "arch/x86":true,
        "arch/arm/mach*":true,
        "arch/arm/cpu/arm11*":true,
        "arch/arm/cpu/arm720t":true,
        "arch/arm/cpu/arm9*":true,
        "arch/arm/cpu/armv7m":true,
        "arch/arm/cpu/armv8":true,
        "arch/arm/cpu/pxa":true,
        "arch/arm/cpu/sa1100":true,
        "board/[a-e]*":true,
        "board/[g-z]*":true,
        "board/[0-9]*":true,
        "board/[A-Z]*":true,
        "board/fir*":true,
        "board/freescale/b*":true,
        "board/freescale/l*":true,
        "board/freescale/m5*":true,
        "board/freescale/mp*":true,
        "board/freescale/c29*":true,
        "board/freescale/cor*":true,
        "board/freescale/mx7*":true,
        "board/freescale/mx2*":true,
        "board/freescale/mx3*":true,
        "board/freescale/mx5*":true,
        "board/freescale/p*":true,
        "board/freescale/q*":true,
        "board/freescale/t*":true,
        "board/freescale/v*":true,
        "configs/[a-l]*":true,
        "configs/[n-z]*":true,
        "configs/[A-Z]*":true,
        "configs/M[a-z]*":true,
        "configs/M[A-Z]*":true,
        "configs/M[0-9]*":true,
        "configs/m[a-w]*":true,
        "configs/m[0-9]*":true,
        "configs/[0-9]*":true,
        "include/configs/[a-l]*":true,
        "include/configs/[n-z]*":true,
        "include/configs/[A-Z]*":true,
        "include/configs/m[a-w]*":true,
     },
     "files.exclude": {
        "**/.git": true,
        "**/.svn": true,
        "**/.hg": true,
        "**/CVS": true,
        "**/.DS_Store": true,
        "**/*.o":true,
        "**/*.su":true,
        "**/*.cmd":true,
        "arch/arc":true,
        "arch/avr32":true,
        "arch/blackfin":true,
        "arch/m68k":true,
        "arch/microblaze":true,
        "arch/mips":true,
        "arch/nds32":true,
        "arch/nios2":true,
        "arch/openrisc":true,
        "arch/powerpc":true,
        "arch/sandbox":true,
        "arch/sh":true,
        "arch/sparc":true,
        "arch/x86":true,
        "arch/arm/mach*":true,
        "arch/arm/cpu/arm11*":true,
        "arch/arm/cpu/arm720t":true,
        "arch/arm/cpu/arm9*":true,
        "arch/arm/cpu/armv7m":true,
        "arch/arm/cpu/armv8":true,
        "arch/arm/cpu/pxa":true,
        "arch/arm/cpu/sa1100":true,
        "board/[a-e]*":true,
        "board/[g-z]*":true,
        "board/[0-9]*":true,
        "board/[A-Z]*":true,
        "board/fir*":true,
        "board/freescale/b*":true,
        "board/freescale/l*":true,
        "board/freescale/m5*":true,
        "board/freescale/mp*":true,
        "board/freescale/c29*":true,
        "board/freescale/cor*":true,
        "board/freescale/mx7*":true,
        "board/freescale/mx2*":true,
        "board/freescale/mx3*":true,
        "board/freescale/mx5*":true,
        "board/freescale/p*":true,
        "board/freescale/q*":true,
        "board/freescale/t*":true,
        "board/freescale/v*":true,
        "configs/[a-l]*":true,
        "configs/[n-z]*":true,
        "configs/[A-Z]*":true,
        "configs/M[a-z]*":true,
        "configs/M[A-Z]*":true,
        "configs/M[0-9]*":true,
        "configs/m[a-w]*":true,
        "configs/m[0-9]*":true,
        "configs/[0-9]*":true,
        "include/configs/[a-l]*":true,
        "include/configs/[n-z]*":true,
        "include/configs/[A-Z]*":true,
        "include/configs/m[a-w]*":true,
     }
}

上述代码用到了通配符“”,比如“**/.o”表示所有.o 结尾的文件。“configs/[a-l]*”表示 configs 目录下所有以‘a’~‘l’开头的文件或者文件夹。上述配置只是排除了一部分文件夹,大家在实际的使用中可以根据自己的实际需求来选择将哪些文件或者文件夹排除掉。排除以后我们的工程就会清爽很多,搜索的时候也不会跳出很多文件了。

三、U-Boot顶层Makefile分析

在阅读 uboot 源码之前,肯定是要先看一下顶层 Makefile,分析 gcc 版本代码的时候一定是先从顶层 Makefile 开始的,然后再是子 Makefile,这样通过层层分析 Makefile 即可了解整个工程的组织结构。顶层 Makefile 也就是 uboot 根目录下的 Makefile 文件,由于顶层 Makefile 文件内容比较多,所以我们将其分开来看。

1.版本号

顶层 Makefile 一开始是版本号,内容如下(为了方便分析,顶层 Makefile 代码段前段行号采用 Makefile 中的行号,因为 uboot 会更新,因此行号可能会与你所看的顶层 Makefile 有所不同):
在这里插入图片描述

VERSION 是主版本号,PATCHLEVEL 是补丁版本号,SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2016.03”。EXTRAVERSION 是附加版本信息,NAME 是和名字有关的,一般不使用这两个。

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中有如下代码:

在这里插入图片描述

上述代码使用“+=”来给变量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“–include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。

3.命令输出

uboot 默认编译是不会在终端中显示完整的命令,都是短命令
在这里插入图片描述
在终端中输出短命令虽然看起来很清爽,但是不利于分析 uboot 的编译过程。可以通过设置变量“V=1“来实现完整的命令输出,这个在调试 uboot 的时候很有用,
在这里插入图片描述
顶层 Makefile 中控制命令输出的代码如下:
在这里插入图片描述

上述代码中先使用 ifeq 来判断"$(origin V)"和"command line"是否相等。这里用到了 Makefile中的函数 origin,origin 和其他的函数不一样,它不操作变量的值,origin 用于告诉你变量是哪来的,语法为:

$(origin <variable>)

variable 是变量名,origin 函数的返回值就是变量来源,因此$(origin V)就是变量 V 的来源。

如果变量 V 是在命令行定义的那么它的来源就是"command line",这样"$(origin V)"和"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 为“@”,综上所述:

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_”的话,整个命令都不会输出。

4.静默输出

置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的
短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出命令,这个时候就可以使用 uboot 的静默输出功能。编译的时候使用“make -s”即可实现静默输出,顶层 Makefile中相应的代码如下:
在这里插入图片描述
第91行判断当前正在使用的编译器版本号是否为4.x,判断 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))和“ ”(空)是否相等,如果不相等的话就成立,执行里面的语句。也就是说 ( f i l t e r 4. (filter4.%, (filter4.(MAKE_VERSION))不为空的话条件就成立,这里用到了 Makefile 中的 filter 函数,这是个过滤函数,函数格式如下:

$(filter <pattern...>,<text>)

filter 函数表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词,可以有多个模式。函数返回值就是符合 pattern 的字符串。

因此 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))的含义就是在字符串“MAKE_VERSION”中找出符合“4.%”的字符(%为通配符),
MAKE_VERSION是make工具的版本号,ubuntu16.04里面默认自带的make工具版本号为4.1,大家可以输入“make -v”查看。因此 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))不为空,条件成立,执行92~94 行的语句。
第 92 行也是一个判断语句,如果 ( f i l t e r (filter %s , (filter(firstword x$(MAKEFLAGS)))不为空的话条件成立,变量 quiet 等于“silent_”。

这里也用到了函数 filter,在 ( f i r s t w o r d x (firstword x (firstwordx(MAKEFLAGS)))中过滤出符合“%s”的单词。到了函数 firstword,函数 firstword 是获取首单词,函数格式如下:

$(firstword <text>)

firstword 函数用于取出 text 字符串中的第一个单词,函数的返回值就是获取到的单词。

当使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile。在顶层 Makfile 中添加如图 所示的代码:
在这里插入图片描述

两行代码用于输出 ( f i r s t w o r d x (firstword x (firstwordx(MAKEFLAGS))的结果

5.设置编译结果输出目录

6.代码检查

7.模块编译

8.获取主机架构和系统

9.设置目标架构、交叉编译器和配置文件

10.调用scripts/Kbuild.include

11.交叉编译工具变量设置

12.导出其他变量

13.make xxx_defconfig过程

14.Makefile.build脚本分析

15.make过程

总结:

这里只整理了一部分,更多更详细的资料请参考正点原子的linux驱动开发指南。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值