文章目錄
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"
下面分析參數的個數是否正常,@#
就代指參數的個數,注意@#
不把#值是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