U_Boot入门
U_Boot简介
- Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段 bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(NAND, NOR FLASH,SD,MMC 等)拷贝到 DDR 中,最后启动 Linux 内核。当然了,bootloader 的实 际工作要复杂的多,但是它最主要的工作就是启动 Linux 内核,bootloader 和 Linux 内核的关系 就跟 PC 上的 BIOS 和 Windows 的关系一样,bootloader 就相当于 BIOS。所以我们要先搞定 bootloader,很庆幸,有很多现成的 bootloader 软件可以使用,比如 U-Boot、vivi、RedBoot 等 等,其中以 U-Boot 使用最为广泛,为了方便书写,本书会将 U-Boot 写为 uboot。
- uboot 的全称是 Universal Boot Loader,uboot 是一个遵循 GPL 协议的开源软件,uboot 是一 个裸机代码,可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高 级功能。
- uboot 官网为 http://www.denx.de/wiki/U-Boot/
UBoot顶层makefile
编译需要的shell脚本

- 第 1 行是 shell 脚本要求的,必须是“#!/bin/bash”或者“#!/bin/sh”
- 第 2 行使用了 make 命令,用于清理工程,也就是每次在编译 uboot 之前都清理一下工程。 这里的 make 命令带有三个参数,第一个是 ARCH,也就是指定架构,这里肯定是 arm;第二个 参数 CROSS_COMPILE 用于指定编译器,只需要指明编译器前缀就行了,比如 arm-linux-gnueabihf-gcc 编译器的前缀就是“arm-linux-gnueabihf-”;最后一个参数 distclean 就是清除工程。
- 第 3 行也使用了 make 命令,用于配置 uboot。同样有三个参数,不同的是,最后一个参数是 mx6ull_14x14_ddr512_emmc_defconfig。前面说了 uboot 是 bootloader 的一种,可以用来引导 Linux,但是 uboot 除了引导 Linux 以外还可以引导其它的系统,而且 uboot 还支持其它的架构 和外设,比如 USB、网络、SD 卡等。这些都是可以配置的,需要什么功能就使能什么功能。所 以在编译 uboot 之前,一定要根据自己的需求配置 uboot。mx6ull_14x14_ddr512_emmc_defconfig 就是正点原子针对 I.MX6U-ALPHA 的 EMMC 核心板编写的配置文件,这个配置文件在 uboot 源码的 configs 目录中。在 uboot 中,通过“make xxx_defconfig”来配置 uboot,xxx_defconfig 就是不同板子的配置文件,这些配置文件都在 uboot/configs 目录中
- 第 4 行有 4 个参数,用于编译 uboot,通过第 3 行配置好 uboot 以后就可以直接“make”编 译 uboot 了。其中 V=1 用于设置编译过程的信息输出级别;-j 用于设置主机使用多少线程编译 uboot,最好设置成我们虚拟机所设置的核心数,如果在 VMware 里面给虚拟就分配了 4 个核, 那么使用-j4 是最合适的,这样 4 个核都会一起编译
查看UBoot之前需要先进行一次编译
编译后的文件结构


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

顶层 Makefile 一开始是版本号,内容如下(为了方便分析,顶层 Makefile 代码段前段行号 采用 Makefile 中的行号,因为 uboot 会更新
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 。 |
- 有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明, 否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。在uboot的主Makefile 中有如下代码:
MAKEFLAGS += -rR --include-dir=$(CURDIR) |
- 上述代码使用“+=”来给变量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐 含规则和变量定义,“–include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。
命令输出
uboot 默认编译是不会在终端中显示完整的命令,都是短命令

上述代码中先使用 ifeq 来判断”$(origin V)”和”command line”是否相等。这里用到了 Makefile 中的函数 origin,origin 和其他的函数不一样,它不操作变量的值,origin 用于告诉你变量是哪 来的
$(origin <variable>) |
variable 是变量名,origin 函数的返回值就是变量来源,因此$(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 的话

V=0 或者命令行不定义 V 的话

Makefile 中会用到变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令,在顶层 Makefile 中有很多如下所示的命令
$(Q)$(MAKE) $(build)=tools
如果 V=0 的话上述命令展开就是“@ make $(build)=tools”,make 在执行的时候默认会在终 端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。当 V=1 的时候 Q 就为空, 上述命令就是“make $(build)=tools”,因此在 make 执行的过程,命令会被完整的输出在终端上
有些命令会有两个版本
sym 命令分为“quiet_cmd_sym”和“cmd_sym”两个版本,这两个命令的功能都是一样的, 区别在于 make 执行的时候输出的命令不同。quiet_cmd_xxx 命令输出信息少,也就是短命令, 而 cmd_xxx 命令输出信息多,也就是完整的命令。
- 如果变量 quiet 为空的话,整个命令都会输出。
- 如果变量 quiet 为“quiet_”的话,仅输出短版本。 _
- _如果变量 quiet 为“silent_”的话,整个命令都不会输出。
静默输出
上一小节讲了,设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的 短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出命令,这个时候 就可以使用 uboot 的静默输出功能。编译的时候使用“make -s”即可实现静默输出
判断当前正在使用的编译器版本号是否为 4.x,判断$(filter 4.%,$(MAKE_VERSION)) 和“ ”(空)是否相等,如果不相等的话就成立,执行里面的语句。也就是说 $(filter 4.%,$(MAKE_VERSION))不为空的话条件就成立,这里用到了 Makefile 中的 filter 函数,这是 个过滤函数,函数格式如下

- filter 函数表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词, 可以有多个模式。函数返回值就是符合 pattern 的字符串。因此$(filter 4.%,$(MAKE_VERSION)) 的含义就是在字符串“MAKE_VERSION”中找出符合“4.%”的字符(%为通配符), MAKE_VERSION 是make工具的版本号,ubuntu16.04里面默认自带的make工具版本号为4.1, 大家可以输入“make -v”查看。因此$(filter 4.%,$(MAKE_VERSION))不为空,条件成立
,如果$(filter %s ,$(firstword x$(MAKEFLAGS)))不为空的话条件 成立,变量 quiet 等于“silent_”。这里也用到了函数 filter,在$(firstword x$(MAKEFLAGS)))中 过滤出符合“%s”的单词。到了函数 firstword,函数 firstword 是获取首单词,函数格式如下
$(firstword <text>)firstword 函数用于取出 text 字符串中的第一个单词,函数的返回值就是获取到的单词。当 使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile。
添加如下内容
可见增加
-s之后,firstword变成了
- 可见也不是直接
-s,但是filter的返回值肯定不为空
设置编译结果的输出目录
uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定 输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中。这么做是为了将源文件 和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在 同一个目录内,一般我们不指定 O 参数。


第 124 行判断“O”是否来自于命令行,如果来自命令行的话条件成立,KBUILD_OUTPUT 就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录。
第 135 行判断 KBUILD_OUTPUT 是否为空。
第 139 行调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路 径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。



