嵌入式系统 Boot Loader 技术内幕(2)
在继续本节的讨论之前,首先我们做一个假定,那就是:假定内核映像与根文件系统映像都被加载到 RAM 中运行。之所以提出这样一个假设前提是因为,在嵌入式系统中内核映像与根文件系统映像也可以直接在 ROM 或 Flash 这样的固态存储设备中直接运行。但这种做法无疑是以运行速度的牺牲为代价的。
从操作系统的角度看,Boot Loader 的总目标就是正确地调用内核来执行。
另外,由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。
Boot Loader 的 stage1 通常包括以下步骤(以执行的先后顺序):
硬件设备初始化。
为加载 Boot Loader 的 stage2 准备 RAM 空间。
拷贝 Boot Loader 的 stage2 到 RAM 空间中。
设置好堆栈。
跳转到 stage2 的 C 入口点。
Boot Loader 的 stage2 通常包括以下步骤(以执行的先后顺序):
初始化本阶段要使用到的硬件设备。
检测系统内存映射(memory map)。
将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中。
为内核设置启动参数。
调用内核。
3.1.1 基本的硬件初始化
这是 Boot Loader 一开始就执行的操作,其目的是为 stage2 的执行以及随后的 kernel 的执行准备好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序):
1. 屏蔽所有的中断。为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。
2. 设置 CPU 的速度和时钟频率。
3. RAM 初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。
4. 初始化 LED。典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。
5. 关闭 CPU 内部指令/数据 cache。
3.1.2 为加载 stage2 准备 RAM 空间
为了获得更快的执行速度,通常把 stage2 加载到 RAM 空间中来执行,因此必须为加载 Boot Loader 的 stage2 准备好一段可用的 RAM 空间范围。
由于 stage2 通常是 C 语言执行代码,因此在考虑空间大小时,除了 stage2 可执行映象的大小外,还必须把堆栈空间也考虑进来。此外,空间大小最好是 memory page 大小(通常是 4KB)的倍数。一般而言,1M 的 RAM 空间已经足够了。具体的地址范围可以任意安排,比如 blob 就将它的 stage2 可执行映像安排到从系统 RAM 起始地址 0xc0200000 开始的 1M 空间内执行。但是,将 stage2 安排到整个 RAM 空间的最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方法。
为了后面的叙述方便,这里把所安排的 RAM 空间范围的大小记为:stage2_size(字节),把起始地址和终止地址分别记为:stage2_start 和 stage2_end(这两个地址均以 4 字节边界对齐)。因此:
stage2_end=stage2_start+stage2_size
另外,还必须确保所安排的地址范围的的确确是可读写的 RAM 空间,因此,必须对你所安排的地址范围进行测试。具体的测试方法可以采用类似于 blob 的方法,也即:以 memory page 为被测试单位,测试每个 memory page 开始的两个字是否是可读写的。为了后面叙述的方便,我们记这个检测算法为:test_mempage,其具体步骤如下:
1. 先保存 memory page 一开始两个字的内容。
2. 向这两个字中写入任意的数字。比如:向第一个字写入 0x55,第 2 个字写入 0xaa。
3. 然后,立即将这两个字的内容读回。显然,我们读到的内容应该分别是 0x55 和 0xaa。如果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。
4. 再向这两个字中写入任意的数字。比如:向第一个字写入 0xaa,第 2 个字中写入 0x55。
5. 然后,立即将这两个字的内容立即读回。显然,我们读到的内容应该分别是 0xaa 和 0x55。如果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。
6. 恢复这两个字的原始内容。测试完毕。
为了得到一段干净的 RAM 空间范围,我们也可以将所安排的 RAM 空间范围进行清零操作。
3.1.3 拷贝 stage2 到 RAM 中
拷贝时要确定两点:(1) stage2 的可执行映象在
- VXWORKS内核分析(11-11)
- linux内核编译步骤(11-11)
- Linux内核解读入门(11-09)
- Linux内核编译过程详解(kernel2.6.7) (04-16)
- 嵌入式Linux内核移植相关代码分析(04-21)
- 什么是module 以及如何写一个module(转)(04-23)