由boot loader和固化在固件(firmware)中的boot代码(可选)共同组成一个嵌入式系统的引导加载程序。它的作用和功能就像固化到计算机内主板上的一个rom芯片程序bios(basic input output system)。但是它一般不配置像bios那样的固件程序,这是因为要考虑经济方面的原因,因此必须自己完成这方面的工作。boot loader可以初始化硬件设备,建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。它的实现严重地依赖于硬件,特别是在嵌入式系统中,即使基于同一个cpu的boot loader,对于不同的板子,也有很大的不同。
1 boot loader分析
系统加电,然后复位后,基本上所有的cpu都是从复位地址上取得指令的。以微处理器为核心的嵌入式系统中,通常都有某种类型的固态存储设备(flash,e2prom等),这个固态存储设备被映射到一个预先设置好的地址上。在系统加电复位后,一开始处理器就会去执行存放在复位地址处的程序,而且通过开发环境可以将boot loader定位在复位地址一开始的存储空间上,因此boot loader是系统加电后,在操作系统内核或者一些应用程序被运行之前,首先会运行的程序。对于嵌人式系统来说,比较复杂的或者为了方便后期开发大的应用程序,有的使用操作系统,也有很多的情况下,因功能简单,或仅包括应用程序的系统不使用操作系统,但是不论有无操作系统在启动时都必须执行boot loader,为的是准备好软硬件运行环境。
以微处理器为核心的嵌入式系统中,一般都有某种类型的固态存储设备(flash,e2prom等),这个固态存储设备被映射到一个预先设置好的地址上。在系统加电复位后,一开始处理器就会去执行存放在复位地址处的程序。而且通过开发环境可以将boot loader定位在复位地址一开始的存储空间上,因此boot loader是系统加电后,在操作系统内核或者一些应用程序被运行之前,首先会运行的程序。对于linux系统,它的主要任务有以下7个方面。
(1)初始化处理器及外设的硬件资源配置。一般嵌入式系统的处理器在上电复位后,外部的i/o引脚都处于输入状态,处理器的片内和片外设备资源都需要配置。
(2)建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的环境,这样就能为最终启动操作系统的内核提供最好条件。
(3)把操作装载到映射的内存中,这也是所有任务当中最重要的一个,只有完成这个任务,操作系统才能被装载到内存当中去,boot loader一般会提供串口和网络装载两种方式。
(4)为了把操作系统的映像保存在flash中,以便以后启动,可以直接装载flash的数据,而不用重新下载程序,但需要对flash进行编程。
(5)运行操作系统。设置相关的寄存器和资源,跳转到操作系统的所在空间,进行相关的引导,这就是boot loader。
(6)在linux系统启动时,传递系统的启动参数,可以给内核传递命令行等参数,通过命令行可以选择控制系统的启动模式。
(7)命令行的解析和输入/输出控制。为了开发的方便,多数的boot loader都采用串口作为终端的控制方式。
boot loader的启动过程可分为两个重要阶段。第一阶段:由于boot loader的实现依赖于cpu的体系结构,所以设备代码的初始化等功能都在该阶段完成。而且,为了达到缩短代码的目的,通常用汇编语言来编写。在这一阶段的执行过程中,又可分为几个方面。
①硬件设备的初始化。在该阶段的执行过程中,首先需要对硬件设备进行初始化,其目的主要是为第二阶段的执行以及随后kernel的调用准备基本的硬件环境。
②为加载boot loader的第二阶段准备ram空间。为了获得更快的执行速度,通常把第二阶段加载到ram空间中来执行。因此,必须为加载boot loader准备好一段可用的ram空间范围。
③设置堆栈指针。设置堆栈是为了执行c语言代码作好准备。
④跳转到第二阶段的c入口点。当程序执行到这个位置时,可以通过修改pc寄存器的值,使其跳转到第二阶段。
第二阶段阶段的启动流程分析:为了便于实现复杂的功能和获得更好的代码可读性和可移植性,通常第二阶段的代码用c语言来实现。但是,与普通c语言的不同之处是,这里使用了“弹簧床”的概念,即先用汇编语言写一段小程序,并将这段小程序作为第二阶段可执行映像的执行入口点,然后在汇编程序中用cpu跳转指令跳入main()函数中去执行,当main()函数返回时,cpu执行路径再次返回到汇编程序中第二阶段,包括初始化本阶段要使用的硬件设备,检测系统内存映射,会将kernel映像和根文件系统映像从flash中读到ram空间中,为内核设置启动参数调用内核。
2 boot loader的设计
2.1 中断向量表(二级)的设计与建立
如果有中断或者异常发生时,处理器便会强制性地把pc指针指向向量表中它所对应的中断类型地址值。为了提高中断响应速度,flash的0x0地址存放能跳转到0x33ffff00地址处中断向量的跳转指令,也就是会在在ram中建立一个二级中断向量表,起始地址为0x33ffff00。除了复位外,所有的异常入口地址都由flash跳转得到,代码如下:
2.2 第二阶段拷贝到ram
把第二阶段stage2拷贝到ram地址的最顶大小为1 mb的开始空间,ram的起始地址为0x30000000。代码如下所示:
2.3 堆栈指针的设置
用户使用哪些中断决定了系统堆栈的初始化,以及系统需要处理的哪些错误类型。一般情况下,堆栈设置是必须,而且是由管理者自己设置的。如果需要使用irq中断,那么irq堆栈的设置也是必须的,下面是irq堆栈的设置:
3 stage2的设计
3.1 可执行映像stage2的入口
由于glibc库支持的函数不能用于编译和链接boot loader这样用c语言编写的程序,因此把main()函数的起始地址作为第二阶段的入口点是最直接的想法。可以用汇编编写一段trampoline小程序,用cpu跳转指令跳到main()函数去执行,当函数返回时会再次回到trampoline程序,代码如下:
程序顺利时就不会再回到开始的trampoline程序,不然就会回到最后的语句,系统就会重新启动。
3.2 内存影射
一般s3c2410上配置的sdrsam大小为64 mb,该sdram的物理地址范围是ox30000000~ox33ffffff(属于bank 6)。由section的大小可知,该物理空间可被分成64个物理段。因为arm体系结构中数据缓冲必须通过mmu开启,因此boot loader效率不是很高,但是mmu可以通过平板映射(虚拟地址和物理地址相同)方式被开启,这样使用内存空间dcache,从而使boot loader的运行速度得到有效的提高。映射关系代码如下:
3.3 装载内核映像和根文件系统映像
像arm这样的嵌入式cpu通常都是在统一的内存地址空间中寻址flash等固态存储设备的,因此从flash上读取数据与从ram单元中读取数据一样,用一个简单的循环就可以完成从flash设备上拷贝映像的工作:其中count为根文件系统映像的大小或内核映像的大小。
3.4 内核的启动参数的设置
内核启动可以从nand flash(nor flash)中启动运行linux,需要修改启动命令如下:
lcd启动参数一般都包括root,init和console。noinitrd不使用ramdisk。root根文件系统在mtd分区。init内核运行入口命令文件。co-nsol内核信息控制台,ttys0表示串行口0;tty0表示虚拟终端。
4 结语
通过对boot loader的分析可以看出,设计一个性能优良的boot loader可以提高系统的稳定性及实时性,它是嵌入式开发中不可或缺的一部分。只有设计出一个稳定的boot loader,才能进行下一步的系统开发工作,直至完成整个嵌入式系统的开发。设计boot loader是一项很复杂的工作,需要对硬件资源和所用的操作系统有很深的理解。在实际开发中可以根据需要简化设计,去除不必要的系统功能,这样可以大大提高程序执行的效率和稳定性。这里给出的boot loader已经顺利通过了调试,可以正常加载操作系统。
中兴通讯可编程网络处理器芯片,加快了先进技术向实业领域的转化
未来智能传感技术将赋能智慧医疗行业的发展
马来西亚举办工业4.0峰会 目标2020年跻身已开发国家
理想变压器的特点
Intersil推出用于车载信息娱乐和ADAS显示的14通道可编程Gamma缓冲器
ARM+Linux嵌入式系统的BootLoader设计
如何在LoRaWAN网关的内置NS创建应用与节点设备
挑选电热水袋,一定要拒绝电极式
华为助力中国广电打造全网体验一致的5G网络,将700M网络价值最大化
60V转0.8V-30v800mA电池充电电源芯片AH8690
MWC取消之后 欧洲或将成为全球5G手机竞争最激烈的市场
Hue成功之道:如何连接互联网和智能家居
VR行业2020年度大事件
荣耀WaterPlay防水平板电脑搭载麒麟659八核处理器10月24日开售
里约奥运会12大高科技大比拼 LED、VR、可穿戴设备等齐上阵!
固态价格持续腰斩,主控芯片厂商有何动作
ios15支持手机型号 ios15适用于哪些机型
缤特力Voyager 3200系列蓝牙耳机上市—独特设计与出色性能
荣耀V9评测:性能逆天、颜值逆天,这就是我要的快!
人机界面的主要作用有哪些_人机界面主要功能