Bootloader类似于Windows里的BIOS,用于启动Linux内核,其本质也是一段单片机程序,用于初始化硬件和搭建Linux启动环境。例如关看门狗,关中断,初始化Flash等。初始化完成以后就会引导内核启动。
Bootloader的启动大体可以分为两个部分,第一阶段使用汇编编写,用于做前期准备工作。可以这么理解第一阶段:
第一阶段的准备类似于我们小时候玩的跳方格。
第一步:我们得去找一根粉笔或者可以刻出字的石头=>初始化硬件。
第二步:准备好工具后,我们需要找场地,用粉笔画出我们要游戏的范围=>也就是准备Bootloader需要的RAM空间。
第三步:我们要把小朋友请到这里游戏 => Bootloader会将第二阶段的代码拷贝到RAM空间
第四步:给方格标记跳的顺序 => 设置栈
第五步:站在1号方格开始游戏 => 跳转到函数入口处
完成第一阶段的硬件准备工作后Bootloader将会跳转到第二段代码,这一阶段主要用于内核启动的前期工作,主要使用C语言编写的,可以这么理解这一阶段的操作:类似于打群架:
第一步:准备打斗工具:硬邦邦的拳头或者大棒 => 初始化调试用的硬件用于程序员与Bootloader进行交互
第二步:观察一下对方人数和位置 => 确认单板上有多少内存和地址空间是多少
第三步:过去把对方拖到计划的位置打一顿 => 将内核映射读到RAM后解压缩
第四步:在要害处拳拳留印记 => 在指定的地址处设置内核启动参数
第五步:最后踹上一脚 => 启动内核
在第二阶段完成后启动内核之前,还有初始化CPU设置,寄存器设置,禁止中断等操作。完成这些操作之后,会在内存的指定位置存放内核需要的一些参数,内核会到这个指定的地址里获取需要的参数。这个参数是一个结构体,内部一个tag_header和联合体,tag_header用于表示参数的类型,不同的tag_header调用不同的union联合体。
单板用的最多的Bootloader是U-boot,其对应关系就是手机和小米的对应关系。启动过程和实现的功能不同的Bootloader大体相同。那么如何使用U-boot和编译进单板?
首先在官网下载自己需要的u-boot源码,查看顶层目录的Makefile,里面回复告诉如何make config自己需要的,一般命名为单板名_config,配置完成后执行make all,将生成的.bin文件通过NFS,OFlash下载到单板上
我们将U-boot烧写进板子后启动,然后启动开发板。用串口进行监听,会发现u-boot启动时候会出现一段倒计时,此时我们按下任何一个按键,u-boot会进入下载模式,该模式是用于给开发者进行调试安装使用的。
四、Bootloader之uboot
一、uboot的编译体验
源码结构分析:Uboot共有26个子目录,大致可以分为4类:
编译步骤:解压缩uboot文件---->打补丁----->配置----->编译
解压缩:tar xjf u-boot-1.1.6.tar.bz2--->跳转到解压缩后的文件夹内
1)怎么打补丁
补丁文件中修改过的代码表示:“---” 表示是 原来代码 。“+++”表示修改后的代码。
执行命令:patch -p? <..补丁文件(?表示忽略前面文件目录的总数)
例如:patch -p1 <../u-boot-1.1.6_jz2440.patch
解释:-p1为补丁文件内增加的内容里第一个斜杆之前的目录
2)怎么配置
执行命令:make 100ask24x0_config
3)怎么编译:make
二、Makefile结构分析
1)配置命令 :make smdk2440_config 执行什么?
在Makefile中可以找到下面代码
...........
MKCONFIG := $(SRCTREE)/mkconfig
........
smdk2440_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 samsung s3c24x0
$(@:_config=)的结 果就是将”smdk2440_config“中的_config去掉,结果为“smdk2440”.所以“make smdk2440_config”实际上就是执行如下命令:
./mkconfig smdk2440 arm arm920t smdk2410 NULL s3c24x0
MKCONFIG := $(SRCTREE)/mkconfig的意思是在源文件下有一个mkconfig文件,打开它,会看到下面这个
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
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}" ] || BOARD_NAME="$1"
对于上面执行的./mkconfig smdk2440 arm arm920t smdk2440 NULL s3c2440 这条命令中没有--, -a ,-n等,所以不会这行这段代码,[ "${BOARD_NAME}" ] || BOARD_NAME="$1"则是将BOARD_NAME命名为s3c2440
所以我们接着往下看,会看到这段
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
fi
if [ "$SRCTREE" != "$OBJTREE" ] ; then判断源代码目录和目标文件目录是否是一样,如果条件不满足,将执行else分支的代码,进入include目录,删除asm文件,然后再次建立 asm文件,并令它链接向asm-$2目录,即asm-arm
然后对相关参数进行配置
#
# Create include file for Make
#
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
由./mkconfig smdk2440 arm arm920t smdk2410 NULL s3c24x0可得
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC =s3c24x0
接着创建开发板的头文件include/config.h
#
# Create board specific header file
#
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
APPEND维持原值no时 if [ "$APPEND" = "yes" ]不执行所以执行> config.h 创建一个config.h头文件,并将注释"/* Automatically generated - do not edit */" 和头文件#include <configs/$1.h>和#include <asm/config.h>包含进去
看完了mkconfig文件,返回根文件目录下的Makefile,继续往下看,看到这段代码
# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export ARCH CPU BOARD VENDOR SOC
ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),ppc)
CROSS_COMPILE =
else
ifeq ($(ARCH),ppc)
CROSS_COMPILE = powerpc-linux-
endif
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
ifeq ($(ARCH),i386)
ifeq ($(HOSTARCH),i386)
CROSS_COMPILE =
else
CROSS_COMPILE = i386-linux-
endif
endif
ifeq ($(ARCH),mips)
CROSS_COMPILE = mips_4KC-
endif
ifeq ($(ARCH),nios)
CROSS_COMPILE = nios-elf-
endif
ifeq ($(ARCH),nios2)
CROSS_COMPILE = nios2-elf-
endif
ifeq ($(ARCH),m68k)
CROSS_COMPILE = m68k-elf-
endif
ifeq ($(ARCH),microblaze)
CROSS_COMPILE = mb-
endif
ifeq ($(ARCH),blackfin)
CROSS_COMPILE = bfin-elf-
endif
ifeq ($(ARCH),avr32)
CROSS_COMPILE = avr32-
endif
endif
endif
export CROSS_COMPILE
# load other configuration
include $(TOPDIR)/config.mk
第 一行中包含的config.mk文件,就是在第一开始配置过程中制作出来的include/conifg.mk文件,我们在一开始配置U-boot时执行 时生成的文件,其中定义了ARCH,CPU,BOARD,SOC等。
最后一句话include $(TOPDIR)/config.mk 包含顶层目录的config.mk文件。它根据上面4个变量的值确定了编译器。编译选项等。
2)分析编译过程:0
①第1个文件:cpu/arm920t/start.S
②链接地址:board/100ask24x0_u-boot.lds+0x33F80000
三、u-boot源码分析
1)第一阶段(硬件相关的初始化)
a>设置SVC模式: set the cpu to SVC32 mode 将 CPU 设置为 SVC32 管理模式
b>关看门狗
c>屏蔽中断
d>初始化SDRAM
e>设置栈
f>时钟
g>代码Flash-->SDRAM(重定位)
h>清BSS段
i>调用start_armboot