几年前,我是一个标准的原厂Linux系统开发人员,用公司自己的芯片,移植整套BSP。对于Linux系统启动方面,自认为摸得很熟了,从上电加载的汇编码到Linux内核及文件系统的配置,无一不留下了我修改的代码,细节精确到nand flash的某block某page放了什么代码,内存某个地址加载的是什么,代码某个改动是为了兼容什么project,一清二楚。所以最近接到这个移植系统的工作,信心挺足,估摸硬件没有问题的话几天之内移植就能完成,哪知道被现实结结实实打脸。
之前我用的Linux系统是2.6,Uboot版本不详但Fat32的支持还是我们自己移植进去的,可以说相当跟不上时代了。最新的Kernel早就采用了DTS,Uboot也加了很多功能和驱动,我在移植的过程中几乎完成了一次重新学习的过程。虽然我这次用的板是基于TI的,但我希望能不局限于具体的板子型号,而是把规律性的东西写出来,让后来人有个参考。
你看到的是非授权版本!爬虫凶猛,请尊重知识产权!
转载请注明出处:http://conanwhf.github.io/2017/05/30/bootup-1-main/
访问原文「ARM板移植Linux系统启动(一)启动流程」获取最佳阅读体验并参与讨论
硬件环境
首先交代背景。我在前公司做的板子硬件是自己设计的,系统烧写在nand flash上,也支持从SD卡加载Uboot和Kernel uImage启动。大致的启动流程是BootROM上电读取nandflash上某个固定page的数据,大小不超过2K(为了兼容不同的nand flash page),这是IPL,然后自加载启动程序,初始化RAM, flash,接着读取Uboot,用Uboot加载flash上的内核和跟文件系统,跑起来。Uboot、Kernel、rootdisk都是用特定的工具写在Flash固定的位置,没有基于文件系统。
这次我要做的板子,硬件是基于TI的BeagleBoneBlack,拿掉了一些不需要的硬件,也修改了一些东西。影响比较大的是拿掉了CapeSystem用的EEPROM,网口,TF卡,以及换了一块内存芯片,能用的只剩下USB,和内部的一块emmc。TI的文档对于手动修改、编译、烧写等方面的信息写得语焉不详,源码也有点问题,我的移植过程变得跟想象的不太一样,有点摸索前进的感觉。
启动流程
言归正传。对于任何Linux系统启动的流程,其实都是一样的:
- 系统上电,BootROM寻找可以用来启动的设备,从设备中读取SPL
- 运行SPL读取和跑起来真正的Uboot
- 在Uboot中读取Kernel、根文件系统、DTB,加载到各自的内存地址中
- 将启动参数通过启动命令传递给Kernel,跳转到Kernel运行
- Kernel初始化基本部分,加载根文件系统,启动完毕
SPL和Uboot都来自于U-boot的release,有部分共用的代码,编译完毕同时得到。注意在TI的SDK中(其他的我不清楚是否一样),SPL有两种形式:u-boot-spl.bin和MLO。它们一个是串口启动用,一个是eMMC/Nandflash启动用,不能混用。以上每个阶段不同的东西跑起来的步骤都是一样的:被加载到内存的某个地址,PC指针跳转运行。区别只是在于被谁加载、从哪里加载、加载到内存的哪里。
启动流程的变式
被谁加载
对于一个完整的流程而言,每个文件被谁加载基本上都是固定的,除了根文件系统。根文件系统既有可能被uboot加载,也可能被kernel加载。注意被uboot加载时,根文件系统需要做成一个uboot能够接受的image,有固定的头,类似zImage和uImage的区别。而在调试过程中,加载的过程有可能是手动的,或者通过不同的设备传输拷贝,被谁加载其实完全不重要了。
从哪里加载
这个问题的各种变化,就对应了不同的启动模式(一般意义上的,非硬件意义上)。你当然可以所有的东西都读取自内置emmc,也可以插卡让系统从卡上加载;你也可以从Uart传输uboot的image跑起来,进入uboot的命令行,手动从TF卡或者网络读取kernel;你甚至可以在uboot中从nand flash读取Kernel image,从网络获得根文件系统,从usb中加载dtb,从TF卡里拿到启动参数,最后把它们综合到一起,引导整个系统。
加载到内存的哪里
其实也很简单:加载到哪里都可以,只要不冲突。首先是要计算好文件的大小,不要让后来加载的文件覆盖了之前的;然后是最好将Kernel从较高的地址跑起来,规划好内存分区,以免kernel跑起来后破坏了根文件系统等数据。关于怎么定义内存地址,又是另外一个话题,网上教程很多,我就不多说了。
加载文件的变化
在实际中,Kernel启动的阶段变化很多,Kernel、根文件系统、DTB这三个部分也并非形式僵化的三个文件。比如大部分日常启动时,uboot只加载了kernel和dtb,文件系统往往是kernel后来自己mount上去的,这时候只是传递了一个启动参数,_root=XXXXX_;而调试阶段常用的自带rootdisk的kernel,也不需要另外加载根文件系统。DTB文件是DTS全面应用之后才出现的,定义了板子的硬件设备信息,经过某些特殊配置有时也是可以省略(理论上,并未亲测过)。
在调试中,往往会遇到很多启动的问题,明确启动的这几个基本原则和流程,能让调试的过程更轻松。