01 uboot的配置和编译

uboot编译体验

windows与linux的启动过程对比

windows linux
上电 上电
BIOS BootLoader
引导操作系统 启动linux内核
识别磁盘 挂接根文件系统
运行应用程序 运行应用程序

BootLoader的最终目的:启动内核

uboot编译

解压老韦准备好的uboot源文件,并打好补丁,配置,然后编译,注意如果权限不够,最好将整个文件夹递归chmod,给权限777

patch -p1 < ../u-boot-1.1.6_jz2440.patch
make 100ask24x0_config
make

uboot烧录

使用老韦提供的oflash,在nand flash中完成烧录

uboot菜单

开机按空格,进入boot菜单
q退出
menu进入
help帮助
? + 指令查看使用方法
查看环境变量 pri
设置环境变量set
保存save

希望uboot实现的功能

从Flash读出内核
初始化SDRAM
将内核放到SDRAM
初始化时钟
启动内核

烧写Flash
网卡功能
USB功能
串口功能

uboot Makefile分析

一般编译的步骤为

  • 先make xxxconfig配置
  • 再make编译

分析配置过程

make 100ask24x0

在makefile可以看到配置的时候,执行的指令

100ask24x0_config	:	unconfig
	@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0

首先看MKCONFIG指的是什么,应该是源文件下的mkconfig

MKCONFIG	:= $(SRCTREE)/mkconfig
export MKCONFIG

@$(MKCONFIG),其中的@,表示在执行命令时不输出命令本身(不回显),只输出命令执行的结果
$(@:_config=),这里用到了makefile的变量替换规则,$(VAR:A=B)表示替换变量$(VAR)中的A为B,这里的A位_config,B为空,VAR是$@,意思是目标文件,一般写在行首,往上找一行,发现$@的其实就是代指100ask24x0_config,所以最后的效果是将100ask24x0_config中的_config替换为空

得到替换后的表示形式

100ask24x0_config	:	unconfig
	mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

当执行配置命令的时候,就相当于去执行这个脚本mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0

下一步去mkconfig脚本去分析
这一段是对传进来的参数进行判断,目前我们传的参数跟--,-a,-n,*无关,所以不用管

while [ $# -gt 0 ] ; do
	case "$1" in
	--) shift ; break ;;
	-a) shift ; APPEND=yes ;;
	-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
	*)  break ;;
	esac
done

是否已经定义过BOARD_NAME,默认是没有的,如果没有定义过的话,就使用传进来的第一个参数$1,注意在shell脚本中,$0代表的是脚本文件名,参数是从$1开始的,这里的$1代指100ask24x0,执行完后的结果是变量BOARD_NAME的值为100ask24x0

BOARD_NAME=""	# Name to print in make output
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"

下面分析参数的个数是否正常,@#就代指参数的个数,注意@#不把 0 0算在内,因此这里的 #值是6
当参数的个数小于4,或者大于6,则认为异常,脚本直接退出

[ $# -lt 4 ] && exit 1
[ $# -gt 6 ] && exit 1

这是一句打印到终端的话,在脚本中使用echo打印字符串到屏幕显示,做提示用

echo "Configuring for ${BOARD_NAME} board..."

以下代码先判断SRCTREE和OBJTREE是否相等,这两个变量要去Makefile去找,如下
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
其中要知道$(CURDIR),CURDIR是Makefile内嵌的变量,即使没有定义,也能使用,它就代表当前目录
第一句话,如果定义过变量BUILD_DIR,则OBJTREE=BUILD_DIR,否则OBJTREE=CURDIR
由于没有定义过变量BUILD_DIR,因此OBJTREE和SRCTREE,都等于CURDIR
因此,这段代码,最终执行else分支,其中$2为arm,执行的结果是先强制删除文件asm(无论是否存在),然后创建一个链接文件asm,指向目录asm-arm
为什么会创建这个链接,主要是为了少修改代码,例如我们写程序时,有时要去包含头文件#include <asm/type.h>,这里的asm就会自动替换为asm-arm,去asm-arm目录下找type.h文件,下次如果编译i386时,它也会自动替换为#include <asm-i386/type.h>,这样换平台编译时,不用我们手动去修改代码,方便了很多

if [ "$SRCTREE" != "$OBJTREE" ] ; then
	。。。//太长省略
else
	cd ./include
	rm -f asm
	ln -s asm-$2 asm        //$2=arm
fi

$2指的是arm,因此替换后rm -f asm-arm/arch,删除asm-arm目录下的arch文件,如果你执行过配置的话,发现这个arch文件也是一个链接文件

rm -f asm-$2/arch //$2=arm

分析这段之前,先了解一下shell语法
if [ -z $string ],这句话的意思是判断string变量是否为空
-o ,意思是逻辑或
因此第一句话的判断if [ -z "$6" -o "$6" = "NULL" ],意思就很明确了,判断参数$6是否为空或者为NULL,我们传进来的是s3c24x0,所以条件不满足,执行else分支
ln -s ${LNPREFIX}arch-$6 asm-$2/arch,变量LNPREFIX目前为止还未定义,因此为空,我们将参数替换进去,最后的结果是ln -s arch-s3c24x0 asm-arm/arch,这句话创建了一个名为asm-arm/arch的链接文件,指向arch-s3c24x0

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

这句话判断$2是否为arm,显然条件是成立的,经过变量的替换
发现第一句话,是在强行删除一个链接文件,asm-arm/proc
第二句话,创建一个链接文件asm-arm/proc,指向proc-armv

if [ "$2" = "arm" ] ; then
	rm -f asm-$2/proc
	ln -s ${LNPREFIX}proc-armv asm-$2/proc
fi

先解释一下shell脚本中的 > 和 >>, 一个>表示将左边的结果,写到右边的文件(覆盖,从头开始写),两个>>表示追加到右边的文件(从末尾开始写)
前面三句话是输出,最后两句话是判断参数$5和$6是否为空或者为NULL,如果不为空,也不为NULL,则追加两句话到config.mk文件,由于$5为NULL,$6为s3c24x0,因此末尾会有追加SOC=s3c24x0
这里结果就是建立了一个文件,config.mk,该文件最终内容如下
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
SOC = s3c24x0

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

如果APPEND的值为yes,则末尾的两句话,将会以追加(末尾写)的形式,写入config.h
此处我们的的APPEND变量未定义,所以执行else分支,> 的左边什么都没写,意思是创建一个空的文件,名字为config.h,末尾两句话打印到config.h,其中第一句是一个注释信息,第二句是包含一个头文件#include <configs/100ask24x0.h>,最后使用exit退出脚本,返回值是0

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

编译过程的分析

makefile开头的部分,主要作用是生成版本的信息,其中version_autogenerated.h文件是由Makefile自动生成,在后面会将版本信息写进去,其它文件包含它后,就能读取出uboot的版本信息了,这个头文件其实是定义一个宏#define U_BOOT_VERSION "U-Boot 1.1.6"

VERSION = 1     //主版本号
PATCHLEVEL = 1  //补丁版本号
SUBLEVEL = 6    //次版本号
EXTRAVERSION =  //附加信息
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)    //uboot版本 1.1.6
VERSION_FILE = $(obj)include/version_autogenerated.h    //存放版本信息字符串的文件名和目录

之前配置生成的config.mk,在Makefile中使用到了,我们在make的时候,怎么知道要用哪个工具链,就是通过这里指定,生成变量CROSS_COMPILE(如果没有手动指定的话)

# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export	ARCH CPU BOARD VENDOR SOC
...
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
...

这里是CPU是配置文件的arm920t

OBJS  = cpu/$(CPU)/start.o

LIB是将这些编译好的静态库文件,加入到LIBS变量中,LIBS会告诉编译器,要链接哪些库文件

LIBS  = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
	fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)

这就是执行make后第一条编译的语句,目标all依赖于变量ALL
先看u-boot.bin,它又依赖u-boot,u-boot是一个elf格式的二进制文件,在这段的末尾,告诉了我们怎么生成它

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)
		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

$(obj)u-boot: ... 太长省略,关于这一段,如何生成elf格式的u-boot,可以在make命令执行后,生成的打印信息中看出来,在编译完成后,终端末尾打印了这段信息,刚好就是u-boot编译信息的展开

UNDEF_SYM=`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 && 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 \
                        --start-group 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  \
                        -Map u-boot.map -o u-boot

UNDEF_SYM的展开先不用管,后面紧跟的cd /work/system/u-boot-1.1.6,进入这个目录,执行arm-linux-ld进行链接
利用-T指定了链接脚本u-boot.lds的位置,还指定了代码段存放的地址-Ttext 0x33F80000,再后面就是所有的源文件和静态库,最后-o输出u-boot

再看一下链接脚本怎么排放所有的段

SECTIONS
{
	. = 0x00000000;     //这个0会加上0x33F80000,后面的段,将放在0x33F80000之后,按序排放
	. = ALIGN(4);
	.text      :
	{
	  cpu/arm920t/start.o	(.text)                 //先放这个文件的代码段
          board/100ask24x0/boot_init.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段,uboot自己定义的,比较特殊
	__u_boot_cmd_end = .;
	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) }                      //bss段,未初始化的全局变量
	_end = .;
}

分析Makefile的收获

  • 第一个启动的文件:cpu/arm920t/start.S
  • 链接地址:board/100ask24x0/u-boot.lds和0x33F80000

其中0x33F80000的定义见于board/100ask24x0/config.mk,其值为TEXT_BASE = 0x33F80000,然后在u-boot-1.1.6目录下的LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),其中的TEXT_BASE就是所使用的链接地址,这句话展开后形式如下,也可以从make编译后打印信息的末尾中看到展开信息

LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
		   -Bstatic -T /work/system/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000

猜你喜欢

转载自blog.csdn.net/whitefish520/article/details/106246733