mysterybootloader與u-boot詳解

很多人在刚接触这两个东西的时候,可能和我一样,对这两个东东有不少疑惑,于是我查阅了相关资料,整理如下

------------------------------------------------------------------

Bootloader的引入

在系统上电之后,需要一段程序来进行初始化:关闭WATCHDOG、改变系统时钟、初始化存储控制器、将更多的代码复制到内存中等等。如果它能将操作系统内核(无论从本地,比如Flash;还是从远端,比如通过网络)复制到内存中运行,就称这段程序为Bootloader。

简单地说,Bootloader就是这么一小段程序,它在系统上电时开始执行,初始化硬件设备、准备好软件环境,最后调用操作系统内核。

Bootloader的实现严重依赖于具体硬件,在嵌入式系统中硬件配置千差万别,即使是相同的CPU,它的外设(比如Flash)也可能不同,所以不可能有一个Bootloader支持所有的CPU、所有的电路板。即使是支持CPU架构比较多的U-Boot,也不是一拿来就可以使用的(除非里面的配置刚好与你的板子相同),需要进行一些移植。

-----------------------------------------------------------------------

U-Boot与Bootloader的关系

我们可以增强Bootloader的功能,比如增加网络功能、从PC上通过串口或网络下载文件、烧写文件、将Flash上压缩的文件解压后再运行等──这就是一个功能更为强大的Bootloader,也称为Monitor。U-boot就是这样一种bootloader,全功能体积高达数百Kbytes,实际上,在最终产品中用户并不需要这些功能,它们只是为了方便开发。

U-Boot全称是Universal Bootloader,也是一款开源项目,作为Bootloader的一种,具备多种引导内核启动的方式。常用的go和bootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。

-----------------------------------------------------------------------

bootloader的作用

那我们也知道其实OS(如linux)的Kernel其实也是一段裸机程序,也是直接掌管硬件的,不过这个程序极其复杂,许许多多的计算机专家和黑客为其耗费心血,如果三四百K的U-boot已经比较复杂,那么一个典型功能的linux Kernel编译后竟然能高达万Kbytes,光从代码量来看,linux的复杂度已经是U-boot的百倍。而其实远不止。

虽然kernel很complex,但终究不过是一段裸机代码,Bootloader进行所谓的“kernel引导”,其过程不过是从bootloader里的一句跳转代码,跳转到kernel代码处(执行kernel中的第一个函数),所谓传递参数也不过是bootloader和kernel约定一个内存地点存放。在这个过程中,bootloader和kernel虽然都处于同一个内存里,但是它们除了“引导”与“传递有限的参数”这样的关系,并无其它关系,完全是两个独立的程序。之所以在kernel之前用一个bootloader来引导(为什么开机不能直接执行kernel,所有事情都交给kernel做?),其思想类似于一个板级支持包:kernel假定执行的时候已经具备了一个基本的硬件运行条件,这个环境的初始化(最底层的一些硬件初始化、硬件信息设定)需要bootloader来完成,也许这样kernel的设计才能保持一定的一致性。

说到底,bootloader是一段裸机程序,是直接与硬件打交道的。① 它在系统上电后开始执行(PC系统中在bootloader之前有个BIOS固件,嵌入式系统中一般没有),其最终目的是“初始化硬件设备,准备好软件环境,最后调用操作系统内核”。② 为了方便进行底层开发(比如调试内核等),bootloader增加了很多功能,主要有UART、网络、USB、读写Flash(Nor or Nand)、解压缩、LCD支持等等。

-------------------------------------------------------------------

通用概念

在一个嵌入式Linux系统中,从软件的角度通常可以分为4个层次:

(1)引导加载程序。

包括固化在固件(firmware)中的 boot 代码(可选)和Bootloader两大部分。

有些CPU在运行Bootloader之前先运行一段固化的程序(固件,firmware),比如x86结构的CPU就是先运行BIOS中的固件,然后才运行硬盘第一个分区(MBR)中的Bootloader。

在大多嵌入式系统中并没有固件,Bootloader是上电后执行的第一个程序。

(2)Linux内核。

特定于嵌入式板子的定制内核以及内核的启动参数。内核的启动参数可以是内核默认的,或是由Bootloader传递给它的。

3)文件系统。

包括根文件系统和建立于Flash内存设备之上的文件系统。里面包含了Linux系统能够运行所必需的应用程序、库等,比如可以给用户提供操作Linux的控制界面的shell程序,动态连接的程序运行时需要的glibc或uClibc库,等等。

(4)用户应用程序。

特定于用户的应用程序,它们也存储在文件系统中。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:Qtopia 和 MiniGUI 等。

----------------------------------------------------------------------

Bootloader的启动方式

CPU上电后,会从某个地址开始执行。比如MIPS结构的CPU会从0xBFC00000取第一条指令,而ARM结构的CPU则从地址0x0000000开始。嵌入式单板中,需要把存储器件ROM或Flash等映射到这个地址,Bootloader就存放在这个地址开始处,这样一上电就可以执行。

在开发时,通常需要使用各种命令操作Bootloader,一般通过串口来连接PC和开发板,可以在串口上输入各种命令、观察运行结果等。这也只是对开发人员才有意义,用户使用产品时是不用接串口来控制Bootloader的。从这个观点来看,Bootloader可以分为两种操作模式(Operation Mode):

(1)启动加载(Boot loading)模式。

上电后,Bootloader从板子上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。产品发布时,Bootloader工作在这种模式下。

(2)下载(Downloading)模式。

在这种模式下,开发人员可以使用各种命令,通过串口连接或网络连接等通信手段从主机(Host)下载文件(比如内核映像、文件系统映像),将它们直接放在内存运行或是烧入Flash类固态存储设备中。

板子与主机间传输文件时,可以使用串口的xmodem/ymodem/zmodem协议,它们使用简单,只是速度比较慢;还可以使用网络通过tftp、nfs协议来传输,这时,主机上要开启tftp、nfs服务;还有其他方法,比如USB等。

像Blob或U-Boot等这样功能强大的Bootloader通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,U-Boot在启动时处于正常的启动加载模式,但是它会延时若干秒(这可以设置)等待终端用户按下任意键而将U-Boot切换到下载模式。如果在指定时间内没有用户按键,则U-Boot继续启动Linux内核。

------------------------------------------------------------------

Bootloader的两个阶段

Bootloader的启动过程启动过程可以分为单阶段(Single Stage)、多阶段(Multi-Stage)两种。通常多阶段的Bootloader能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的Bootloader大多都是 2 阶段的启动过程。这从前面的硬件实验可以很好地理解这点:第一阶段使用汇编来实现,它完成一些依赖于 CPU 体系结构的初始化,并调用第二阶段的代码。第二阶段则通常使用C语言来实现,这样可以实现更复杂的功能,而且代码会有更好的可读性和可移植性。

一般而言,这两个阶段完成的功能可以如下分类,但这不是绝对的:

(1)Bootloader第一阶段的功能。

硬件设备初始化。

为加载Bootloader的第二阶段代码准备RAM空间。

拷贝Bootloader的第二阶段代码到 RAM 空间中。

设置好栈。

跳转到第二阶段代码的C入口点。

在第一阶段进行的硬件初始化一般包括:关闭WATCHDOG、关中断、设置CPU的速度和时钟频率、RAM初始化等。这些并不都是必需的,比如S3C2410/S3C2440的开发板所使用的U-Boot中,就将CPU的速度和时钟频率的设置放在第二阶段。

甚至,将第二阶段的代码复制到RAM空间中也不是必需的,对于NOR Flash等存储设备,完全可以在上面直接执行代码,只不过这相比在RAM中执行效率大为降低。

(2)Bootloader第二阶段的功能。

初始化本阶段要使用到的硬件设备。

检测系统内存映射(memory map)。

将内核映像和根文件系统映像从Flash上读到RAM空间中。

为内核设置启动参数。

调用内核。

为了方便开发,至少要初始化一个串口以便程序员与Bootloader进行交互。

-------------------------------------------------------------------------

U-Boot工程简介

U-Boot,全称为Universal Boot Loader,即通用Bootloader,是遵循GPL条款的开放源代码项目。其前身是由德国DENX软件工程中心的Wolfgang Denk基于8xxROM的源码创建的PPCBOOT工程。后来整理代码结构使得非常容易增加其他类型的开发板、其他架构的CPU(原来只支持PowerPC);增加更多的功能,比如启动Linux、下载S-Record格式的文件、通过网络启动、通过PCMCIA/CompactFLash/ATA disk/SCSI等方式启动。增加ARM架构CPU及其他更多CPU的支持后,改名为U-Boot。

它的名字“通用”有两层含义:可以引导多种操作系统、支持多种架构的CPU。它支持如下操作系统:Linux、NetBSD、 VxWorks、QNX、RTEMS、ARTOS、LynxOS等,支持如下架构的CPU:PowerPC、MIPS、x86、ARM、NIOS、XScale等。

U-Boot有如下特性:

开放源码;

支持多种嵌入式操作系统内核,如Linux、NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS;

支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;

较高的可靠性和稳定性;

高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;

丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;

较为丰富的开发调试文档与强大的网络技术支持;

支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统

支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;

可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤对Linux支持最为强劲;

支持目标板环境变量多种存储方式,如FLASH、NVRAM、EEPROM;

CRC32校验,可校验FLASH中内核、RAMDISK镜像文件是否完好;

上电自检功能:SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号;

特殊功能:XIP内核引导;

-------------------------------------------------------------------------

U-Boot的启动过程源码分析

U-Boot属于两阶段的Bootloader,第一阶段的文件为cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S,前者是平台相关,后者是开发板相关。

U-Boot第一阶段代码分析

它与前面描述的Bootloader第一阶段所完成的功能可以一一对应:

(1)硬件设备初始化。

依次完成如下设置:将CPU的工作模式设为管理模式(svc),关闭WATCHDOG,设置FCLK、HCLK、PCLK的比例(即设置CLKDIVN寄存器),关闭MMU、CACHE。

(2)为加载Bootloader的第二阶段代码准备RAM空间。

所谓准备RAM空间,就是初始化内存芯片,使它可用。

-------------------------------------------------------------------

参考资源

U-boot百度百科:http://baike.baidu.com/view/1348254.htm

Bootloader百度百科:http://baike.baidu.com/view/1223454.htm

Bootloader(U-boot):http://hi.baidu.com/firstm25/item/5a716411073a414fe75e068d

Bootloader(U-boot)学习研究心得:http://www.cnblogs.com/andrew-wang/archive/2012/12/09/2810483.html

u-boot 移植步骤详解:http://www.lisdn.com/html/38/n-10038.html

UBOOT和bootloader的关系:http://blog.sina.com.cn/s/blog_4c66653101000cda.html

Database error: [Table 'ac_search_cache' is marked as crashed and should be repaired]

SELECT * FROM ac_search_cache WHERE hash = '2f1cd71c2e61f3a47c29b760711b1e7f06aadc01' LIMIT 1;