您好,欢迎访问三七文档
当前位置:首页 > 办公文档 > 其它办公文档 > 最为详细的vivi代码分析
通过vivi研究bootloader有一段时间了,基本是在与之相关的基础方面做工作,还没有真正深入研究vivi。以后的学习重心就要放到研究vivi源代码上面了。我想,真正细致地弄清楚vivi实现的细节,对C语言水平的提高,对ARM体系结构的认识,对S3C2410的熟悉,对嵌入式bootloader相关技术,都能有很大的好处。学习的进度会慢一些,但是务求深入,并且打好相关的基础。一、写在前面的话嵌入式系统软件开发主要包括五个方面:bootloader编写(移植)、驱动程序编写(移植)、操作系统裁减(移植)、文件系统制作、应用程序编写(移植)。嵌入式开发流程我已经熟悉,但是仅限于完成最为基本的工作,大部分是借助网络资料,自己独立解决的问题很有限。学习嵌入式系统已经一年了,算是入门了。然而,入门之后如何继续深入学习嵌入式系统开发,如何提高自身的能力?我想,这也许是独立摸索的学习者都会遇到的问题吧。思考之后有所得,核心就是一句话:务实,理论与实践结合!具体说来,就是要不断的认识自己,去了解自己最适合做什么。这是最为重要的,如果不知道做什么,就无法安排学习的重点。嵌入式开发的领域太广,要想在方方面面都深入不太容易(当然牛人除外)。现在对自己的认识如下:本科有硬件、通信背景,但是没有太多机会进行硬件设计。而硬件设计最为重要的就是经验,动手能力,所以不打算把硬件设计作为学习的重点。底层软件开发既需要对硬件熟悉,又需要软件设计能力,正适合我。所以以后的学习,以底层软件开发(bootloader设计、驱动程序设计)为重点,同时也要加强硬件学习。学习有重点,但是嵌入式开发的其他领域也要涉及,了解广博才能更有助于设计。进展慢不要紧,关键是要深入,深入,再深入。真正地去理解这些技术,并且能够熟练的应用。这半年的核心就是bootloader技术研究,打算先看vivi,然后看uboot。手头上的板子有s3c2410、at91rm9200,这些都可以拿来训练,争取能够通过bootloader技术的掌握,同时熟悉了ARM体系结构、ARM汇编、开发工具等等一系列相关内容,总结学习的方法,提高学习能力。二、准备工作在分析vivi源代码的时候,不打算完全按照vivi的代码来进行。我的思路是,以从nandflash启动为主线,分析从上电到引导内核的过程。以自己的理解去实现vivi的源代码,要自己手动编写,即使与vivi的代码相同。只有这样,才能从整体上理解vivi的设计方法,理解vivi各个功能的实现细节。这份文档算是自己的学习笔记。三、bootloaderstage1:【arch/s3c2410/head.S】首先解决一个问题,就是为什么使用head.S而不是用head.s?有了GNUAS和GNUGCC的基础,不难理解主要原因就是为了使用C预处理器的宏替换和文件包含功能(GNUAS的预处理无法完成此项功能)。可以参考前面的总结部分。这样的好处就是可以使用C预处理器的功能来提高ARM汇编的程序设计环境,更加方便。但是因为ARM汇编和C在宏替换的细节上有所不同,为了区分,引入了__ASSEMBLY__这个变量,这是通过Makefile中AFLAGS来引入的(一般在顶层Makefile中定义),具体如下:AFLAGS:=-D__ASSEMBLY__$(CPPFLAGS)在后面的头文件中,会看到很多#ifdef__ASSEMBLY__等的操作,就是用来区分这个细节的。在编译汇编文件时,加入AFLAGS选项,所以__ASSEMBLY__传入,也就是定义了__ASSEMBLY__;在编译C文件时,没有用AFLAGS选项,自然也就没有定义__ASSEMBLY__。由此相应的问题就比较清晰了。这个小技巧也是值得学习和借鉴的。1首先关注一下开始的三个头文件。#includeconfig.h#includelinkage.h#includemachine.h(1)利用sourceinsight来查看【include/config.h】。#ifndef_CONFIG_H_#define_CONFIG_H_#includeautoconf.h#endif/*_CONFIG_H_*/可见,config.h只是包含一个autoconf.h。而关于autoconf.h的生成,在vivi配置文件分析的时候也解释的很清楚了,在这里就不用再细分析了。需要解释的一点是,如果写一个专用的bootloader,不采用vivi的配置机制,那么配置部分就没有这么复杂了,只需要在include文件夹中包含一个配置头文件即可。现在bootloader的设计有两种趋势,一种是针对特定应用,有特殊要求,也就是“专用”。那么设计时,不需要过多的配置,只需要简单的完成引导内核的功能就可以了。二是普通应用,一般是对基本“通用”的bootloader,比如uboot等,然后根据相应的模版进行移植。这就需要了解uboot等的架构,可以进行定制和功能的增加。uboot完成的不仅仅是一个bootloader的功能,还可以提供调试等功能,所以其角色还包含驻留程序这个功能,也就是uboot真正的角色是monitor。当然,可以不加区分,统称为bootloader。而分析vivi源代码的实现,对这两个方向都有帮助。(2)【include/linkage.h】就是实现了ENTRY宏的封装。其实这个头文件也仅仅为head.S提供了服务,实际上没有必要写的这么复杂,可以简化一些。比如,我修改了这个头文件,如下:[armlinux@lqminclude]$catlinkage.h#ifndef_VIVI_LINKAGE_H#define_VIVI_LINKAGE_H#defineSYMBOL_NAME(X)X#ifdef__STDC__#defineSYMBOL_NAME_LABEL(X)X##:#else#defineSYMBOL_NAME_LABEL(X)X/**/:#endif#ifdef__ASSEMBLY__#defineALIGN.align0#defineENTRY(name)/.globlSYMBOL_NAME(name);/ALIGN;/SYMBOL_NAME_LABEL(name)#endif#endif在这里,要加强一下C语言宏的设计和分析能力。下面就几个点简单的分析一下,后面专门就C宏部分做个总结。关于__STDC__这个宏,是编译器自动添加的,含义就是支持标准C。如果支持标准C,那么##的作用就是“连接”,所以SYMBOL_NAME_LABEL(_start)宏展开为_start:(依我的意思理解##也就相当于分号,表示后面可以继续连接其他的一些定义),如果不支持标准C,则利用了C预处理器对注释的处理方式,就是把/**/替换为一个空格(也就是相当于展开的字符后带个空格),可以测试一下。另外,关于ENTRY宏的封装,利用了GNUAS在ARM上的相关特点。首先,利用了分号作为三条语句的连接符(包括最后一个由##展开来的分号),而分号是GNUAS汇编注释符的一种(另外一种是@)。另外,关于ALIGN为什么用.align0。这可以参考GNUAS手册,上面讲解的比较清晰,主要是为了兼容ARM本身的编译器。理解了这个也就不难得出ENTRY(_start)宏展开后的形式了。有一个技巧就是可以通过下面的命令来检测宏展开后的结果,比如:[root@lqmvivi_myboard]#gcc-E-D__ASSEMBLY__-I./includearch/s3c2410/head.Saaa可以查看aaa文件的显示结果,做了一些注释:#1arch/s3c2410/head.S#1built-in#1commandline#1arch/s3c2410/head.S#35arch/s3c2410/head.S#1include/config.h1#14include/config.h#1include/autoconf.h1#15include/config.h2#36arch/s3c2410/head.S2#1include/linkage.h1#37arch/s3c2410/head.S2#1include/machine.h1#19include/machine.h#1include/platform/smdk2410.h1#1include/s3c2410.h1#22include/s3c2410.h#1include/hardware.h1#23include/s3c2410.h2#1include/bitfield.h1#24include/s3c2410.h2#3include/platform/smdk2410.h2#1include/sizes.h1#8include/platform/smdk2410.h2#74include/platform/smdk2410.h#1include/architecture.h1#75include/platform/smdk2410.h2#20include/machine.h2#38arch/s3c2410/head.S2@Startofexecutablecode宏定义展开.globl_start;.align0;_start:.globlResetEntryPoint;.align0;ResetEntryPoint:下面是装载中断向量表,ARM规定,在起始必须有8条跳转指令,你可以用b标号也可以用ldrpc标号。这样的8条规则的标志被arm定义为bootloader的识别标志,检测到这样的标志后,就可以从该位置启动。这样的做法是因为开始的时候不一定有bootloader,必须有一种识别机制,如果识别到bootloader,那么就从bootloader启动。@@Exceptionvectortable(physicaladdress=0x00000000)@@0x00:ResetbReset@0x04:UndefinedinstructionexceptionUndefEntryPoint:bHandleUndef@0x08:SoftwareinterruptexceptionSWIEntryPoint:bHandleSWI@0x0c:PrefetchAbort(InstructionFetchMemoryAbort)PrefetchAbortEnteryPoint:bHandlePrefetchAbort@0x10:DataAccessMemoryAbortDataAbortEntryPoint:bHandleDataAbort@0x14:NotusedNotUsedEntryPoint:bHandleNotUsed@0x18:IRQ(InterruptRequest)exceptionIRQEntryPoint:bHandleIRQ@0x1c:FIQ(FastInterruptRequest)exceptionFIQEntryPoint:bHandleFIQ下面是固定位置存放环境变量@@VIVImagics@@0x20:magicnumbersowecanverifythatweonlyput.long0@0x24:.long0@0x28:wherethisviviwaslinked,sowecanputitinmemoryintherightplace.long_start//_start用来指定链接后的起始装载地址装载到内存中的地址@0x2C:thiscontainstheplatform,cpuandmachineid.long((124)|(616)|193)@0x30:vivicapabilities.long0@0x34:bSleepRamProc@@StartVIVIhead@Reset://上电后cpu会从0x0地址读取指令执行,也就是bReset@disablewatchdogtimermovr1,#0x53000000movr2
本文标题:最为详细的vivi代码分析
链接地址:https://www.777doc.com/doc-6378046 .html