
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启动流程,为项目的后续的开发奠定良好的基础。