1 引 言
对于PC的体系结构,其开机后的初始化处理器配置、硬件初始化、启动操作系统等操作是由BIOS(Basic Input/Output SysteIn)和位于硬盘MBR(Master Bool Record)中的系统导引程序(如LILO和GRUB等)一起完成的。但在嵌入式系统中,主要使用FLASH作为系统存储媒质,而很少使用磁盘,因此整个系统的加载启动任务就完全由引导程序(Bootloader )来完成。Bootloader 主要完成的功能有:装载(load)和启动(boot)。Bootloader 依赖于实际的硬件和应用环境,因此要为嵌入式系统建立一个通用、标准的Bootloader是非常困难的,通常都需要修改Boot-loader的源程序来完成对特定平台的移植。Blob是一款优秀的Bootloader程序,对其源程序进行修改之后可以很容易移植到XScale体系的。Intel PXA270平台上.本文基于Intel PXA270开发平台.分析Blob的启动流程。
2 Blob及实验平台简介
Blob(Boot Loadel Object)是一款功能强大的BOOtloader。他遵循GPL,源代码完全开放。Blob既可以用来简单的调试,也可以启动Linux kernel。Blob最初是JanDerk Bakker和Erik Mouw为一块名为LART(Linux Advanced Radio Terminal)的板子写的,该板使用的处理器就是Intel公司的Strong ARM SA-1100,所以很容易把Blob移植到ARM,XScale等体系结构的处理器上,如PXA270。
开发平台的主要配置为:
(1)基于Intel XScale架构内核的嵌入式处理器PXA270,内部集成IWMMX指令,加快处理器对多媒体数据的处理速度。
(2)系统稳定工作在520 MHz主频。
(3)64 MB SDRAM,16 MB Nor FLASH,64 MBNand FLASH。
(4)JTAG接口。
(5)2个标准RS 232标准串口。
(6)2个LED灯。
3启动流程分析
Blob的启动过程分为两个阶段:stagel和stage2。
3.1 Blob的第一阶段(stagel)
系统加电复位后,几乎所有的CPU都从复位地址上取指令,Blob第一阶段的代码就是从0x00000000开始的。Stagel主要完成了两个任务:硬件初始化和复制stage2到内存并运行。其流程如图1所示。
3.1.1基本的硬件初始化
硬件初始化是Blob一开始就执行的操作,其目的是为stage2的执行以及随后的kernel的执行准备好一些基本的硬件环境。他通常包括以下步骤:
(1)屏蔽所有的中断。为中断提供服务通常是OS设备驱动程序的责任,因此在Boot Loader的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写CPU的中断屏蔽寄存器或状态寄存器(比如ARM的CPSR寄存器)来完成。
(2)设置CPU的速度和时钟频率。
(3)存储器初始化。配置存储器等信息,包括I/O口,SDRAM控制器及各Bank存储模式等。
(4)初始化LED。典型地,通过GPIO来驱动LED,其目的是表明系统的状态OK还是Error。
3.1.2 为加载stage2准备RAM空间
为了获得更快的执行速度,通常把stage2加载到RAM空间中来执行,因此必须为加载Blob的stage2准备好一段可用的RAM空间范围。由于stage2通常是C语言执行代码,因此在考虑空间大小时,除了stage2可执行映象的大小外,还必须把堆栈空间也考虑进来。一般而言,1 MB的RAM空间已经足够了。具体的地址范围可以任意安排,如该开发平台的Blob就将stage2可执行映像安排到从系统RAM起始地址OxA0000000开始的1 MB空间内执行。另外,还必须确保所安排的地址范围是可读写的RAM空间,因此,必须对所安排的地址范围进行测试。具体的测试方法采用类似于Blob的方法,即:以memory page 为被测试单位,测试每个memory page开始的两个字是否是可读写的。这个检测算法的具体步骤如下:
(1)先保存memory page开始两个字的内容。
(2)向这两个字中写入任意的数字。比如:向第一个字写入0x55,第二个字写入Oxaa。
(3)然后,立即将这两个字的内容读回。显然读到的内容应该分别是0x55和Oxaa。如果不是,则说明这个memory page 所占据的地址范围不是一段有效的RAM空间。
(4)再向这两个字中写入任意的数字。比如:向第一个字写入0xaa,第二个字中写入Ox55。
(5)然后立即将这两个字的内容立即读回。显然读到的内容应该分别是Oxaa和Ox55。如果不是,则说明这个memory page所占据的地址范围不是一段有效的RAM空间。
(6)恢复这两个字的原始内容,测试完毕。
3.1.3 拷贝stage2到RAM中
拷贝时确定两点:
(1)stage2的可执行映象在固态存储设备的存放起始地址和终止地址;
(2)RAM空间的起始地址(即目标地址)。
3.1.4 跳转至stage2的C入口点
在上述一切就绪后,就可以跳转到Blob的stage2去执行了。比如,可以通过修改PC寄存器为合适的地址来实现。
stage1 执行过程其核心代码为:
stage1 完成之后的FLASH和内存中的Blob各部分分布如图2所示
3.2 Blob的第二阶段(stage2)
stage2的代码通常用C语言来实现,以便于实现更复杂的功能和取得更好的代码可读性和可移植性。但是与普通C语言应用程序不同的是,在编译和链接Boot Loader这样的程序时,我们不能使用glibc 库中的任何支持函数。如果直接把main函数的起始地址作为整个stage2执行映像的入口点将有两个缺点:
(1)无法通过main函数传递参数;
(2)无法处理main函数返回的情况。
一种更为巧妙的方法是利用trampoline(蹦床)的概念。也即,用汇编语言写一段trampoline 小程序,并将这段trampoline 小程序作为stage2可执行映象的执行人口点。然后可以在trampoline 汇编小程序中用CPU跳转指令跳人main函数中去执行;而当main函数返回时,CPU执行路径显然再次回到trampoline 程序。简而言之,这种方法的思想就是:用这段trampoline小程序作为main函数的外部包裹(external wrapper)。Trampoline程序完成的主要工作有:清除BSS段;设置栈指针;跳转到C代码(main函数)。其核心代码为:
当main函数返回后,我们又用一条跳转指令重新执行trampoline程序,这也就是trampoline(蹦床)一词的意思所在,通常在Blob中是不会让main函数退出的。
进入main函数以后,Blob的主要工作流程如图3所示。
值得注意的是,Blob代码中并没有对MMU的管理代码,也就是说,处理器在运行:Blob时,可直接访问物理地址。同时因为ARM体系结构中数据缓冲(DCache)必须通过MMU开启,所以Blob也不会使用DCcache,这也是Blob效率比较低的主要原因。可通过平板映射(flat,即虚拟地址和物理地址相同的方式)的方式开启MMU,从而使用内存空间的DCache,以提高Blob的运行速度。
Blob调用Linux内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到KERNEI_RAM_BASE地址处。
其核心代码为:
4结语
Blob启动过程就是初始化基体硬件设备,建立中断向量表及堆栈等,然后跳转到一般由高级语言(如C语言)编写的主函数的应用程序代码去执行,这样就可以利用高级语言来编写完成系统设计所要求的各种功能。掌握了PXA270 的Blob启动流程,为项目的后续的开发奠定良好的基础。