(1)启动加载模式:这种模式也称为“自主”模式。也就是Bootloader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入,这种模式是在嵌入式产品发布里的通用模式。
(2) 下载模式:在这种模式下,目标机上的Bootloader将通过串口连接或网络连接等通信手段从主机下载文件,例如:下载内核映像和根文件系统映像等。从 主机下载的文件 通常首先被Bootloader保存到目标机的RAM中,然后再被Bootloader写到目标上的Flash类的固态存储设备中,Bootloader 的这种模式是在在开发时使用的工作于这种模式的Bootloader通常都 会向它的终端用户提供一个简单的命令行接口。
在嵌入式Linux系统中从软件的角度通常可以分为4个层次:
(1)引导加载程序,包括固化在固件中的boot代码(可选)和Bootloader两大部分。
有些CPU在运行Bootloader之前运行一段固化的程序 ,比如x86结构的CPU就是先运行BIOS中的固件,然后才运行硬盘的第一个分区中的BootLoader。在大多数的嵌入式系统中并没有固件,Bootloader是上电后第一个执行的程序。
(2)Linux内核
嵌入式定制的内核以及启动参数,启动参数可以是Bootloader传递给内核的,也可以是内核默认的。
(3)文件系统
包括根文件系统和建立于Flash内存设备之上的文件系统。里面包括了Linux系统能够运行所必要的应用程序和库文件等。比如可以给用户提供操作Linux的控制shell程序。
(4)用户应用程序
特定于用户的应用程序,它们也存储在文件系统中,有时在用户应用程序和内核层之间可以还会包括一个嵌入式图形用户界面。
2. Bootloader启动的两个阶段
从固态存储设备上启动的Bootloader大多都是两阶段的启动过程,第一阶段使用汇编来实现。它完成一些依赖于CPU体系结构的初始化,并调用第二阶段的代码,第二阶段则通常使用C语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。
(1) Bootloader第一阶段的功能
1)硬件设备初始化
2)为加载Bootloader的第二阶段准备RAM空间。
3)复制Bootloader的第二阶段代码到RAM空间中。
4)设置好栈
5)跳转到第二阶段代码的C入口点。
在第一阶段进行的硬件初始化一般包括:关闭WATCHDOG,关中断,设置 CPU的速度和时钟频率RAM初始化等。这些不都是必需的。
(2)Bootloader第二阶段的功能
1)初始化本阶段要使用的硬件设备
2)检测系统内存映射
3)将内核映像和根文件系映象从Flash望到RAM空间中
4)为内核设置启动参数
5)调用内核
将内核存放在适当的位置后,直接跳到它的入口点即可调用内核,调用内核之前,下列条件要满足
(1)CPU寄存器的设置
R0=0.
R1=机器类型ID;对于ARM结构的CPU,其机器类型ID在linux/arch/arm/tools/mach-types
R2=启动参数标记列表在RAM中起始基地址
(2)CPU工作模式
必须禁止中断(IRQs和FIQs)
CPU必须为SVC模式
(3)Cach和MMU的设置
MMU必须关闭
指令Cach可以打开也可以关闭
数据Cach必须关闭
U-boot.bin:二进制可执行文件,它就是可以直接烧入ROM,NORFlash的文件
u-Boot:ELF格式的可执行文件,
U-Boot.srec:Motorla S-Record格式的可执行文件
对于S3C2410的开发板,执行”make smdk2410_config“."make all"后生成的U-Boot.bin可以烧入NOR Flash中运行,启动后可以看到串口输出一些信息后进行控制界面。
|
这是在根目录下的MAKEFILE文件中的两个语句,其中的MKCONFIG就是根目录下的mkconfi文件。$(@:_config=)的结 果就是将”smdk2410_config“中的_config去掉,结果为“smdk2410”.所以“make smdk2410_config”实际上就是执行如下命令:
|
|
(1)确定开发板名称BOARD_NAME,相关代码如下:
|
|
(2)创建到平台开发板相关折头文件的链接
|
|
|
|
|
APPEND维持原值"NO",所以config.h被重新建立,也就是执行echo "#include <configs/$1.h>" >>config.h
#include <configs/smdk2410.h>
总之,当你执行make smdk2410_config ,实际的作用就是执行./mkconfig smdk2410 arm arm920t smdk2410 NULL s3c24x0,它将产生如下的
几种作用
(1) 开发板的名称 BOARD_NAME等于 $1
(2)创建到平台,开发板相关的头文件的链接,如下所示
ln -s asm-$2 asm
ln -s arch-$6 asm-S2/arch
ln - s proc-armv asmn-$2/proc 如果$2不是arm的话,此行没有
(3)创建顶层Makefile包含的incldue /config.mk,如下所示
ARCH = $2
CPU = $3
BOARD = $4
VENDOR = $ $5 为空,或者NULL的话,些行没有
SOC = $6
(4) 创建开发板相关的头文件include/config.h,如下 所示
#include <config.h/$1.h>
从上面执行完命令后的结果,可以看出来,如果要在board目录下新建一个开发板<board_name>的目录,则在 include/configs 目录下也要建立一个文件<board_name>.h,里面存放的就是开发板<board_name>的配置信息。
3.U-Boot的编译,连接过程
|
第 一行中包含的config.mk文件,就是在第一开始配置过程中制作出来的include/conifg.mk文件,我们在一开始配置U-boot时执行 过mkconfig。mini2440 时生成的文件,其中定义了ARCH,CPU,BOARD,SOC等。4个变量的值为arm,arm920t,smdk2410,s3c24x0.我们在执 行mkconfig。mini2440时,其实执行的是如下的命令:
|
|
继续分析MAKEFIle文件:
|
|
|
|
总结一下U-Boot的编译流程:
(1)首先编译cpu/$(CPU)/start.s,对于不同的CPU,还可能编译cpu/$(CPU)下面的其他文件。
(2)然后,对于平台开发板相关的每个目录,每个通用目录都使用它们各自的MAKEFILE生成相应和库。
(3)将1,2步骤生成的.o.a文件按照$(BOARDDIR)/config.mk 文件中指定的代码段起始地址。$(obj)u-boot.lds 连接脚本进行连接。
(4)第3步得到的是ELF格式的U-Boot,后面MAKEFILE还会将它转换为二进制格式 S-Record格式。
(1)硬件设备初始化
依次完成如下设置:将CPU的工作模式设为管理模式(SVC),关闭WATCHDOG,设置FCLK,HCLK,PCLK的比例,关闭MMU,CACHE。代码在cpu/arm920t/start.S中,
(2)为加载Bootloader的第二阶段代码准备RAM空间。
所谓准备RAM空间,就是初始化内存芯片,使它可用,对于S3C24x0,通过在Start.S中调用lowlevel_init函数来设置存储控制器,使得外接
|
这里将整个U-Boot代码都复制到SDRAM中,这在cpu/arm920t/start.s中实现
|
(4)设置好栈
|
在跳转之前,还要清除BSS段(初始值0,无初始值的全局变量,静态变量放在BSS段。
|
|
第二阶段从lib_arm/borad.c中的start_armboot函数开始,程序的流程如下 :
board_init 函数设置MPLL,改变系统时钟,它是开发板相关函数。board\samsung\smdk2410/smdk2410.c中实现。串口的初始化函数主 要是serial_init,它设置UART控制器,是CPU相关的函数,在cpu/arm920t/s3c24x0/serial.c中实现。
|
(3) 为内核设置启动参数
在start_armboot()函数的最后,调用main_loop()函数,进行一个无限循环,该函数在common/main.c文件中定义。
|
|
|
|
|
|
image是bootm_headers结构体的指针,可以在inlcude/image.h文件中看到这个结构体的定义如下:
|
|
要知道哪个地址是启动内核,哪个地址启动文件系统,要分析common/cmd_bootm.c中的函数 do_bootm,因为引导kernel就是bootm这条命令的工作,do_bootm是命令bootm的执行函数现在我们来分析一下 common/cmd_bootm.c中的函数do_bootm,这是bootm命令的处理函数.do_bootm()函数中的很多功能都是分成了函数的 形式,而在以前的版本中没有这么有结构层次,这里我们也只是分析对引导Linux内核有作用的部分,因为这是一个在common文件夹下的文件,也就意味 着,在引导别的操作系统时也会用到这个函数,而不单单是Linux操作系统.
|
|
uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。
mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象 是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么?到这里整个U-Boot是如何启动Linux内核的,基本上也就清楚了,特别是如何向Linux内核传送的参 数。
|