0%

Linux驱动学习日记(6) Uboot学习(一)

什么是Uboot

  U-Boot 本质上是运行在裸机环境下的 Bootloader 程序,但它本身已经具备操作系统级别的模块化结构(命令系统、驱动模型等)。Uboot最主要的工作就是初始化DDR,因为Linux是运行在DDR里面的,一般Linux镜像、设备树存放在SD、EMMC、NAND、SPI FLASH等外置存储区域。Uboot的主要目的就是为系统的启动做准备。
  Uboot不仅仅能启动Linux,也可以启动其它系统,比如vsworks等等。Linux不仅仅能通过Uboot启动,也可以通过其他bootloader启动。Uboot是一个通用的Bootloader,支持多种架构。

编译烧录后效果

  根据正点原子教程编译烧录后,将sd卡插入开发板,连接串口,发现终端上出现如下信息:

图 1 Uboot串口输出
这些信息包含了uboot版本号、编译日期、CPU信息、复位原因、板子名称等等。

文件结构分析

图 2 Uboot文件结构
图 3 目录列表

arch文件夹

  arch文件夹存放着架构相关的文件,我们使用的是arm芯片,所以我们只关心其中的arm文件夹即可。打开arm文件夹,如下图所示

图 4 arm文件夹
其中包含了许多的设备,因为我们用的是i.mx,我们只需关心imx-common文件夹即可。同时arm文件夹下的cpu文件夹中存放了我们使用的lds链接脚本,还有ARM-V7内核有关的文件。

board文件夹

  此文件夹中包含了不同的开发板相关的文件,我们后面移植 uboot 到时候就是参考 NXP 官方的开发板,也就是要参考mx6ullevk 这个文件夹来定义我们的板子。

configs文件夹

  这个文件夹存放了uboot的配置文件,uboot 是可配置的,但是你要是自己从头开始一个一个项目的配置,那就太麻烦了,因此一般半导体或者开发板厂商都会制作好一个配置文件。我们可以在这个做好的配置文件基础上来添加自己想要的功能,这些半导体厂商或者开发板厂商制作好的配置文件统一命名为“xxx_defconfig”, xxx 表示开发板名字这些 defconfig 文件都存放在 configs 文件夹中。

图 5 configs文件夹
使用“make xxx_defconfig”命令即可配置 uboot,如

make mx6ull_14x14_ddr512_emmc_defconfig

.uboot.xxx.cmd文件

  这些文件是Makefile运行时生成的构建记录和依赖规则,下面有三个文件,它们分别是:

1
2
3
u-boot                ELF可执行文件
u-boot-nodtb.bin 纯二进制U-Boot(不含设备树)
u-boot.bin 最终发布的uboot镜像

这三个文件通过.cmd转化。

uboot.xxx文件

文件名 说明
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 如果CONFIG_OF_SEPARATE=y,由u-boot-nodtb.bin + dtb拼接,不然是简单复制关系

UBoot顶层Makefile分析

裸机 Makefile U-Boot 顶层 Makefile
直接编译 .c/.S 几乎不编译代码
写 gcc/ld 命令 几乎不写 gcc
控制 .o → .elf 控制 构建流程

版本号

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

1
2
3
4
5
VERSION = 2016
PATCHLEVEL = 03
SUBLEVEL =
EXTRAVERSION =
NAME =

MAKEFLAGS

  make是支持递归调用的,在Makefile中运行

$(MAKE) -C subdir

则会自动运行subdir中的make。

1
MAKEFLAGS += -rR --include-dir=$(CURDIR)

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

命令输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif

ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif

  先判断V是否来自命令行,若是,则将其赋值给KBUILD_VERBOSE,在判断KBUILD_VERBOSE是不是1,控制是否输出详细命令。

指定编译结果输出路径

make O=…

  执行上述语句即可指定编译结果输出路径。

获取主机系统架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HOSTARCH := $(shell uname -m | \
sed -e s/i.86/x86/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/ppc64/powerpc/ \
-e s/ppc/powerpc/ \
-e s/macppc/powerpc/\
-e s/sh.*/sh/)

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

export HOSTARCH HOSTOS

Makefile会获取主机架构和系统,并将信息存入变量中。“|”意为管道,将左边的输出作为右边的输入。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif

KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG

# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)

HOSTCC = cc
HOSTCXX = c++
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2

ifeq ($(HOSTOS),cygwin)
HOSTCFLAGS += -ansi
endif

对于这个实验,我们应在第三行endif后添加如下语句

1
2
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-

这样就默认设置了目标架构和目标编译器,并生成了一个.config文件并用export命令将其导出使得子文件夹中的makefile也可以调用。

交叉编译工具变量设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
AS  = $(CROSS_COMPILE)as
# Always use GNU ld
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
LD = $(CROSS_COMPILE)ld.bfd
else
LD = $(CROSS_COMPILE)ld
endif
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
LDR = $(CROSS_COMPILE)ldr
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
AWK = awk
PERL = perl
PYTHON = python
DTC = dtc
CHECK = sparse

CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void -D__CHECK_ENDIAN__ $(CF)

KBUILD_CPPFLAGS := -D__KERNEL__ -D__UBOOT__

KBUILD_CFLAGS := -Wall -Wstrict-prototypes \
-Wno-format-security \
-fno-builtin -ffreestanding
KBUILD_AFLAGS := -D__ASSEMBLY__

# Read UBOOTRELEASE from include/config/uboot.release (if it exists)
UBOOTRELEASE = $(shell cat include/config/uboot.release 2> /dev/null)
UBOOTVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)

export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION
export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
export MAKE AWK PERL PYTHON
export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS

export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS KBUILD_AFLAGS

ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR这几个变量在uboot根目录下的config.mk文件中定义。config.mk中又读取了以下文件。

arch/arm/config.mk
arch/arm/cpu/armv7/config.mk
arch/arm/cpu/armv7/mx6/config.mk (此文件不存在)
board/ freescale/mx6ullevk/config.mk (此文件不存在)

make xxx_defconfig 过程

图 6 make xxx_defconfig 过程

  当敲下命令make xxx_defconfig后,顶层makefile识别出这是一个config类目标,先构建scripts/basic(配置工具),调用kconfig系统,然后读取arch/arm/configs/xxx_defconfig,最后生成.config + include/config/*

make 过程

图 7 make 过程

make默认目标是u-boot.srec、u-boot.bin、u-boot.sym、System.map、u-boot.cfg 和 binary_size_check这几个文件。
依赖关系如下

1
2
3
4
5
6
7
u-boot.bin

u-boot-nodtb.bin

u-boot

start.o + 各个 built-in.o