ARM-Linux嵌入式系统启动流程

时间:2022-07-29 18:44:02

        作为一个嵌入式新手,阅读资料之后做一下笔记还是很有必要的,下面从四个阶段来描述嵌入式系统的大致启动流程。如下图所示:

              

ARM-Linux嵌入式系统启动流程

图片引用自OMAPpedia的WIKI

http://omappedia.org/wiki/Bootloader_Project

其他相关参考资料:

TI官方WIKI的boot sequence

http://processors.wiki.ti.com/index.php?oldid=106907

 

       由此可见启动流程分为四个步骤,分别是ROM Code -> x-loader -> U-BOOT -> kernel ,下面从四个阶段来进行分析。

 

1、第一阶段启动ROM-Code

        每一个芯片都会有一个片内ROM,在出厂时厂家在其中固化好了一段代码,不可更改,这段代码执行第一阶段的启动任务。当芯片上电后,这段ROM-Code会读取启动引脚配置(boot configuration pins),然后根据引脚配置情况来决定以哪种方式下载接下来的外部启动代码(指的就是x-loader),下载的方式可以是NAND, UART,或者SD/MMC卡。

        比如BBB板子上,如果上电时没有按住boot按键,那么ROM-Code从板子上的eMMC中的第一启动分区(boot partition)下载x-loader(就是通常板子上电后,电脑上弹出的大概70MB的盘符中的MLO文件);如果上电时按住了boot按键,那么ROM-Code从板子上的SD卡的第一启动分区(boot partition)下载x-loader(编译u-boot后得到的MLO文件,制作SD卡时这个文件连同u-boot.img一起放在第一分区,事实上SD卡分区以及文件的情况和eMMC中是一样的)。

那么ROM-Code具体完成了哪些任务呢?

step1:初始化最小运行系统,包括时钟初始化、片内SRAM初始化以及一些芯片级外设的初始化。

step2:读取系统启动引脚配置后,按相应的启动设备列表的顺序,依次访问这些设备直到下载x-loader到SRAM成功。

step3:x-loader被下载到片内SRAM后,CPU控制权移交x-loader,ROM-Code任务结束。

Note:在step2中假若启动设备列表顺序是1、serial(UART3) , 2、SD card(MMC1), 3、NAND flash(eMMC),那么,

        首先ROM-Code将发送一个ID到串口(串口3),此时主机在一定时间内响应了这个ID,那么ROM-Code将把接下来收到的数据(MLO)写入片内SRAM,写完后CPU控制权移交MLO。如果一定时间内主机没有进行响应,

        接着ROM-Code将在MMC1控制器上寻找SD卡,如果找到一个SD卡,那么读取第一启动分区(boot partition),接着扫描该分区根目录,在根目录下寻找名为MLO的文件,并将这个文件下载到片内SRAM,再把控制权移交MLO。事实上MLO文件包含了x-loader二进制文件以及一些头部信息,这些信息指示了MLO文件应该下载在片内SRAM的哪个起始地址以及文件大小。如果ROM-Code没有找到SD卡,

        最后ROM-Code将NAND flash的第一个分区寻找MLO文件,如果发现第一个分区损坏或者空白分区,那么尝试第二个分区(最多尝试4个分区),找到MLO文件同样将其下载到片内SRAM,在移交控制权。(对于eMMC将会进行相同的操作)。

       

2、第二阶段启动x-loader

         x-loader事实上是u-boot的一个简化分支而来,其源码目录结构和u-boot比较相似。接上步骤,他运行于片内SRAM中承担着下载第三阶段启动代码u-boot.img到系统片外的主要内存SDRAM的任务。

         下面是一段x-loader主页的原文应用,介绍x-loader的作用和原因。

       X-loader is a small first stage boot loader derived from the u-boot base code to be loaded into the internal static ram by the OMAP ROM code. Because the internal static ram is very small (64k-32k), x-loader is stripped down to the essentials and is used to initialize memory and enough of the peripheral devices to access and load the second stage loader (UBoot) into main memory.

          由于片内SRAM空间太小,所以u-boot无法在其中运行,而NAND flash也不支持XIP(eXecute In Place),所以只能将u-boot搬至片外主存SDRAM中运行,所以最后应该是x-loader、u-boot被烧写在NAND flash中(即MLO、u-boot.img被烧写在SD卡或者eMMC的第一启动分区中),上电后ROM-Code将体积较小的x-loader搬至片内SRAM中运行,而运行在SRAM中的x-loader又初始化片外SDRAM,并将NAND Flash中的u-boot下载至SDRAM,控制权移交u-boot。 

相关参考资料:

X-loader主页以及代码库,支持OMAP3、BeagleBoard、BeagleBoard XM、zoom3、OMAP4、PandaBoard等。

https://gitorious.org/x-loader/pages/Home

 

从beagleboard.org找到的x-loader工程代码库,这个x-loader代码独立于u-boot,对源码进行整理后,编译的二进制文件支持所有的启动方式,这个代码库由Steve Sakoman托管在gitorious上。网页中同时告诉你如何根据板子、CPU类型配置来x-loader,推荐了TI的一些工具用来生成目标文件、烧写文件。

https://gitorious.org/x-load-omap3/pages/Home 

 

x-loader 以及 通过u-boot命令如何向nand中写入x-loader

http://blog.csdn.net/ghostyu/article/details/6941505

 

对X-loader进行了源代码方面的分析,特别细致

http://blog.csdn.net/gqb_driver/article/details/20069643

 

另外,一般从DENX下载的UBOOT源码编译后能同时得到MLO和u-boot.img。

 

那么X-loader具体完成了哪些任务呢?

ARM-Linux嵌入式系统启动流程

step1:配置复用引脚、初始化时钟频率、初始化片外SDRAM存储器

step2:CPU、板级外设初始化、串口控制台初始化

step3:读取启动设备,将u-boot读取到SDRAM中,控制权移交u-boot,并不再返回。

Note:在step3中,x-loader从何处读取u-boot.img?一般来说,MLO和u-boot.img都会放在同一个分区中或者以相同的方式获取,所以如果x-loader是从串口获得的,那么x-loader运行时,也会从串口等待主机发送u-boot.img文件。

 

 3、第三阶段启动任务u-boot

          和功能强大的u-boot相类似的BootLoader还有Blob,他们任务是通过各种方式将内核下载至SDRAM,并传递一些内核启动参数。所以介绍的相关资料也将u-boot的启动任务分成两个阶段stage1和stage2进行讲解。stage1主要完成基本硬件设备的初始化和为加载stage2部分的代码准备RAM空间,stage2则是为引导内核准备环境。另外,u-boot还可以工作在启动加载模式和下载模式,在启动加载模式下,是u-boot正常的工作模式,即正常的把内核加载到内核中,而在下载模式下,一般是u-boot要更新nand flash上的内核映像,或者是第一次烧写内核映像到flash。其实u-boot的功能和STM32处理器的IAP程序功能极为相似。

 

其他相关参考资料:

对于不了解bootloader概念的入门绝好资料!!!

http://www.ibm.com/developerworks/cn/linux/l-btloader/

 

u-boot源代码分析

http://blog.csdn.net/gqb_driver/article/details/8931775

http://blog.csdn.net/ghostyu/article/details/6953982

 

Linux Core U-boot User's Guide,TI出的一篇基于BBB等一些平台的讲解内容涉及设备树、MLO和u-boot编译、环境变量等的绝好的资料,必看!!!

http://processors.wiki.ti.com/index.php/Linux_Core_U-Boot_User%27s_Guide#Overview

 

bootloader讲解 之 《嵌入式Linux系统开发技术详解-基于ARM》 (2) 根据开发板来对u-boot进行配置、编译以及如何使用u-boot。

http://blog.csdn.net/yongbudl2012/article/details/34939613

 

关于u-boot

http://www.cnblogs.com/yashi88/archive/2010/02/11/1667548.html

 

那么u-boot到底完成了那些任务呢?

ARM-Linux嵌入式系统启动流程

stage1

step1:硬件设备初始化。

step2:为加载Bootloader的stage2准备RAM空间。

step3:拷贝Bootloader的stage2到RAM空间中。

step4:设置好堆栈。

step5:跳转到stage2的C入口点。

 

stage2

step1:初始化本阶段要用到的硬件设备。

step2:检测系统内存映射。

step3:将kernel映像和根文件系统映像从flash上读到RAM空间中。

step4:为内核设置启动参数。

step5:调用内核。

 

4、启动内核kernel

            内核是一个操作系统功能发挥的根本,从内核源码的目录结构上看,可以知道内核大致分为五个模块,分别是内存管理、进程调度、虚拟文件系统、网络接口、进程间通信。

 

相关参考资料

获取内核源码的标准

www.kernel.org

 

英蓓特提供的Angstrom源码

http://www.embest-tech.cn/download/35-drivers/

 

另外,TI-SDK的包里面也有内核源码,TI的资料只能用海量两个字来形容!

 

内核架构分析

http://www.redflag-linux.com/source/Documents/kernelframe.html

 

Linux内核启动分析,分析非常透彻,包括原码分析

http://blog.csdn.net/gqb_driver/article/details/26954425

 

从操作系统的启动角度来看,内核的启动过程分为三个阶段。

step1:内核的自解压过程。

step2:主要工作是设置ARM处理器的工作模式、使能MMU、设置一级页表等。

step3:主要为C代码,包括内核初始化的全部工作。

 

5、加载根文件系统

        根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。

        那么根文件系统在系统启动中到底是什么时候挂载的呢?先将/dev/ram0挂载,而后执行/linuxrc.等其执行完后。切换根目录,再挂载具体的根文件系统.根文件系统执行完之后,也就是到了Start_kernel()函数的最后,执行init的进程,也就第一个用户进程。对系统进行各种初始化的操作。

        根文件系统之所以在前面加一个”根“,说明它是加载其它文件系统的”根“,既然是根的话,那么如果没有这个根,其它的文件系统也就没有办法进行加载的。它包含系统引导和使其他文件系统得以挂载(mount)所必要的文件。根文件系统包括Linux启动时所必须的目录和关键性的文件,例如Linux启动时都需要有init目录下的相关文件,在 Linux挂载分区时Linux一定会找/etc/fstab这个挂载文件等,根文件系统中还包括了许多的应用程序bin目录等,任何包括这些Linux 系统启动所必须的文件都可以成为根文件系统。

 

根文件系统至少包括以下目录:

/etc/存储重要的配置文件。

/bin/存储常用且开机时必须用到的执行文件。

/sbin/存储着开机过程中所需的系统执行文件。

/lib/存储/bin/及/sbin/的执行文件所需的链接库,以及Linux的内核模块。

/dev/存储设备文件。

注:五大目录必须存储在根文件系统上,缺一不可。

以只读的方式挂载根文件系统,之所以采用只读的方式挂载根文件系统是因为:此时Linux内核仍在启动阶段,还不是很稳定,如果采用可读可写的方式挂载根文件系统,万一Linux不小心宕机了,一来可能破坏根文件系统上的数据,再者Linux下次开机时得花上很长的时间来检查并修复根文件系统。

    挂载根文件系统的而目的有两个:一是安装适当的内核模块,以便驱动某些硬件设备或启用某些功能;二是启动存储于文件系统中的init服务,以便让init服务接手后续的启动工作。

 

相关参考资料

Linux内核启动及根文件系统加载过程

http://blog.csdn.net/gqb_driver/article/details/26954425