终于搞到这里了,相对于之前的裸机程序,代码量猛增,但是也算是真正开始学习嵌入式Linux的起步阶段了。
Bootloader就和我们电脑上面的BIOS的作用差不多,它主要有两个作用。
启动引导作用:上电后,Bootloader从板子上的某个固态存储设备上将操作系统加载到RAM中运行,整个过程并没有用户的介入。当产品发布的时候,Bootloader工作在这种模式下。
下载模式:这种模式主要是针对开发人员的,开发人员可以使用各种命令,通过串口连接或者网络连接等通信手段从主机上下载文件,比如内核映像、文件系统映像,将他们直接放在内存运行或者是烧入Flash类固态存储设备中。
用U盘或者光盘安装过系统的人应该知道,开机之后BIOS运行,如果用户输入某个按键,比如F8或者F12等等,就可以进入BIOS设置界面,如果没有输入,则会开始引导操作系统。嵌入式系统中的Bootloader也是一样的,开机之后它首先运行,等待输入某个按键比如空格,如果有相应的输入就会进入下载模式供开发人员使用,否则直接引导系统启动。
Bootloader的起动可以分为单阶段、多阶段两种。通过多阶段的Bootloader能提供更为复杂的功能以及更好的可移植性。从固态存储设备上启动的Bootloader大多都是两阶段启动的。第一阶段使用汇编来实现,他完成一些依赖于CPU体系结构的初始化,并调用第二阶段的代码;第二阶段则通常使用C语言来实现,这样可以实现更加复杂的功能,而且代码会有更好的可读性和可移植性。
一般来说,第一阶段大体上要完成的工作:
硬件设备的初始化;(一般包括关闭Watchdog,关中断,设置CPU的速度和时钟频率,RAM初始化等,当然有些初始化也可以放到第二阶段)
为加载Bootloader的第二阶段代码准备RAM空间;
复制Bootloader的第二阶段的代码到RAM空间;(这一步也不是必须的,NORFlash中可以直接运行代码,只不过相比在RAM中执行效率大为降低)
设置好栈;(调用C函数前要设置好堆栈)
跳转到第二阶段代码的C入口点。
Bootloader的第二阶段一般要完成的功能:
初始化本阶段要使用到的硬件设备;
检测系统内存映射;
将内核映像和跟文件系统映像从Flash上读到RAM空间中;
为内核设置启动参数;
调用内核。
当然了,为了方便开发,至少得初始化一个串口以便程序员与Bootloader进行交互。有的Bootloader还具有网络功能。
Bootloader引导内核,它和内核也需要有参数的交互,不过因为二者不能够同时运行,所以它们的交互是单向的,只能够由Bootloader将各类参数传递给内核,而且是通过Bootloader将参数以一种特定的格式放到特定的地方,然后再启动内核,内核再去那个特定的地方去读取参数。这种参数是以标记列表的形式存放的,代码上是通过一个结构体实现的,结构体包括了一个tag_header和一个联合体构成的,个人觉得这个联合体使用的很巧妙。因为不同的标记需要不同的存储格式,但是如果每种参数都去定义一个结构体又太麻烦,使用结构体就很好的解决了这个问题。值得去学习这种编程思想。 |