伏牛堂米粉加盟费多少:7.2 引导装入程序的挑战

来源:百度文库 编辑:偶看新闻 时间:2024/05/03 06:18:29

    7.2 引导装入程序的挑战

    即使是简单的"Hello World"C语言程序也需要相当数量的硬件和软件资源。应用程序开发人员并不需要知道或者过多地关心其中的细节,因为C运行时环境已经悄无声息地提供了这些基础结构。引导装入程序开发人员可没这么幸运,引导装入程序需要的每种资源在使用之前都必须进行详尽的初始化并完成资源分配。其中最明显的例子莫过于DRAM(动态随机访问内存)。

    7.2.1 DRAM控制器

    DRAM芯片不能像其他微处理器总线资源那样直接读写,它们需要专门的硬件控制器来支持读写周期。DRAM必须定时刷新,否则其中的数据就会丢失,这会使事情更加错综复杂。刷新操作的实现方式是按照一定规律,并按DRAM厂商规定的时序规范读取DRAM的每个存储单元。目前的DRAM芯片支持多种操作模式,例如针对高性能应用的突发模式和双倍数据速率(DDR)。配置DRAM并保证其按照生产商的时序规范进行刷新,是DRAM控制器的职责,它还负责响应来自处理器的各种读写命令。

    设置DRAM控制器是嵌入式开发新手容易受挫的地方。完成这项工作需要详细了解DRAM体系结构、控制器本身、所采用的特定DRAM芯片以及总体的硬件设计。这些内容已经超出本书的范围,对此感兴趣的读者可以阅读本章末尾的参考资源来进一步了解这个重要的概念。附录C介绍了关于这个主题的更多背景知识。

    在嵌入式系统中,如果不对DRAM控制器和DRAM进行适当的初始化,基本上什么都做不了。引导装入程序的首要任务就是启用内存子系统。内存完成初始化之后,就可以作为一种资源进行使用。实际上,很多引导装入程序完成内存初始化后的第一个动作,就是将自身复制到DRAM中,以便获得更快的执行速度。

     

    7.2.2 闪存与RAM

    引导装入程序固有的另一个复杂性,是它需要保存在非易失存储器中,但通常又要载入到RAM中才能运行。此外,引导装入程序的复杂性还随它使用的资源增加而增加。在Linux这样完整的操作系统上编译一个程序并从非易失存储器中调用是非常容易的。运行时库、操作系统和编译器一起协作创建了必需的基础设施,负责将程序从非易失存储器中载入到内存,并将控制权移交给这个程序。前面提到的"Hello World"程序是一个非常好的例子。在程序被编译后,它可以载入到内存,并通过在命令行上输入程序的名称(hello)执行(当然,这里假定这个可执行文件所在目录已位于PATH变量中)。

    在系统加电引导装入程序获得控制权时,上述基础设施并不存在。实际上,引导装入程序必须自行创建一个可操作的上下文环境,并在必要时将自身复制到RAM中的适当位置。另外,还要说明的复杂性是从只读介质中执行程序的需求。
     

    7.2.3 映像的复杂性

    作为应用程序开发人员,在为某种熟悉的平台开发程序时,不必关心二进制可执行文件的布局。对于创建包含给定体系结构所需的适当组件的二进制映像,需要预先配置编译器和二进制工具集。链接器会把启动(prologue,序言)和终止(epilogue,尾声)代码加入映像。这些对象构成了应用程序的执行上下文,通常程序开始于main()函数。

    但对于普通的引导装入程序而言,却绝对不是这种情况。当引导装入程序获得控制权时,并没有上下文或执行环境。在一个普通的系统中,在引导装入程序初始化处理器和相关硬件之前,很可能并不存在任何DRAM。(请细细品味这句话的含义。)在普通的C函数中,所有局部变量都存储在栈中,因此代码清单7-1中的这个简单函数无法使用。

    代码清单7-1 简单的C语言函数

    1. int setup_memory_controller(board_info_t *p)  
    2.     {  
    3.     unsigned int *dram_controller_register = p->dc_reg;  
    4. ... 

    系统加电后,引导装入程序获得了控制权,此时并没有栈和栈指针。因此,与代码清单7-1相似的C函数很有可能会搞垮处理器,因为编译器会在栈上生成一段代码,用于创建和初始化dram_controller_register指针,而这个指针此时并不存在。引导装入程序必须在调用任何C函数之前创建这个执行环境。

    当编译和链接引导装入程序后,开发人员必须非常明白映像是怎样创建并链接的,特别是引导装入程序是如何将自身从闪存搬到RAM的。必须向编译器和链接器传入若干参数,这些参数定义了最终可执行映像特征和布局。有两个主要的特征导致了最终的二进制可执行映像的复杂性。

    带来映像复杂性的第一个特征,是需要按照与处理器启动顺序兼容的格式组织启动代码。可执行代码的第一个字节必须根据具体的处理器和硬件体系结构预先定义。例如,AMCC PowerPC 405GP处理器从硬编码地址0xFFFF_FFFC处查找第一条机器指令。其他处理器也会使用类似的方法,不过细节会有些差异。一些处理器配置为系统加电后,根据硬件配置信号的不同,会从几个预先定义好的地址处查找指令代码。

    开发人员如何指定二进制映像的布局呢?链接器可以获得一个链接器描述文件,也称作链接器命令脚本。这个特殊文件可以被认为是构建一个二进制可执行映像的诀窍。代码清单7-2取自一个流行的引导装入程序中链接器描述文件的片段,我们在此简要地讨论。

    代码清单7-2 链接器命令脚本

    1. SECTIONS  
    2. {  
    3.   .resetvec 0xFFFFFFFC :  
    4.   {  
    5.     *(.resetvec)  
    6.   } = 0xffff  
    7. ... 

    完整地描述链接器命令脚本的语法规则已超出了本书的范围,对此感兴趣的读者可以参考本章末尾给出的GNU LD手册。通过代码清单7-2,可以看到二进制ELF映像文件输出段的定义的开始部分。它指定链接器将调用的.resetvec代码段放置在输出映像的一个固定地址,即0xFFFF_FFFC。此外,它还指定了本段剩余的内容应该全用1(0xFFFF)进行填充。这是因为可擦除的闪存阵列包含的都是1。这种技术不仅减少对闪存的磨损,对扇区编程的速度也有提高。

    代码清单7-3取自U-Boot最新发行版中定义了.resetvec代码段的文件,它包含在称作.../cpu/ppc4xx/resetvec.S的汇编语言文件中。注意,这个代码段在32位地址的机器中不能超出4字节长度,因为在这个段中只定义了一个指令,而不论定义的配置选项是什么。

    代码清单7-3 定义.resetvec的源代码

    1. /* Copyright MontaVista Software Incorporated, 2000 */  
    2. #include  
    3.       .section .resetvec, "ax"  
    4. #if defined(CONFIG_440)  
    5.       b _start_440  
    6. #else  
    7. #if defined(CONFIG_BOOT_PCI) && defined(CONFIG_MIP405)  
    8.       b _start_pci  
    9. #else  
    10.       b _start  
    11. #endif  
    12. #endif 
    即使没有汇编语言编程经验,这个汇编文件对你来说也非常容易理解。根据特定的配置(通过CONFIG_*宏指定的),在代码主体的起始位置生成一个无条件分支指令(PowerPC汇编语法中是b指令)。这个分支位置是一个4字节的PowerPC指令,与代码清单7-2中列出的链接器命令脚本片段一样,这个简单的分支指令放置在输出映像的闪存绝对地址0xFFFF_FFFC处。正如前面提到的,PPC 405GP处理器从这个硬件解码地址取到它的第一条指令。这就是最初的代码流的定义方式,也是开发人员针对特殊体系结构和处理器的组合所做的必要处理。  

    7.2.4 执行上下文

    引导装入程序映像复杂的另一个主要原因是它缺少执行环境(上下文)。当代码清单7-3中的指令流开始执行后(回忆一下加电后的第一条机器指令是什么),几乎没有运行程序可用的资源。硬件设计的默认值必须确保从闪存中取到的指令能正常工作,而且系统时钟是有默认值的,不能随便指定 。每一个处理器的复位状态通常都由生产厂商定义,但是开发板的复位状态由硬件设计人员定义。

    的确,大多数处理器在启动时没有可用的DRAM用来临时存储变量,更糟的是,使用C程序的调用需要用到栈,这是一种惯例。如果你非常固执地写一个没有DRAM、没有栈的"Hello World"程序,那么它将与传统的"Hello World"程序极为不同。

    这种局限性给设计硬件初始化的原始代码带来了极大的挑战。因此,引导装入程序启动后首要的执行任务之一,就是配置足够数量的RAM以便支持硬件。一些专门为嵌入式系统设计的处理器使用了片上(on-chip)静态RAM。我们讨论的PPC 405GP处理器用的就是这种方法。当RAM可用时,可以使用这部分RAM分配栈空间,同时可以建立适当的上下文运行高级语言,例如C语言。这也使剩下的处理器和平台初始化工作可以使用其他编程语言完成,而不一定是汇编语言。