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"

下面分析參數的個數是否正常,@#就代指參數的個數,注意@#不把00算在內,因此這裏的#值是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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章