欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Bootloader的结构和启动过程

程序员文章站 2023-08-26 18:35:12
CPU上电后,会在某个地址开始执行,比如MIPS结构的CPU会从0xBFC00000取第一条指令,而ARM结构的CPU则从0x00000000开始,嵌入式开发板中,需要把存储器件ROM或Flash等映射到这个地址。而Bootloader就存在这个地址的开始处,这样一上电后就会从这个地址处执行。Boo ......

cpu上电后,会在某个地址开始执行,比如mips结构的cpu会从0xbfc00000取第一条指令,而arm结构的cpu则从0x00000000开始,嵌入式开发板中,需要把存储器件rom或flash等映射到这个地址。而bootloader就存在这个地址的开始处,这样一上电后就会从这个地址处执行。bootloader执行后从板子上的某个固态存储设备上将操作系统os加载到ram中运行。(一些功能强大的bootloader,比如u-boot在正常启动加载后可以延时若干秒(也可以自己设置),等待终端用户按下任意键后便可进入到下载模式;如果在指定的时间内没有按键,u-boot则会启动linux内核,内核的启动参数可以是默认的或是由u-boot传递给它的)。

**注意:有的cpu在运行bootloader之前 先运行一段固件(firmware)中固化的boot代码,比如x86结构的cpu就是先运行bios中的 固件然后才开始运行硬盘第一个分区中的bootloader。在大多数的嵌入式系统中并没有固件,bootloader是上电后运行的第一个代码。**

下面便更细致得说明bootloader的启动过程:
从固态存储器上启动的bootloader大多数是分二个阶段来启动的。
**第一个阶段**使用汇编代码来实现,它主要完成一些依赖于cpu体系结构的初始化,比如关看门狗、关中断、初始化ram、将第二阶段调用的c语言代码复制到ram(非必须,例如对于nor flash等设备可以直接在上面执行,只不过比在ram上执行效率低),设置cpu的速度和时钟频率(非必需,也可以放在第二阶段),设置好栈,跳转到第二阶段的c语言入口处等;
**第二个阶段**通常用c语言来实现,它主要用来:初始化本阶段要用到的硬件设备、检测系统内存映射(就是确定板上使用了多少内存,他们的地址空间是什么)、将内核映像和根文件系统映像从flash上复制到内存ram并且在内存中的某个固定位置为内核设置启动参数boot parameters(flash上的内核映像有可能是经过压缩的,那么读到ram后还要进行解压。对于有自解压功能的内核不需要bootloader来解压。另外将根文件系统映像复制到ram是非必须的,这取决于是什么类型的根文件系统,以及内核访问它的方法)、调用内核(内核启动后会挂载根文件系统,所以典型的嵌入式linux系统的分区结构为botloader + boot parameters + kernel + root filesystem)。

**从上面的分析可知将内核放到适当的位置后,直接跳到其入口点便可以启动内核,在内核之前需要满足那些条件呢,下面我们来具体分析:**

**cpu寄存器r0、r1和r2值的设置**
由于u-boot在设置完启动参数标记列表后最终是调用thekernel函数来跳转执行linux内核的,uboot调用这个函数(其实就是linux内核)时会直接传递给linux内核3个参数,而这3个参数就是通过寄存器来实现传参的。其中第1个参数固定为0,就放在r0寄存器中,第二个参数为机器类型id也就是我们常说的机器码,就放在r1寄存器中,第3个参数就是启动参数标记列表在ram中的首地址,就放在r2寄存器中。

**cpu工作模式的设置**
必须禁止中断(irqs和fiqs),并且要将cpu设置为svc模式。
这是因为uboot只是完成硬件初始化,环境参数设置,代码搬运等工作,用不到中断。屏蔽中断是为了避免因为意外中断使得boot失败,毕竟很多外设还没有初始化,对应中断代码也都没有准备好。
那么为什么要将cpu设置为svc模式呢?我们先简单的来分析一下cpu的7种模式:
中止abt和未定义und模式:
首先可以排除的是,中止abt和未定义und模式,那都是不太正常的模式,此处程序是正常运行的,所以不应该设置cpu为其中任何一种模式,所以可以排除。

快中断fiq和中断irq模式:
其次,对于快中断fiq和中断irq来说,此处uboot初始化的时候,也还没啥中断要处理和能够处理,而且即使是注册了终端服务程序后,能够处理中断,那么这两种模式,也是自动切换过去的,所以,此处也不应该设置为其中任何一种模式。

用户usr模式:
虽然从理论上来说,可以设置cpu为用户usr模式,但是由于此模式无法直接访问很多的硬件资源,而uboot初始化,就必须要去访问这类资源,所以此处可以排除,不能设置为用户usr模式。

系统sys模式 vs 管理svc模式:
首先,sys模式和usr模式相比,所用的寄存器组,都是一样的,但是增加了一些访问一些在usr模式下不能访问的资源。

而svc模式本身就属于特权模式,本身就可以访问那些受控资源,而且,比sys模式还多了些自己模式下的影子寄存器,所以,相对sys模式来说,可以访问资源的能力相同,但是拥有更多的硬件资源。

所以,从理论上来说,虽然可以设置为sys和svc模式的任一种,但是从uboot方面考虑,其要做的事情是初始化系统相关硬件资源,需要获取尽量多的权限,以方便操作硬件,初始化硬件。

从uboot的目的是初始化硬件的角度来说,设置为svc模式,更有利于其工作。

因此,此处将cpu设置为svc模式。

另外uboot作为一个bootloader来说,最终目的是为了启动linux的kernel,在做好准备工作(即初始化硬件,准备好kernel和rootfs等)跳转到kernel之前,本身就要满足一些条件,其中一个条件,就是要求cpu处于svc模式的。

所以,uboot在最初的初始化阶段,就将cpu设置为svc模式,也是最合适的。

**cache和mmu的设置**
mmu和数据cache必须必须关闭,指令cache可以打开也可以关闭。
由于mmu在上电之初是没有任何作用的,也就是说u-boo第一阶段的汇编代码以及第二阶段的源代码初始化相关外设时访问的都是都是实际地址,mmu起不到任何作用,为了启动之初不影响对程序的启动常关闭mmu。
cache是位于ram和cpu内部寄存器之间的一个存储设施,用来加速二者之间的数据传输速度,即用来加快cpu从内存中取出指令的速度。但是在上电后cpu的初始化要比内存ram快一拍,当cpu初始化完成后需要读取来自内存的数据,若内存还没有准备好那势必会造成异常,系统就挂掉了,因此需要关闭数据cache,而指令cache关与不关影响不大。