BootLoader——嵌入式系统的引导加载程序

  • 引导加载程序(BootLoader)是系统上电后运行的第一段代码,在嵌入式系统中,通常没有像PC体系结构的BIOS固态启动程序,因此嵌入式系统的加载启动任务是由BootLoader来完成的。例如,基于ARM core的嵌入式系统中,系统在启动时通常从地址0x00000000处开始执行,在这个地址存放的通常是系统的BootLoader程序。

一、BootLoader的简介

  • BootLoader是操作系统内核系统运行之前,需要首先运行的一段小程序,通过BootLoader程序,可以初始化硬件设备、建立内存空间的映射图,为调用操作系统内核做好环境准备。
  • 由于嵌入式系统硬件的多样性,且BootLoader与系统硬件具有密不可分的关系,因此,设计一个通用的Bootloader几乎是不可能的。然而,我们却可以归纳出一个通用的BootLoader概念,用以指导用户设计特定的BootLoader。

1.1 BootLoader在固态存储设备中的位置

  • 系统上电或复位后,所有的CPU通常从CPU预先指定的起始地址上读取指令,嵌入式系统通常将某种类型的固态存储设备(例如ROM、EEPROM、FLASH等)安排在这个起始位置。下图就是一个同时安装了BootLoader、内核启动参数、内核映射和跟文件系统映像的固态存储设备的典型空间分配结构。
    在这里插入图片描述

1.2 BootLoader的启动过程

  • BootLoader的启动过程分为单阶段(Single Stage)和多阶段(Multi-Stage)两种。通常多阶段的BootLoader能够提供更为复杂的功能与更好的移植性。从固态存储设备上启动BootLoader通常经过两个阶段启动(分为stage1和stage2)。由于启动过程比较复杂,因此这两个步骤的启动过程放到下面进一步展开讨论。

1.3 BootLoader的操作模式

  • BootLoader通常包括两种操作模式:启动加载(Boot Loading)模式和下载模式(Down Loading)模式。
  • 1、启动加载模式
  • 启动加载模式也成为自主(Autonomous)模式,即BootLoader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行。这种模式是BootLoader的正常工作模式,因此嵌入式产品发布时,BootLoader应该工作该模式下。
  • 2、下载模式
  • 在该模式下,目标机上的BootLoader将通过串口连接或网络连接从主机(Host)上下载文件(通常包括内核映像与跟文件系统映像等)。从主机下载的文件通常首先被BootLoader保存到目标机(Target)的RAM中,最终被BootLoader载入到目标机的FLASH类固态存储设备中。在第一次安装内核、根文件系统时以及更新系统时两种情况下,BootLoader采用下载模式。

二、BootLoader的启动过程

2.1 BootLoader启动简介

  • 嵌入式系统的内核映像、根文件系统映像可以运行于RAM、ROM以及FLASH设备上,本文以加载到RAM上为例讨论BootLoader的启动过程。
  • 由于BootLoader的实现依赖于CPU的体系结构,并且通常分为两个阶段。其中,stage1通常对设备的代码进行初始化,并且通常是采用汇编语言实现的;而stage2通常采用C语言实现,以实现复杂功能,并可以使得代码具有很好的可读性与可移植性。BootLoader的stage1通常包括如下步骤:
硬件初始化
为加载BootLoader的stage2准备RAM空间
拷贝BootLoader的stage2到RAM空间中
设置好堆栈
跳到stage2的C语言入口点
  • stage2通常包括以下步骤:
初始化本阶段需要使用的硬件设备
检测系统内存映射
将内核映像与根间映像从FLASH载入到RAM中
为内核设置启动参数
调用内核

2.2 BootLoader启动的stage1

1、基本硬件初始化

  • BootLoader一开始就执行基本硬件初始化,其目的是为stage2执行以及kernel的执行的执行准备好基本的硬件环境。基本硬件初始化包括以下步骤:
  • (1)屏蔽所有中断:BootLoader的执行过程不必响应任何中断,为中断提供服务通常是操作系统驱动程序的责任。中断的屏蔽可以通过写CPU的中断屏蔽寄存器活状态寄存器来完成。
  • (2)设置CPU的速度和时钟频率。
  • (3)RAM初始化:正确地设置系统内存控制器的功能寄存器。
  • (4)初始化LED系统指示灯:通过GPIO来驱动LED系统指示灯,以表明系统的状态是正常的。
  • (5)关闭CPU内部指令/数据cache。
  • 2、为加载stage2准备RAM空间
  • 为了获得更快的执行速度,通常把stage2加载到RAM空间中执行,因此必须为加载BootLoader的stage2准备一段可用的RAM空间范围。为了便于表述,这里将RAM空间范围大小标记为:是stage2_size(字节),并把起始地址与终止地址分别标记为:stage2_start与stage2_end(这两个地址均以4字节边界对齐):
stage2_end=stage2_start+stage2_end
  • 另外,必须确保所安排的地址范围确定是可读写的RAM空间,并且需要对所安排的地址范围进行测试。具体测试方法为:以memory page(4K的倍数)作为测试单位,测试每个memory page开始的两个字是否可读写,为了便于后续的描述,将这种测试算法叫做test_mempage,其具体步骤如下:
  • (1)首先保存memory page开始的两个字的内容。
  • (2)向这两个字中写入任意的数字(比如0x12,0x13)。
  • (3)立刻将这两个字的内容读回,如果读回的内容为0x12,0x13,则说明这个memory page所占据的地址范围是一段有效的RAM空间,否则不是。
  • (4)恢复这两个字的原始内容,测试完毕。
  • 3、拷贝stage2到RAM中
  • 拷贝时需要确定两点:第一,stage2的可执行映像在固态存储设备的起始地址与终止地址;第二,RAM空间的起始地址。
  • 4、设置堆栈指针sp
  • 堆栈指针的设置是为执行C语言代码做好准备。
  • 执行完以上四个步骤后,系统的物理内存布局如下图所示:
    在这里插入图片描述
  • 5、跳转到stage2的C语言入口点
  • 至此,就可以跳转到BootLoader的stage2去执行了,在ARM系统中,通过修改PC寄存器指向合适的地址来实现跳转。

2.3 BootLoader启动的stage2

  • 在该阶段,与普通C语言应用程序不同,在编译与连接BootLoader程序时,不能使用glibc库的任何支持函数。BootLoader启动过程中的stage2打流程如下所示:
  • 1、初始化本阶段需要使用的硬件设备
  • 首先许需要点亮系统指示LED等,这表明系统已经进入main()函数,并进而初始化至少一个串口,以便和终端用户进行I/O输出信息;还需要初始化计时器等硬件设备。初始化阶段完成后,系统就可以输出一些打印信息、程序名称字符串、版本号等。
  • 2、检测系统的内存映射(memory map)
  • 内存映射就是指在整个4GB物理地址空间中那些地址范围被分配以用作寻址系统的RAM单元。在具体的嵌入式系统中,CPU通常会预留处足够大的地址空间给RAM,而不一定预留全部的RAM地址空间。因此,BootLoader的stage2需要检测整个系统内存映射情况,划分清楚CPU预留的全部RAM地址空间中那些被真正映射到RAM地址单元,那些处于“unused”状态。
  • 3、加载内核映像与根文件系统映像
  • 内存映像与根文件系统映像的加载包括规划内存占用的布局与从FLASH上拷贝两个步骤:
  • (1)规划内存占用的布局
  • 结合系统物理内存分布,规划内存布局的主要内容包括两个方面:第一,内核映像所占用的内存范围;第二,根文件系统所占用的内存范围。在规划内存占用的布局时,主要考虑基地址与映像大小两个方面。
  • 对于内核映像,一般将其拷贝到从MEM_START+0x8000这个基地址开始的1MB的内存范围内(嵌入式Linux的内核一般都小于1MB)。这样就空出了从MEM_START到MEM_START+0x8000这段32KB大小的内存空间,在Linux内核中,这32KB的内存空间通常防止一些全局数据结构,如启动参数与内核页表等。
  • 对于根文件系统映像,通常将其拷贝到MEM_START+0x00100000开始的地方,如果采用的是Ramdisk作为根文件系统映像,解压后的大小通常是1MB。
  • (2)从FLASH上拷贝
  • 对于ARM嵌入式系统,CPU通常在统一的内存地址空间中寻址FLASH固态存储设备,并从FLASH上读取数据。通常,用一个简单的循环就可以完成从FLASH设备上拷贝映像的工作:
while(count){
	*dest++ = *src++; /* they are all aligned with word boundary */
	count -= 4; /*byte number 8/
};

4、设置内核的启动参数

  • 在将内核映像与根文件系统映像拷贝到RAM空间后,就可以准备启动Linux内核了。但是在启动内核之前,通常需要根据需求,设置Linux内核的启动参数。
  • 在Linux 2.4x版本以后,通常以标记列表(tagged list)形式来传递内核启动参数。启动参数标记列表以标记ATAG_CORE开始,以标记ATAG_NONE结束,每个标识包括内核启动参数的tag_header结构与参数结构,数据结构tag与tag_header定义在Linux内核源码的“include/asm/setup.h”头文件中。其中,BootLoader设置的常用启动参数包括ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等。
    5、调用内核
  • BootLoader调用Linux内核的方法是直接跳转到内核的第一条处,即直接跳转到MEM_START+0x8000地址处。在跳转时,应该满足以下条件:
  • (1)CPU寄存器的设置:R0=0;R1=机器类型ID;R2=启动参数标记列表在RAM中的起始基地址。
  • (2)CPU模式:必须禁止中断IRQs与FIQs;CPU必须处于SVC模式。
  • (3)cache与MMU的设置:MMU必须关闭;指令cache可以打开也可以关闭;数据cache必须关闭。
  • 2
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值