vivibootloader的实现参考资料:1.嵌入式系统BootLoader技术内幕,詹荣开(zhanrk@sohu.com)2.GettingstartedwithVIVI,JanhoonLyu,nandy@mizi.com3.嵌入式设备上的Linux系统开发,A.Santhanametc.4.Linuxsystemdevelopmentonanembeddeddevice,A.Santhanam5.vivi有关资料的硬件和软件/linux相关资料说明:本文文字结构照抄”嵌入式系统BootLoader技术内幕,詹荣开(zhanrk@sohu.com)”一文,以vivi中head.S作为stage1,main()作为stage2,解释了VIVIforSMDK2410(basedonS3C2410)开发系统的bootloader的实现。将原文放在这里是为了方便读者。注意,VIVI的实现并非完全跟原文一致。多谢原文作者詹大侠的详细解释。附录有一节__SETUP在kernel的作用来自jeppeter(member)from没作解释。Google“MTDlinuxsubsystem文件系统JFSS2”可以获得足够的解释。如有错误,烦请emailjonesxu@gmail.com告知。多些Ver.0.95JonesSZXujonesxu@gmail.com2004-09-29Chapter1Bootloader基本结构由于BootLoader的实现依赖于CPU的体系结构,因此大多数BootLoader都分为stage1和stage2两大部分。依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stage1中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而stage2则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。BootLoader的stage1通常包括以下步骤(以执行的先后顺序):硬件设备初始化。为加载BootLoader的stage2准备RAM空间。拷贝BootLoader的stage2到RAM空间中。设置好堆栈。跳转到stage2的C入口点。BootLoader的stage2通常包括以下步骤(以执行的先后顺序):初始化本阶段要使用到的硬件设备。检测系统内存映射(memorymap)。将kernel映像和根文件系统映像从flash上读到RAM空间中。为内核设置启动参数。调用内核。1.1BootLoader的stage11.1.1基本的硬件初始化这是BootLoader一开始就执行的操作,其目的是为stage2的执行以及随后的kernel的执行准备好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序):1.屏蔽所有的中断。为中断提供服务通常是OS设备驱动程序的责任,因此在BootLoader的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写CPU的中断屏蔽寄存器或状态寄存器(比如ARM的CPSR寄存器)来完成。2.设置CPU的速度和时钟频率。3.RAM初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。4.初始化LED。典型地,通过GPIO来驱动LED,其目的是表明系统的状态是OK还是Error。如果板子上没有LED,那么也可以通过初始化UART向串口打印BootLoader的Logo字符信息来完成这一点。5.关闭CPU内部指令/数据cache。VIVI在第一阶段完成以下任务Disablewatchdogtimer;disableallinterrupts;initialisesystemclocks;initialisethestaticmemoryAllLEDonsetGPIOforUARTInitializeUART0;copy_myselftoram;jumptoramgetreadtocallCfunctionssetupstackpointercallmain1.1.2为加载stage2准备RAM空间为了获得更快的执行速度,通常把stage2加载到RAM空间中来执行,因此必须为加载BootLoader的stage2准备好一段可用的RAM空间范围。由于stage2通常是C语言执行代码,因此在考虑空间大小时,除了stage2可执行映象的大小外,还必须把堆栈空间也考虑进来。此外,空间大小最好是memorypage大小(通常是4KB)的倍数。一般而言,1M的RAM空间已经足够了。具体的地址范围可以任意安排,比如blob就将它的stage2可执行映像安排到从系统RAM起始地址0xc0200000开始的1M空间内执行。但是,将stage2安排到整个RAM空间的最顶1MB(也即(RamEnd-1MB)-RamEnd)是一种值得推荐的方法。为了后面的叙述方便,这里把所安排的RAM空间范围的大小记为:stage2_size(字节),把起始地址和终止地址分别记为:stage2_start和stage2_end(这两个地址均以4字节边界对齐)。因此:stage2_end=stage2_start+stage2_size另外,还必须确保所安排的地址范围的的确确是可读写的RAM空间,因此,必须对你所安排的地址范围进行测试。具体的测试方法可以采用类似于blob的方法,也即:以memorypage为被测试单位,测试每个memorypage开始的两个字是否是可读写的。为了后面叙述的方便,我们记这个检测算法为:test_mempage,其具体步骤如下:1.先保存memorypage一开始两个字的内容。2.向这两个字中写入任意的数字。比如:向第一个字写入0x55,第2个字写入0xaa。3.然后,立即将这两个字的内容读回。显然,我们读到的内容应该分别是0x55和0xaa。如果不是,则说明这个memorypage所占据的地址范围不是一段有效的RAM空间。4.再向这两个字中写入任意的数字。比如:向第一个字写入0xaa,第2个字中写入0x55。5.然后,立即将这两个字的内容立即读回。显然,我们读到的内容应该分别是0xaa和0x55。如果不是,则说明这个memorypage所占据的地址范围不是一段有效的RAM空间。6.恢复这两个字的原始内容。测试完毕。为了得到一段干净的RAM空间范围,我们也可以将所安排的RAM空间范围进行清零操作。1.1.3拷贝stage2到RAM中拷贝时要确定两点:(1)stage2的可执行映象在固态存储设备的存放起始地址和终止地址;(2)RAM空间的起始地址。1.1.4设置堆栈指针sp堆栈指针的设置是为了执行C语言代码作好准备。通常我们可以把sp的值设置为(stage2_end-4),也即在1.1.2节所安排的那个1MB的RAM空间的最顶端(堆栈向下生长)。此外,在设置堆栈指针sp之前,也可以关闭led灯,以提示用户我们准备跳转到stage2。经过上述这些执行步骤后,系统的物理内存布局应该如下图2所示。1.1.5跳转到stage2的C入口点在上述一切都就绪后,就可以跳转到BootLoader的stage2去执行了。比如,在ARM系统中,这可以通过修改PC寄存器为合适的地址来实现。head.S负责完成硬件初始化操作,具体分析见源码注释,汇编差不多忘光了,下面注释中有关汇编的东西多些。其中linkage.h#defineSYMBOL_NAME_STR(X)#X#defineSYMBOL_NAME(X)X#ifdef__STDC__#defineSYMBOL_NAME_LABEL(X)X##:#else#defineSYMBOL_NAME_LABEL(X)X/**/:#endif#define__ALIGN.align0#define__ALIGN_STR.align0#ifdef__ASSEMBLY__#defineALIGN__ALIGN#defineALIGN_STR__ALIGN_STR#defineENTRY(name)\.globlSYMBOL_NAME(name);\ALIGN;\SYMBOL_NAME_LABEL(name)#endif其中machine.h包括了smdk2410.h(有关开发板的配置),包括memorymap,Porocessormemorymap,FLASH,ROM,DRAM的物理地址和在VIVI中用的虚拟地址(?),Architecturemagicandmachinetype,UART,CPU,DRAM的初始化参数等smdk2410.h进一步包括s3c2410.h,有关CPU的设置,DefinitionofconstantsrelatedtotheS3C2410microprocessor(basedonARM920T)./**vivi/arch/s3c2410/head.S:*Initialisehardware**Copyright(C)2001MIZIResearch,Inc.**Thisprogramisfreesoftware;youcanredistributeitand/ormodify*itunderthetermsoftheGNUGeneralPublicLicenseaspublishedby*theFreeSoftwareFoundation;eitherversion2oftheLicense,or*(atyouroption)anylaterversion.**Thisprogramisdistributedinthehopethatitwillbeuseful,*butWITHOUTANYWARRANTY;withouteventheimpliedwarrantyof*MERCHANTABILITYorFITNESSFORAPARTICULARPURPOSE.Seethe*GNUGeneralPublicLicenseformoredetails.**YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense*alongwiththisprogram;ifnot,writetotheFreeSoftware*Foundation,Inc.,59TemplePlace,Suite330,Boston,MA02111-1307USA***Author:JanghoonLyunandy@mizi.com*Date:$Date:2003/02/2610:38:11$**$Revision:1.18$***History:**2002-05-14:JanghoonLyunandy@mizi.com*-Initialcode**/#includeconfig.h//autoconf.h空的#includelinkage.h//定义#includemachine.h@StartofexecutablecodeENTRY(_start)//入口点ENTRY(ResetEntryPoint)@@Exceptionvectortable(physicaladdress=0x00000000)@//异常向量表物理地址0x0000000@0x00:Reset//最基本操作:复位B是最简单的分支。一旦遇到一个B指令,ARM处理器将立即跳转到给定的地址,从那里继续执行。注意存储在分支指令中的实际的值是相对当前的R15的bReset@0x04:Undefinedinstructionexception//处理未定义的指令UndefEntryPoint:bHandleUndef@0x08:Softwareinterruptexception//软中断SWIEntryPoint:bHandleS