BootLoader的启动流程

标签(空格分隔): Linux


BootLoader的启动流程

BootLoader的第一阶段通常包含以下步骤(以执行的先后顺序):

  • 硬件设备初始化
  • 为加载 Boot Loader的stage2准备RAM空间。
  • 拷贝Boot Loader的stage2到RAM空间中。
  • 设置好堆栈。
  • 跳转到stage2 的 C 入口点。

BootLoader的第二阶段通常包括以下步骤(以执行的先后顺序):

  • 初始化本地阶段要使用到的硬件设备
  • 检测系统内存映射(memory map)
  • 将kernel映像和根文件系统映像从flash上读到RAM空间中。
  • 为内核设置启动参数
  • 调用内核

1. Boot Loader的stage1

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。

2. 为加载 stage2 准备 RAM 空间

为了后面的叙述方便,这里把所安排的RAM空间范围的大小记为:stage2_size(字节),把起始地址和终止地址分别记为: stage2_start 和 stage2_end (这两个地址均以4字节边界对齐)。
因此:
stage2_end = stage2_start + stage2_size
另外,还必须确保所安排的地址范围的的确确是可读写的RAM空间,因此,必须对你所安排的地址范围进行测试。

3. 拷贝stage2到RAM中

拷贝时要确定两点:

  1. stage2的可执行映像在固态存储设备的存放地址和终止地址。
  2. RAM空间的起始地址。

4. 设置堆栈指针sp

堆栈指针的设置是为了执行 C 语言代码作为准备。通常我们可以把sp的值设置为(stage2_end - 4), 也即在上面所安排的那个1MB的RAM空间的最顶端(堆栈向下生长)。
此外,在设置堆栈指针 sp 之前,也可以关闭 led 灯,以提示用户我们准备跳转到stage2。经过上述这些执行步骤后,系统的物理内存布局应该如下图。

BootLoader的启动流程_第1张图片
image_1bqvi1l5bpur1hqf187i12mg1u0k9.png-159.6kB

5. 跳转到 stage2 的 C 入口点

在上述一切都就绪后,就可以跳转到 Boot Loader 的 stage2 去执行了。比如,在 ARM 系统中,这可以通过修改 PC 寄存器为合适的地址来实现。

2. Boot Loader 的 stage2

1. 初始化本阶段要使用到的硬件设备

这通常包括:

  1. 初始化至少一个串口,以便和终端用户进行I/O输出信息
  2. 初始化计时器等。在初始化这些设备之前,也可以重新把LED灯点亮,以表明我们已经进入 main() 函数执行。设备初始化完成后,可以输出一些打印信息,程序名字字符串、版本号等。

2. 检测系统的内存映射 (memory map)

所谓内存映射就是指在整个4GB物理地址空间中有哪些地址范围被分配用来寻址系统的RAM单元。
比如,在SA-1100 CPU中,从0xC000,0000开始的512地址空间被用作系统的RAM地址空间,而在 Samsung S3C44B0X CPU中,从0x0c00,0000到0x1000,0000之间的64M地址空间被用作系统的RAM地址空间。
虽然 CPU 通常预留出一大段足够的地址给系统RAM,但是在搭建具体的嵌入式系统时不一定会实现 CPU 预留的全部 RAM 地址空间。

3. 加载内核映像和根文件系统映像

  1. 规划内存占用的布局
    这里包括两个方面:内核映像所占用的内存范围;根文件系统所占用的内存范围。在规划内存占用的布局时,主要考虑基地址和映像的大小两个方面。
  2. 从 Flash 上拷贝
    由于像 ARM 这样的嵌入式CPU通常都是在统一的内存地址空间中寻址Flash等固态存储设备的,因此从Flash上读取数据与从 RAM 单元中读取数据并没有什么不同。
    用一个简单的循环就可以完成从Flash设备上拷贝映像的工作:
while(count){
*dest++ = *src++; /*都是以字节方式对齐*/
count -= 4; /*字节数*/
}; 

4. 设置内核的启动参数

应该说,在将内核映像和根文件系统映像拷贝到 RAM 空间中后,就可以准备启动 Linux 内核了。
但是在调用内核之前,应该作进一步准备工作,即:设置Linux内核的启动参数。

5. 调用内核

Boot Loader 调用 Linux 内核的方法是直接跳转到内核的第一条指令处,也即直接跳转到 MEM_START+0x8000 地址处。

你可能感兴趣的:(BootLoader的启动流程)