Bootloader 即引导加载程序,是系统加电后运行的第一段软件代码。简单的说它们都是bootloader,所完成的任务也大同小异。

熟悉x86体系结构的朋友肯定知道,x86平台上bootloader 是由 BIOS和位于硬盘MBR中的OS Bootloader(比如Lilo 和 Grub)组成的。BIOS完成硬件的检测和资源的分配后,将硬盘MBR中的bootloader读到系统RAM中,之后此bootloader 就会开始进行主导,将内核搬到内存中以及进行一些必要的初始化工作,之后跳到内核的入口地址来执行,这样内核就开始启动,也就是系统就启动起来了。
      这里不得不插入一个话题,通过上面的介绍,细心的朋友就会产生一个疑问:为什么要有bootloader?既然bootloader只是作硬件的初始化并将内核引导起来,那为什么不直接将这段代码加到内核中,直接启动内核就完成所有的工作?实际上要将bootloader与内核整合在一起是完全可以做到的,但是如果这样作的話,内核就会失去他的通用性和灵活性,并且将bootloader与内核分开会更有利于开发和管理,将启动过程中与平台硬件相关的代码集合成bootloader,内核就可以集中处理那些平台通用的部分了(当然实际上并没有这么严格的划分,内核中还是会有一些平台相关的代码,不过已经算是比较通用的了)。

而嵌入式平台上就跟x86不一样了,但是很类似,而且因为不同的平台架构本身的特点,每种平台对应的bootloader做得事情会有所不同,相对x86平台,一般不会有bios(但是这些都不是绝对的,有一些平台也会有内嵌类似bios的启动程序),整个系统的引导加载都由存放在flash,rom等存储设备特定位置的bootloader来完成。如arm平台中的2410,2440,bootloader存在在flash中的0x0的地方,板子加电后,系统会将bootloader的最前面的4k代码通过硬件逻辑自动的装载到SRAM中,之后从SRAM中的0开始执行,在这4k的程序中会完成基本的硬件的初始化,将完整的bootloader搬到内存中,并跳转到ram中的bootloader来进行继续执行。

回到之前所说的,bootloader启动起来之后,通常会有两种操作模式:

启动加载模式就是一上电,bootloader进行相关的初始化之后就马上把内核启动起来,注意关键的地方在整个过程中没有用户的参与,这种其实也就是bootloader的默认处理,一般的产品设计ok进行最后的发布时,就会处于此种状态。

下载模这种模式,大家肯定非常熟悉,就是大家在进行开发的时候所处的环境,我们经常使用的tftp, erase, cp.b 等命令将相关的bin,img文件烧到板子上,这种情况下其实就是处于bootloader的执行环境下,所以一定意义来说,大多的bootloader其实就是一个嵌入式操作系统,只是它的功能不强,不像linux的结构那么复杂,而且也不会支持多进程多线程处理。

bootloader 种类和分类

vivi是mizi开发的用于s3c241x/s3c244x 的linux bootloader,友善之臂移植了USB 下载功能后就成了现在看到的supervivi;u-boot是一个广泛用于ARM平台的bootloader, 目前也支持s3c241x/s3c244x,可以用来启动Linux;Eboot是WinCE平台下的bootloader。uboot就是通过usb来下载os image文件的bootloader; eboot就是通过ethernet下载os image的bootloader。

这里的分类实际上是依据上面的bootloader的操作模式来进行划分的,根据一个系统是否支持上面的下载模式我们这里将bootloader划分为bootloader和monitor(这不是我划分的,恩,是从别人的文章中引述过来的,不过我觉得他说的很有道理), 这里”bootloader”是指只是引导设备与执行主程序的固件,而”monitor”是指不仅拥有bootloader功能的,还能够进入下载模式的固件。
===============================================================================================

(1)、ARM程序的组成

此处所说的“ARM程序”是指在ARM系统中正在执行的程序,而非保存在ROM中的bin映像(image)文件,这一点清注意区别。

一个ARM程序包含3部分:RO,RW和ZI           
        RO是程序中的指令和常量;RO就是readonly,
        RW是程序中的已初始化变量; RW就是read/write,

ZI是程序中的未初始化的变量;ZI就是zero;                 
(2) ARM映像文件的组成
    所谓ARM映像文件就是指烧录到ROM中的bin文件,也成为image文件。以下用Image文件来称呼它。Image文件包含了RO和RW数据。之所以Image文件不包含ZI数据,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。
    Q:为什么Image中必须包含RO和RW?         
    A:因为RO中的指令和常量以及RW中初始化过的变量是不能像ZI那样“无中生有”的。

(3)ARM程序的执行过程

从以上两点可以知道,烧录到ROM中的image文件与实际运行时的ARM程序之间并不是完全一样的。因此就有必要了解ARM程序是如何从ROM中的image到达实际运行状态的。 
  实际上,ROM中的指令至少应该有这样的功能:
            1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
            2. 将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中.在程序运行的最初阶段,RO中的指令完成了这两项工作后C程序才能正常访问变量。否则只能运行不含变量的代码。
    下面我将给出几个例子,最直观的来说明RO,RW,ZI在C中是什么意思。
        1; RO
        看下面两段程序,他们之间差了一条语句,这条语句就是声明一个字符常量。因此按照我们之前说的,他们之间应该只会在RO数据中相差一个字节(字符常量为1字节)。        
Prog1:
            #include <stdio.h>
            void main(void)
            {
                ;
            }         
Prog2:
            #include <stdio.h>
            const char a = 5;
            void main(void)
            {           
                ;
            }
 Prog1编译出来后的信息如下:          
================================================================================          
Code RO Data RW Data ZI Data Debug
            948 60 0 96 0 Grand Totals         
================================================================================        
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)          
================================================================================           
Prog2编译出来后的信息如下:          
================================================================================         
Code RO Data RW Data ZI Data Debug
            948 61 0 96 0 Grand Totals           
================================================================================          
Total RO Size(Code + RO Data) 1009 ( 0.99kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)           
================================================================================         
以上两个程序编译出来后的信息可以看出:
   Prog1和Prog2的RO包含了Code和RO Data两类数据。他们的唯一区别就是Prog2的RO Data比Prog1多了1个字节。这正和之前的推测一致。如果增加的是一条指令而不是一个常量,则结果应该是Code数据大小有差别。
     2; RW
     同样再看两个程序,他们之间只相差一个“已初始化的变量”,按照之前所讲的,已初始化的变量应该是算在RW中的,所以两个程序之间应该是RW大小有区别。     
    Prog3:
            #include <stdio.h>
            void main(void)
            {
                ;
            }           
    Prog4:
            #include <stdio.h>
            char a = 5;
            void main(void)
            {         
                ;
            }
            Prog3编译出来后的信息如下:           
================================================================================          
Code RO Data RW Data ZI Data Debug
            948 60 0 96 0 Grand Totals         
================================================================================           
Total RO Size(Code + RO Data) 1008 ( 0.98kB) 
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB) 
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)           
================================================================================       
Prog4编译出来后的信息如下:         
================================================================================      
Code RO Data RW Data ZI Data Debug
            948 60 1 96 0 Grand Totals          
================================================================================          
Total RO Size(Code + RO Data) 1008 ( 0.98kB) 
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)            
================================================================================       
可以看出Prog3和Prog4之间确实只有RW Data之间相差了1个字节,这个字节正是被初始化过的一个字符型变量“a”所引起的。

3; ZI
再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。          
Prog3:
            #include <stdio.h>
            void main(void)
            {
            ;
            }        
Prog4:
            #include <stdio.h>
            char a;
            void main(void)
            {      
                ;
            }
Prog3编译出来后的信息如下:        
================================================================================          
Code RO Data RW Data ZI Data Debug
948 60 0 96 0 Grand Totals           
================================================================================           
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 96 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)          
================================================================================        
Prog4编译出来后的信息如下:         
================================================================================         
Code RO Data RW Data ZI Data Debug
            948 60 0 97 0 Grand Totals         
================================================================================          
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1008 ( 0.98kB)           
================================================================================           
编译的结果完全符合推测,只有ZI数据相差了1个字节。这个字节正是未初始化的一个字符型变量“a”所引起的。
注意:如果一个变量被初始化为0,则该变量的处理方法与未初始化华变量一样放在ZI区域。          
即:ARM C程序中,所有的未初始化变量都会被自动初始化为0。
    总结:  1; C中的指令以及常量被编译后是RO类型数据。
               2; C中的未被初始化或初始化为0的变量编译后是ZI类型数据。
               3; C中的已被初始化成非0值的变量编译后市RW类型数据。

(3)、下面是在ADS下,一种常用存储器模型的直接实现:
         LDRr0,=|Image$$RO$$Limit|;RO结束后的地址(在rom中):即得到RW数据源的起始地址
         LDRr1,=|Image$$RW$$Base|;RW区在RAM里的执行区起始地址
         LDRr3,=|Image$$ZI$$Base|;ZI区在RAM里面的起始地址

|Image$$RO$$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址
 |Image$$RW$$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址
 |Image$$ZI$$Base|:ZI区在RAM里面的起始地址
 |Image$$ZI$$Limit|:ZI区在RAM里面的结束地址后面的一个地址
  程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里面|Image$$RW$$Base|开始的地址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地址|Imag开机运行程序设置e$$ZI$$Limit|

(4) ROM主要指:NAND Flash,Nor Flash

RAM主要指:PSRAM,SDRAM,SRAM,DDRAM

(5) Image$$??$$Limit 的含义
=0x0c100000+Tatal RO size+1
                   = 0x0c200000+0x37(4的倍数,0到55,共56个单元)

对于刚学习ARM的人来说,如果分析它的启动代码,往往不明白下面几个变量的含义:|Image$$RO$$Limit|、|Image$$RW$$Base|、|Image$$ZI$$Base|。

当把程序编写好以后,就要进行编译和链接了,在ADS1.2中选择MAKE按钮,会出现一个Errors and Warnings的对话框,在该栏中显示编译和链接的结果,如果没有错误,在文件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据:

Code 163632 ,RO Data 20939 ,RW Data 53 ,ZI Data 17028

Tatal RO size (Code+ RO Data)             184571 (180.25kB)

Tatal RW size(RW Data+ ZI Data)           17081(16.68 kB)

Tatal ROM size(Code+ RO Data+ RW Data)   184624(180.30 kB)

后面的字节数是根据用户不同的程序而来的,下面就以上面的数据为例来介绍那几个变量的计算。

在ADS的Debug Settings中有一栏是Linker/ARM Linker,在output选项中有一个RO base选项,

假如 RO base设置为0x0c100000,后面的RW base 设置为0x0c200000,然后在Options选项中有Image entry point ,是一个初始程序的入口地址,设置为0x0c100000 。

有了上面这些信息我们就可以完全知道这几个变量是怎么来的了:

|Image$$RO$$Base| = Image entry point =RO base =0x0c100000 ;表示程序代码存放的起始地址

|Image$$RO$$Limit|=程序代码起始地址+代码长度+1

= 0x0c100000 + 184571 + 1 = 0x0c100000 +0x2D0FB + 1

= 0x0c12d0fc

|Image$$RW$$Base| = 0x0c200000=RW base 地址指定

|Image$$RW$$Limit| =|Image$$RW$$Base|+ RW Data 53

=0x0c200037

|Image$$ZI$$Base| = |Image$$RW$$Limit| + 1 =0x0c200038

|Image$$ZI$$Limit| = |Image$$ZI$$Base| + ZI Data 17028

=0x0c200038 + 0x4284

=0x0c2042bc

也可以由此计算:

|Image$$ZI$$Limit| = |Image$$RW$$Base| +TatalRWsize(RWData+ZIData) 17081

=0x0c200000+0x42b9+3(要满足4的倍数)

=0x0c2042bc

随机推荐

  1. tab左右箭头切换(修改后)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. HDU1019

    Least Common Multiple Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Ot ...

  3. 关于ASPCMS标签调用的一些总结

    菜单的应用 <ul class="nav navbar-nav"> {aspcms:navlist} {}<!--判断是否有下级目录--> <li c ...

  4. js 进度条,可实现结束和重新开始

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  5. spring security remember me实现自动登录

    1 默认策略 在我们自定义的login中增加一个选择框 <input type="submit" value="Login" /> <br/& ...

  6. dede数据库类使用方法 $dsql

    dedecms的数据库操作类,非常实用,在二次开发中尤其重要,这个数据库操作类说明算是奉献给大家的小礼物了. 引入common.inc.php文件 require_once (dirname(__FI ...

  7. BizTalk开发系列(三) 单机环境下的BizTalk Server 2006 R2安装

    大部分的开发环境都是在单机环境下进行的,今天整理了一下BizTalk Server 2006 R2在单机环境下的安装步骤. 1. 软件需求 在独立服务器中完整安装BizTalk Server 2006 ...

  8. php课程---Json格式规范需要注意的小细节

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. 它基于JavaScript Programming Lan ...

  9. php中htmlspecialchars和htmlentiti

    Certain characters have special significance in HTML, and should be represented by HTML entities if ...

  10. tomcat服务重启linux

    1杀掉tomcat 进程  用ssh登陆到服务器 lsof -i:8080         //找到端口 ps -ef|grep tomcat kill -9 端口 2找到tomcat目下的start ...