U-Boot配置和编译
前面在大概了解U-Boot及其目录结构后,就可以开始配置和编译U-Boot了。
在U-Boot源码中有一个README文件,它描述了如何配置并编译U-Boot。(其实很多都有,只不过是英文的,哈哈哈)
正确编译U-Boot
正确编译U-Boot代码的过程如下:
1)首先是编译器的问题,如果使用GNU交叉编译工具链,请确保环境变量的设置生效。
2)为特定的板子建立配置文件。输入make NAME_config就可以,其中“NAME_config”代表一个存在的配置名称,顶层目录下的boards.cfg文件中是已经支持的配置名称。
3)最后,输入“make all”就可以得到U-Boot映像文件了。其中“u-boot.bin”是二进制文件,“U-Boot”是ELF二进制格式的文件。
其中第二步举个例子
第二步:为特定的硬件板pcDuino3建立配置文件,打开boards.cfg文件,添加一行配置信息:
Active arm armv7 sunxi - sunxi
pcDuino3 sun7i:PCDUINO,SPL,SUNXI_EMAC
然后输入make pcDuino3_config命令,执行第二步的命令,接下来分析这个命令做了什么:
%_config:: outputmakefile
@$(MKCONFIG) -A $(@:_config=)
由于%是个通配符,所以无论是何种配置,make xxx_config都是这个目标。命令中的MKCONFIG在Makefile之前有如下定义:
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
该定义表明MKCONFIG是顶层目录下的mkconfig脚本文件。
我们继续来看完整的命令:@ ( M K C O N F I G ) − A (MKCONFIG)-A (MKCONFIG)−A(@:_config=),这里$(@:_config=)是变量的替换引用。
其具体使用方法为:格式为“ ( V A R : A = B ) ”或者“ (VAR:A=B)”或者“ (VAR:A=B)”或者“{VAR:A=B}”,意思是:**替换变量“VAR”中所有以“A”字符结尾的字为“B”结尾的字。**这将相当于把pcDuino3_config末尾的_config去除了。因此实际执行的是“mkconfig-A pcDuino3”命令。
执行该脚本将生成两个文件,这两个文件将在后面的步骤中被引用。
下面就是执行mkconfig脚本了:
mkconfig -A pcDuino3
执行该脚本,生成两个文件,一个是include/config.h,如下:
/* Automatically generated - do not edit */
#define CONFIG_PCDUINO 1
#define CONFIG_SPL 1
#define CONFIG_SUNXI_EMAC 1
#define CONFIG_SYS_ARCH "arm"
#define CONFIG_SYS_CPU "armv7"
#define CONFIG_SYS_BOARD "sunxi"
#define CONFIG_SYS_TARGET "pcDuino3"
#define CONFIG_SYS_SOC "sunxi"
#define CONFIG_BOARDDIR board/sunxi
#include <config_cmd_defaults.h>
#include <config_defaults.h>
#include <configs/sun7i.h>
#include <asm/config.h>
#include <config_fallbacks.h>
#include <config_uncmd_spl.h>
还有一个文件是include/config.mk,如下:
ARCH = arm
CPU = armv7
BOARD = sunxi
SOC = sunxi
下一步就是make。
因为在boards.cfg文件中有配置sun7i:PCDUINO、SPL、SUNXI_EMAC,所以这里我们将使用SPL框架。在spl目录下,有一个Makefile文件。我们简单分析一下这个Makefile文件。
(移植过uboot的都知道,uboot的启动其实是分为BL0,BL1,BL2三个阶段的,即:ROM->SPL->uboot.img.而这个SPL架构将可以编译产生一个uboot-spl.bin。即BL1的代码。也就是说SPL结构其实做的工作就是uboot的BL1阶段的工作。但是这个SPL不是必须的,后面再讲讲这个东西)
首先
CONFIG_SPL_BUILD := y
export CONFIG_SPL_BUILD
定义并导出CONFIG_SPL_BUILD,这个定义是SPL框架最重要的定义,在最初的汇编代码中很多代码段都由该定义隔开。
include include/config.mk
include $(TOPDIR)/config.mk
引入刚刚生成的include/config.mk和顶层目录下的config.mk,其中顶层目录下的config.mk是设置了一些编译链接的选项。
接下来是SPL需要的代码和模块。再往下是指定链接脚本。
# Linker Script
ifdef CONFIG_SPL_LDSCRIPT
# need to strip off double quotes
LDSCRIPT := $(addprefix $(SRCTREE)/,$(CONFIG_SPL_LDSCRIPT:"%"=%))
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot-spl.lds
Endif
ifeq ($(wildcard $(LDSCRIPT)),)
$(error could not find linker script)
endif
再下面是描述最终的目标:
ALL-y += $(obj)/$(SPL_BIN).bin
ifdef CONFIG_SUNXI
ifndef CONFIG_SPL_FEL
ALL-y += $(obj)/sunxi-spl.bin
endif
endif
所以最后在该目录下会生成u-boot-spl.bin和sunxi-spl.bin,其中sunxi-spl.bin是使用mksunxiboot工具生成的:
ifdef CONFIG_SUNXI
quiet_cmd_mksunxiboot = MKSUNXI $@
cmd_mksunxiboot = $(OBJTREE)/tools/mksunxiboot $< $@
$(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin
$(call if_changed,mksunxiboot)
Endif
签名分析的是makefile
接下来分析一下链接脚本。根据前面关于链接脚本的一系列判断和指定,此处的链接脚本采用的是arch/arm/cpu/armv7/sunxi/u-boot-spl.lds,脚本如下:
MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\
LENGTH = CONFIG_SPL_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \
LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.text :
{
__start = .;
arch/arm/cpu/armv7/start.o (.text)
*(.text*)
} > .sram
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
. = ALIGN(4);
.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
. = ALIGN(4);
__image_copy_end = .;
_end = .;
.bss :
{
. = ALIGN(4);
__bss_start = .;
*(.bss*)
. = ALIGN(4);
__bss_end = .;
} > .sdram
}
在脚本最开始处,使用MEMORY命令定义了两个存储区域:一个是sram,起始地址为CONFIG_SPL_TEXT_BASE,长度为CONFIG_SPL_MAX_SIZE;另一个是sdram,起始地址为CONFIG_SPL_BSS_START_ADDR,长度为CONFIG_SPL_BSS_MAX_SIZE。这几个定义都是在include/configs/sunxi-common.h中:
#define CONFIG_SPL_TEXT_BASE 0x20
#define CONFIG_SPL_TEXT_BASE 0x5fe0
#define CONFIG_SPL_BSS_START_ADDR 0x50000000
#define CONFIG_SPL_BSS_MAX_SIZE 0x80000
这里可以看到TEXT段的基址定为0x20,这是因为mksunxiboot的存在,它会加上大小为0x20的特定的头。
在SECTIONS命令中定义了.text、.rodata、.data和.bss这四个段。其中.text、.rodata和.data段放在sram内存区域中,而.bss段放在sdram内存区域中。
在.text段中,我们将arch/arm/cpu/armv7/start.o中的.text段放置在最前面,其他对象文件的.text段随后放置。在.rodata段和.data段中,使用SORT_BY_ALIGNMENT将各个对象文件的相应段对齐排序。
或许到这里你很困惑,这是因为你和我一样对SPL框架没有认识,下面我们来学一下SPL,在了解了SPL以后,再反过来看看这里对SPL的Makefile和ldscript的讲解。