文章目录
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