首页 > 代码库 > u-boot学习(二):u-boot简要分析

u-boot学习(二):u-boot简要分析

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

看到不错的文章,不要添加收藏夹,想着以后有时间再看,因为很有可能你以后再也不会看它们了。

想写总结的文章,不要想着等到以后有时间了再总结,因为很有可能你以后更没有时间总结它们了。

——送给自己

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

要想知道一个程序的作用,最好的办法是分析程序的源代码。这一方法对了解u-boot的功能也适用,因此,我们首先看u-boot的源码结构,然后根据源码结构总结出u-boot的主要特性。最后,根据配置、编译的过程,简单分析u-boot。

(一)

以u-boot-1.1.6为例分析目录结构如下:

1、平台相关的或开发板相关的目录:board、cpu、lib_i386类似

2、通用函数的目录:include、lib_generic、common

3、通用的设备驱动程序:disk、drivers、dtt、fs、nand_spl、net、post、rtc

4、u-boot工具、示例程序、文档:doc、example、tools

u-boot顶层目录说明

目录特性解释说明
board开发板相关对应不同配置的电路板(即使CPU相同),比如smdk2410等
cpu平台相关对应不同的CPU,如arm920t、i386等。
lib_i386平台相关某一架构下通用的文件
include通用函数头文件和开发板配置文件,开发板的配置文件都放在include/configs目录下
lib_generic通用函数需要通用的库函数,比如printf等
common通用函数通用函数,多是对下一层驱动程序的进一步封装
disk通用设备驱动程序硬盘接口程序
drivers通用设备驱动程序
各类具体设备的驱动程序
dtt通用设备驱动程序
数字温度测量或者传感器的驱动
fs通用设备驱动程序
文件系统
nand_spl通用设备驱动程序
u-boot一般从ROM、NORFLASH等设备启动,现在开始支持NAND Flash启动
net通用设备驱动程序
各种网络协议
post通用设备驱动程序
上电自检程序
rtc通用设备驱动程序
实时时钟的驱动
doc文档开发、使用文档
example示例程序一些测试程序,可以使用u-boot下载后运行
tools工具制作S-Record、u-boot格式映像的工具,如mkimage
从上表中u-boot目录的分析可以看出u-boot的一些特性:支持多种嵌入式操作系统内核,如Linux、Vxworks等;支持多个处理器系列,如PowerPC、ARM、x86等;丰富的设备驱动源码,如串口、SDRAM、Flash、以太网等;支持NFS挂载、RAMDISK形式的根文件系统、从Flash中引导压缩或非压缩系统内核;支持目标板环境变量多种存储方式,如Flash等。CRC32校验、上电自检功能等。


u-boot中各目录间也是层次结构的,这有助于在移植过程中为我们需要修改哪些文件提供信息,由下图可以知道,只需修改board、cpu、lib_xxx目录下的文件,即可完成移植过程,如果想要添加其他的命令等,则需要修改其他文件。

lib_generic    common
post    net    fs   disk
dtt      nand_spl   rtc   drivers
board   cpu  lib_xxx

(二)配置
上一篇的u-boot概述中配置u-boot时使用了命令:make 100ask24x0_config命令。执行该命令时执行Makefile中的如下一行规则:

100ask24x0_config	:	unconfig
	@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
该规则解释为:
100ask24x0_config	:	unconfig
	mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
因此配置过程,我们可以分析根目录下的mkconfig文件,从而了解配置过程,做了哪些工作。逐行分析如下:

#!/bin/sh -e

# Script to create header files and links to configure
# U-Boot for a specific board.
#
# Parameters:  Target  Architecture  CPU  Board [VENDOR] [SOC]
#
# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
#

APPEND=no	# Default: Create new config file
BOARD_NAME=""	# Name to print in make output

mkconfig 100ask24x0 arm arm920t smdk2410 NULL s3c24x0
  $0         $1      $2   $3       $4     $5    $6

while [ $# -gt 0 ] ; do  <span style="color:#ff6666;">/*$ # :保存程序命令行参数的数目*/</span>
	case "$1" in
	--) shift ; break ;;
	-a) shift ; APPEND=yes ;;
	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
	*)  break ;;
	esac
done

[ "${BOARD_NAME}" ] || BOARD_NAME="$1"  <span style="color:#ff6666;">/*因为上面定义了BOARD_NAME="",所以该句得到结果为BOARD_NAME=100ask24x0*/
</span>
[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1  <span style="color:#ff6666;">/*$#是参数的个数,此处$#=6,所以这两句都不会执行到exit 1语句*/</span>

echo "Configuring for ${BOARD_NAME} board..."
<span style="color:#ff6666;">/*此句便是执行make 100ask24x0_config时显示的那条语句:Configuring for 100ask24x0 board...*/</span>
#
# Create link to architecture specific headers
#
<span style="color:#ff6666;">/*查看Makefile可知,
 *OBJTREE		:= $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
 *SRCTREE		:= $(CURDIR)
 *所以if语句不执行,而是执行下面的else语句
 */</span>
if [ "$SRCTREE" != "$OBJTREE" ] ; then 
	mkdir -p ${OBJTREE}/include
	mkdir -p ${OBJTREE}/include2
	cd ${OBJTREE}/include2
	rm -f asm
	ln -s ${SRCTREE}/include/asm-$2 asm
	LNPREFIX="../../include2/asm/"
	cd ../include
	rm -rf asm-$2
	rm -f asm
	mkdir asm-$2
	ln -s asm-$2 asm
else
	cd ./include
	rm -f asm
	ln -s asm-$2 asm <span style="color:#ff6666;">/*删除asm文件,然后再次建立asm文件,并令它链接向asm-$2目录,即asm-arm目录*/</span>
fi

rm -f asm-$2/arch  <span style="color:#ff6666;">/*删除asm-$2/arch文件,即asm-arm/arch文件*/</span>

if [ -z "$6" -o "$6" = "NULL" ] ; then  <span style="color:#ff6666;">/* -z string表示string为null $6也不是NULL,因此执行else分支*/</span>
	ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
	ln -s ${LNPREFIX}arch-$6 asm-$2/arch  <span style="color:#ff6666;">/*因为LNPREFIX为空,该句为ln -s arch-s3c24x0 asm-arm/arch*/</span>
fi

if [ "$2" = "arm" ] ; then
	rm -f asm-$2/proc
	ln -s ${LNPREFIX}proc-armv asm-$2/proc <span style="color:#ff6666;">/*重新建立asm-arm/proc文件,并让它链接向proc-armv目录*/</span>
fi

#
# Create include file for Make
#
<span style="color:#ff6666;">/*
 *创建config.mk文件
 *ARCH = arm
 *CPU  = arm920t
 *BOARD= smdk2410
 *SOC  = s3c24x0
 */</span>
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

#
# Create board specific header file
#
<span style="color:#ff6666;">/*创建config.h文件*/</span>
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
总结配置的作用有以下4点:

1、开发板名称BOARD_NAME等于$1

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

4、创建开发板相关的头文件include/config.h

(三)编译、链接

既然在编译之前必须经过上面的配置过程,那么配置过程一定为编译过程提供了某些东西,否则的话,我们直接编译就可以了。那么,上面配置过程生成的文件、修改的地方是如何在编译过程中体现的呢。其实,这在Makefile文件中有所体现。如:

include $(OBJTREE)/include/config.mk  <span style="color:#ff6666;">//包含了配置过程中宏生成的include/config.mk文件</span>
export	ARCH CPU BOARD VENDOR SOC
include $(TOPDIR)/config.mk  <span style="color:#ff6666;">//包含了配置过程中生成的顶层的config.mk文件</span>

配置完成后,执行make命令即可链接。执行make,我们执行的是Makefile里的make all语句,它的命令如下:

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

all:		$(ALL)

$(obj)u-boot.hex:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@

$(obj)u-boot.srec:	$(obj)u-boot
		$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@

$(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)
		<span style="color:#ff6666;">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
从运行结果反看Makefile的执行:

UNDEF_SYM=</span>`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a |sed  -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;
cd /work/system/u-boot-1.1.6</span> && arm-linux-ld -Bstatic -T /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000  $UNDEF_SYM cpu/arm920t/start.o 			<span style="color:#ff6666;">--start-group</span> lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a --end-group -L /work/tools/gcc-3.4.5-glibc-2.3.6/lib/gcc/arm-linux/3.4.5 -lgcc 			<span style="color:#ff6666;">-Map u-boot.map -o u-boot
总结编译流程:

1、编译cpu/$(CPU)/start.S。

2、然后,对于平台/开发板相关的每个目录、每个通用目录都使用它们各自的Makefile生成相应的库。

3、将1、2步骤生成的.o、.a文件按照board/$(BOARDDIR)/config.mk文件中指定的代码段起始地址、board/$(BOARDDIR)/U-Boot.lds链接脚本进行链接。

4、第3步得到的ELF格式的U-Boot,后面Makefile还会将它转换为二进制格式S-Record格式。

(四)逐行分析链接文件

/*
 * (C) Copyright 2002
 * Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") <span style="color:#ff6666;">/*定义3种输出文件的格式(大小端)*/</span>
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm) <span style="color:#ff6666;">/*设置输出文件的machine architecture(体系结构)*/</span>
ENTRY(_start)  <span style="color:#ff6666;">/*将符号_start的值设置成入口地址*/</span>
SECTIONS
{
	. = 0x00000000;

	. = ALIGN(4);
	.text      :
	{
	  cpu/arm920t/start.o	(.text)
          board/100ask24x0/boot_init.o (.text)
	  *(.text)
	}                  <span style="color:#ff6666;">/*将start.o boot_init.o 以及除此之外的所有(*符号代表任意输入文件)
			    *输入文件的.text section 以4字节对齐的方式合并成一个.text section,
			    *该section的地址由定位器符号的值指定,即为0x00000000
			    */</span>

	. = ALIGN(4);
	.rodata : { *(.rodata) }
			<span style="color:#ff6666;">/*将所有输入文件的只读数据段.rodata section 以4字节对齐的方式
			 *合并成一个.rodata section,该section的地址为
			 *0x00000000+.text section的大小
			 */</span>
	. = ALIGN(4);
	.data : { *(.data) }
			<span style="color:#ff6666;">/*将所有输入文件的数据段.data section 以4字节对齐的方式
			 *合并成一个.data section,该section的地址为
			 *0x00000000+.text section的大小+.rodata section的大小
			 */</span>
	. = 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 = .;
}

参考:韦东山《嵌入式Linux应用开发完全手册》

    Linux下的lds链接脚本基础