uboot简单介绍:
uboot 即通用的Bootloader,一般有两层含义:1.他可以引导多种操作系统
Linux、VxWorks、等,2.支持多长架构CPU:PowerPC、MIPS、X86、ARM等。
一般情况下:UBOOT分为两个阶段来完成它的工作:
(1)uboot第一阶段:
硬件初始化
为加载uboot第二阶段代码初始化RAM空间
复制第二阶段代码到RAM
设置堆栈
跳转到第二阶段
这一阶段的硬件初始化包括:关闭WATCHDOG、关闭中断、设置CPU时钟频率、RAM初始化
等
(2)uboot第二阶段
初始化本阶段用到的硬件设备
检测系统内存映射
将内核映像文件盒根文件系统从FLASH中加载到RAM中
为内核设置启动参数
调用内核
下面来了解下U-Boot的编译连接过程
uboot的源码文件众多,想要了解某个架构,某款开发版如何使用,最直接的方式就是阅读Makefile,这里先以S3C2410举个例子
根据顶层的ReadMe文件说明,我们知道,想要利用某种类型的开发板,首先要执行make board_config 命令进行配置,然后
再执行make all即可以生成如下文件:
U-Boot.bin:二进制可执行文件,直接烧录进ROM或者NORflash的文件
U-Boot:ELF格式的可执行文件
首先我们先来分析makefile 看一看make smdk2410_config做了那些事情。
顶层makefile中有如下规则:
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
可知执行make smdk2410_config后,实际上执行的是
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
$(@:_config=)的结果就是将smdk2410_config中的_config去掉
根据MKCONFIG := $(SRCTREE)/mkconfig,可知$(MKCONFIG)就是源码目录下的mkconfig,接着我们来分析下mkconfig中重要的部分,在文件开头给出了mkconfig的用法
Parameters: Target Architecture CPU Board [VENDOR] [SOC]
make smdk_2410 arm arm920t smdk2410 NULL s3c24x0
接着我们来分析下mkconfig中重要的部分
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1
echo "Configuring for ${BOARD_NAME} board..."
执行完上面的命令后BOARD_NAME就等于smdk2410,接着分析:
if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
......
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else
cd ./include
rm -f asm
ln -s asm-$2 asm
fi
很显然上面这段我们走else分支,创建ln -s asm-$2 asm ($2=arm),执行后,asm就是指向asm-arm的软链接
rm -f asm-$2/arch
if [ -z "$6" -o "$6" = "NULL" ] ; then
ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
fi
上面这一段也是走到else分支且${LNPREFIX}为空,故为ln -s arch-$6 asm-$2/arch->ln -s arch-s3c24x0 asm-arm/arch
将asm-arm目录下的arch文件指向同一个目录下arch-s3c24x0
#
# Create include file for Make
#
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
上面这段在./include目录下创建了,config.mk文件,并写入
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC = s3c24x0
接着继续分析
#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
exit 0
上面这段在./include目录下创建了,config.h文件,并写入
/* Automatically generated - do not edit */
#include <configs/smdk2410.h>
总结下执行 make smdk2410_config做了那些事情:
1.设置BOARD_NAME=”$1”,即smdk2410
2.创建开发板平台相关的头文件的链接
ln -s asm-$2 asm
ln -s arch-$6 asm-$2/arch
ln -s proc-armv asm-$2/proc
3.创建顶层Makefile包含的include/config.mk,内容如下:
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC = s3c24x0
4.创建开发板相关的头文件include/config.h,内容如下:
/* Automatically generated - do not edit */
#include <configs/smdk2410.h>
从上面的结果能够看出:
如果需要加入一个新的开发板,需要在board目录下新建一个board_name的目录,
同时也需要在include/config目录下建立一个文件board_name.h,里面存放的
就是board_name的配置信息,可以通过手动修改include/config/.h
来裁剪、设置U-BOOT.
配置文件中主要有两类宏
(1)一类是前缀为CONFIG_,它们用于选择CPU、SOC、开发板类型,设置时钟选择设备驱动等。比如:
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
#define CONFIG_S3C2410 1 /* in a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK2410 1 /* on a SAMSUNG SMDK2410 Board */
(2)另一类是参数(Setting),前缀”CFG_”,它们用于设置malloc缓冲池的大小U-Boot的提示符、
U-Boot下载文件时的默认加载地址、FLASH的起始地址
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024)
#define CFG_LOAD_ADDR 0x33000000 /* default load address */
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE PHYS_FLASH_1
实际上在编译过程中,大部分的文件都会被编译到,但是是不每个文件的代码都是有效的,主要有功能宏控制,比如:对于网卡驱动CS8900.C,有如下宏判断
#ifdef CONFIG_DRIVER_CS8900
实际代码
#ENDIF
从上面的分析能够看出CONFIG_的宏,主要用于功能的选择,CFG_主要用于细节参数的设置。
下面来看编译链接的过程。
配置完成以后,执行make all编译,来分析下主目录下Makefile中的内容。
# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export ARCH CPU BOARD VENDOR SOC
/*包含的就是前面make smdk2410_config在include目录下生成的config.mk,有ARCH CPU BOARD VENDOR SOC等的定义*/
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
/*指明交叉编译时用的工具名称*/
# load other configuration
include $(TOPDIR)/config.mk
在顶层目录的config.mk里面有这样一些重要信息:
BOARDDIR = $(BOARD)
ifdef BOARD
sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific rules
endif
LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
在board/smdk2410/config.mk中,定义了“TEXT_BASE=0x33f80000”。所以,最终结果如下:
BOARDDIR 为smdk2410;LDFLAGS中有“-T board/smdk2410/U-boot.lds -Ttext 0x33f80000”
继续往下看makefile
#############################################################
# U-Boot objects....order is important (i.e. start must be first)
OBJS = cpu/$(CPU)/start.o
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS变量就是平台/开发板相关的各个目录、通用目录下的库构成
$(OBJS):
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
$(LIBS):
$(MAKE) -C $(dir $(subst $(obj),,$@))
$(SUBDIRS):
$(MAKE) -C $@ all
从规则能够看出,OBJS成的每个成员都需要进入到cpu/$(CPU)目录,即cpu/arm920t目录编译
现在OBJS为cpu/arm920t/start.o,它将由cpu/arm920t/start.S编译得到。
对于LIBS中的每个成员,都将进入相应的子目录执行make命令,生成对应的库文件
当所有的OBJS、LIBS所表示.a和.o文件都生成后,就剩最后的链接,对应下面的几行:
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
$(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
先由上面的规则生成ELF格式的uboot文件,然后生成可用于烧写的二进制文件,其中LDFLAGS确定了链接方式
其中有-T board/smdk2410/U-boot.lds -Ttext 0x33F800指明了程序的布局、地址,U-Boot.lds的内容如下:
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
从上面这一段能够看出,cpu/arm920t/start.o会放在代码段的开始位置
总结下编译流程:
1.首先编译cpu/$(CPU)/start.S,对于不同的CPU,还可能编译不同目录下的其它文件
2.然后,对于平台/开发板相关目录、每个通用的目录使用它们自己各自的Makefile生成
3.将步骤1,2生成的.o、.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码起始地址、board/$(BOARDDIR)/U-Boot.lds链接脚本进行链接
4.第3步得到的ELF格式的U-boot然后转换为二进制格式的uboot
在分析完makefile和链接脚本后,下面我们就正式开始分析uboot代码