1、目 录1vxworks映像类型11.1可加载的映像类型(vxwoks)12vxworks映像启动顺序12.1可加载型vxworks映像启动顺序22.2基于ROM的vxworks映像启动顺序22.3基于ROM驻留型vxworks映像启动顺序33BSP基础知识33.1BSP的定义33.2BSP的功能33.3 BSP的组成44BSP的启动44.1 BSP的启动流程44.2.1romInit.s:romInit()函数54.2.2bootInit.c:romStart()函数144.2.3bootConfig.c分析244.2.4sysLib.s:sysInit()函数264.2.5usrconfig
2、.c:usrInit()函数274.2.6usrconfig.c:usrRoot()函数285总结3132VxWorks及BSP启动流程与顺序 李守轩摘要:本文首先介绍vxworks映像的类型及各类型vxworks映像的启动顺序;然后介绍BSP的启动流程与初始化顺序。关键词:vxworks映像;BSP启动;代码分析1 vxworks映像类型对于vxworks映像的启动情况,从根本上看,在初始化和装载vxworks映像的过程中,处理器所执行的步骤在逻辑上是一样的。对于有些处理器可能需要增加一些额外的步骤,而另一些处理器可能会省略掉某些步骤。当构造vxworks映像时,根据需要可以构造不同类型的映
3、像,系统把这些映像划分成以下三种类型。1.1 可加载的映像类型(vxwoks)可加载型映像的执行需要通过引导代码把它装载到目标机RAM中,然后才开始执行。而引导代码分为两种:(1) 引导代码固化在ROM或FLASH中;(2) 引导代码是一个独立的vxworks应用;引导代码通常也是一种vxworks映像,也被称为引导映像。它的作用就是把包含应用的vxworks映像装入到RAM中。引导映像可能在ROM/FLASH中执行,也可能在RAM中执行。1.2 基于ROM的映像类型(vxworks_Rom & vxwoks_RomCompress)基于ROM的vxworks映像在执行前首先把自己从ROM/F
4、LASH中装载到RAM中,这种类型的映像通常在启动阶段较慢,但在执行阶段比ROM驻留型要快。1.3 基于ROM驻留映像类型(vxwoks_RomResident)ROM驻留型映像在启动时把数据段拷贝到目标机RAM中,这种类型的映像在启动阶段比较快,当RAM空间比较小的时候通常使用它。在嵌入式应用中通常会使用该类型的映像,然而,它在目标机上执行的速度要比其他类型要慢,原因是CPU访问ROM比访问RAM要慢。2 vxworks映像启动顺序在目标机加电启动时发生的顺序启动事件是一个典型vxworks映像需要执行的功能。所有类型的vxworks映像在初始化阶段启动顺序是一样的,处理器通过“jump”跳
5、转指令跳转到ROM或Flash中引导带代码入口处,这段引导代码所要执行的操作包括:(1) 关中断;(2) 初始化目标机内存;(3) 装载适当的vxworks映像段;(4) 跳转到设置目标机为静止状态的代码处。不同类型的vxworks映像启动顺序略有不同,下面介绍vxworks映像的启动顺序。2.1 可加载型vxworks映像启动顺序可加载型vxworks映像引导的详细过程如下: (1)数据段和代码段的装入。系统加电后执行引导带代码,首先把引导代码的代码段和数据段从ROM或Flash里装入RAM中。此时需要考虑下列情况: 压缩型引导代码,这种类型的引导代码在拷贝时,需要对它进行解压缩; 非压缩型
6、引导代码,这种类型的引导代码直接进行拷贝即可; 驻留型引导代码,这种类型的引导代码在拷贝时,仅仅拷贝它的数据段。(2)vxworks映像的装入。引导代码执行后,把vxworks映像装入到RAM中,然后跳转到vxworks映像装入点。(3)系统初始化。执行静态链接在vxworks映像里的系统初始化代码,最终完成系统初始化操作。2.2 基于ROM的vxworks映像启动顺序基于ROM的vxworks映像启动详细过程如下:(1)VxWorks映像的装入。系统加电后执行引导带代码,首先把VxWorks映像的代码段和数据段从ROM或Flash里装入RAM中。此时需要考虑下列情况: 压缩型VxWorks映
7、像,这种类型的VxWorks映像在拷贝时,需要对它进行解压操作; 非压缩型VxWorks映像,这种类型的VxWorks映像直接进行拷贝操作; (2)控制权转移。VxWorks映像被装入后,系统控制权转移给RAM中的vxworks映像的初始化代码。(3)系统初始化。执行静态链接在vxworks映像里的系统初始化代码,最终完成系统初始化操作。2.3 基于ROM驻留型vxworks映像启动顺序基于ROM驻留型的vxworks映像启动详细过程如下:(1)VxWorks映像数据段的装入。系统加电后执行引导带代码,首先把VxWorks映像的数据段从ROM或Flash里装入RAM中。(2)控制权转移。VxW
8、orks映像数据段被装入后,系统控制权转移给ROM或Flash中的vxworks映像的初始化代码。(3)系统初始化。执行ROM或Flash中的静态链接在vxworks映像里的系统初始化代码,最终完成系统初始化操作。3 BSP基础知识BSP是VxWorks的一个重要组成部分,在目标机加电后,首先执行的代码就是BSP,可以说VxWorks的启动流程就是BSP的启动流程。3.1 BSP的定义BSP(board support package)即板级支持包,是介于底层硬件环境和VxWorks之间的一个软件接口。3.2 BSP的功能BSP主要功能是系统加电后初始化目标机硬件、初始化操作系统及提供部分硬件
9、的驱动程序。具体如下。(1)初始化。 CPU初始化。初始化CPU内部寄存器。 目标机初始化。初始化控制芯片的寄存器,为整个软件系统提供底层硬件环境的支持。 系统资源初始化。为操作系统及系统的正常运行做准备,进行资源初始化。(2)使VxWorks能够访问硬件驱动程序。主要是指BSP包含部分必要的设备驱动程序和相关设备的初始化操作。(3)在VxWorks系统中,集成了与硬件相关的软件和部分硬件无关的软件。3.3 BSP的组成开发板上电后首先跑的就是BSP的代码,BSP主要由源文件、头文件、派生文件、Makefile文件组成。BSP包含的具体文件请参考相关资料。BSP在Tornado安装目录中的位置
10、如图3.1中所示。4 BSP的启动下面首先给出BSP的启动流程,然后对启动过程执行的函数进行具体分析,这是本文的重点和难点,是建立在作者自己理解角度和深度基础之上的。4.1 BSP的启动流程BSP的启动流程如图3.2所示,从代码执行的角度描述了BSP的启动过程及启动过程中先后调用函数的功能。4.2 BSP的启动过程分析从BSP的启动流程图中可以看出BSP启动过程中先后执行的函数,上面对这些函数进行详细描述。4.2.1 romInit.s:romInit()函数1. romInit()函数的功能romInit()函数与包含在romInit.s文件中,且用汇编语言编写。它是系统加电后首先执行的代码
11、,也是所有从ROM/FLASH启动的VxWorks映像的入口点。它执行目标机最小的初始化操作及调用romStart()函数,其它硬件初始化操作推迟到sysHwInit()函数中进行。romInit()函数必须包含下列功能:(1)屏蔽处理器中断及处理器复位;(2)初始化内存系统;(3)初始化堆栈指针和其它寄存器,开始执行romStart()函数及传递启动类型。2. 冷启动与热启动 (1)冷启动。所谓冷启动是指硬件环境通过加电启动。在romInit()函数中需要保存系统启动类型,启动类型的宏定义为BOOT_COLD。 (2)热启动。所谓热启动是通过调用reboot()、Ctrl+X或异常中断重新启
12、动目标机系统。实际上这些操作是把控制权传递给ROM中的监控函数sysToMonitor(),这个函数包含在sysLib.c文件中,如执行sysToMonitor(2),则系统执行热启动。3. 代码分析(1)X86硬件相关知识 IDT是中断描述表,是由门描述符组成的一个数组,每个门描述符对应一个中断/ 异常向量,其可以保存在内存中的任何位置,CPU通过访问IDTR寄存器获取IDT的位置。IDTR寄存器的长度为48位,其中包括保存IDT的32位线性地址和16位的大小。对于IDTR寄存器的操作包括两个指令:一个是LIDT,另一个是SIDT。LIDT用来将指定IDT所在线性地址和其长度装入LDTR寄存
13、器;而SIDT则是将IDTR寄存器的内容读出。 从实模式切换到保护模式之前,必须将基址和限长的值用指令LGDT装入GDTR,一旦系统切换到保护模式,则表所在的物理地址就不再改变,同时立即启用全局描述表,IDT表的基址用指令IDTR。 32位控制器CR0的0位为保护允许位PE(protected enable),用于启动保护模式,pe=1,保护模式启动;pe=0,实模式下运行。 实模式进入保护模式的步骤:初始化段描述符准备并加载GDTR打开地址线A20设置CR0寄存器,进入保护模式跳转32位代码段。 A20地址线的激活请查阅相关资料。上述相关知识是作者在理解代码过程中了解的,当然与硬件相关的知识
14、很多,详细请查阅相关资料。(3) 代码分析从作者理解的角度给出了romInit()函数的流程图,如图4.1所示。具体的代码分下如下:包含C的4个头文件。vxWorks.h为系统头文件sysLib.h为系统提供给BSP的头文件config.h是BSP的头文件Asm.h是系统头号文件开始数据段。以下内容出现在数据段里:.globl copyright_wind_river.long copyright_wind_river /* the first in .data */.globl romwait.globl _romInit.globl _sdata 等等申明全局变量_romInit和_sda
15、ta等定义一个以0结尾的字符串”start of data”。这个串出现在数据段的第一个无名变量之后.text.align 16 .text开始代码段,以下内容出现在代码段里。.align 16使对齐编译器进行填充,使得下一条指令出现在能被16整除的地址上。对齐可使CPU取指令快一点。 进入rominit处: Cli关中断 jmp cold 跳转到cold处。这是段内相对跳转。 balign 16 在32-bit代码前加这样的前缀可以让它变为16-bit代码;在16-bit代码前可以变为32-bit代码进入romWarmHigh处:Cli关中断movl SP_ARG1(%esp),%ebx 把
16、esp+ SP_ARG1偏移处的值赋予ebx寄存器jmpwarm 跳转到warm处。这是段内相对跳转.balign 16 在32-bit代码前加这样的前缀可以让它变为16-bit代码;在16-bit代码前可以变为32-bit代码进入romWarm Low处:cli关中断cld清方向标志movl$ RAM_LOW_ADRS,%esi/* 把RAM_LOW_ADRS 地址赋给esi movl$ ROM_TEXT_ADRS,%edi/*把RAM_LOW_ADRS 地址赋给edimovl$ ROM_SIZE,%ecx/*把RAM_size 地址赋给ecxshrl$2,%ecx/* ecx 右移两位25
17、6字节变成64个双字repmovsl循环执行esi-edi 真到ecx=0movl SP_ARG1(%esp),%ebx 把esp+ SP_ARG1偏移处的值赋予ebx寄存器jmpwarm/* jump to warm */.ascii Copyright 1984-2002 Wind River Systems, Inc.balign 16,0x90cold: /cold处.byte0x67, 0x66与blign16功能相同lidt%cs:(romIdtr - romInit)将中断和量表加载到cs:(romIdtr - romInit)处.byte0x67, 0x66与blign16功能
18、相同lgdt%cs:(romGdtr - romInit)将断描述表加载到cs:( romGdtr - romInit)处mov%cr0,%eax/* move CR0 to EAX */.byte0x66/* next inst has 32bit operand */or$0x00000001,%eax/* set the PE bit */mov%eax,%cr0/* move EAX to CR0 */ /以上代码的作用是将控制寄存器 cr0的PE位 置1jmpromInit1/*跳转到rominit1处。Rominit1:进入rominit1处.byte0x66/* next ins
19、t has 32bit operand */mov$0x0010,%eax/* set data segment 0x10 is 3rd one */mov%ax,%ds/* set DS */mov%ax,%es/* set ES */mov%ax,%fs/* set FS */mov%ax,%gs/* set GS */mov%ax,%ss/* set SS */.byte0x66/* next inst has 32bit operand */mov$ ROM_STACK,%esp /* set lower mem stack pointer */.byte0x67, 0x66/* ne
20、xt inst has 32bit operand */ljmp$0x08, $ ROM_TEXT_ADRS + romInit2 - romInit现在已进入保护模式。然而各个段寄存器的值,以及它们的高速缓存寄存器中的值还是实模式下的。把DS, ES, FS, GS, SS寄存器设为0x0010,即指向GDT的第2项(从0开始),DPL=0。它们都指向一个段。把堆栈指针esp设为ROM_STACK,由于CS还是以前的值,意味着目前代码段的属性还是16-bit代码。所以使用指令前缀以执行32-bit代码。执行一个远程段间跳转修改CS。CS的新值为0x08,即GDT的第1项,DPL=0。修改CS
21、时它的高速缓存寄存器也会自动更新。romIDT(Interrupt Description Table),中断描述符表,空的。 romGdtr: .word 0x0027 .long ROM_TEXT_ADRS + ROM_GDT /设置段描述表的大小及基地址 balign 16,0x90 romGdt: 段描述表/* 0(selector=0x0000): Null descriptor */.word0x0000.word0x0000.byte0x00.byte0x00.byte0x00.byte0x00/* 1(selector=0x0008): Code descriptor */.w
22、ord0xffff/* limit: xffff */段界限低16位.word0x0000/* base : xxxx0000 */ 基地址低16位.byte0x00/* base : xx00xxxx */基地址中间8位.byte0x9a/段属性.byte0xcf/段属性 含段界限高4位.byte0x00/* base : 基地址高8位/* 2(selector=0x0010): Data descriptor */.word0xffff/* limit: xffff */.word0x0000/* base : xxxx0000 */.byte0x00/* base : xx00xxxx
23、*/.byte0x92/* Data r/w, Present, DPL0 */.byte0xcf/* limit: fxxxx, Page Gra, 32bit */.byte0x00/* base : 00xxxxxx */* 3(selector=0x0018): Code descriptor, for the nesting interrupt */.word0xffff/* limit: xffff */.word0x0000/* base : xxxx0000 */.byte0x00/* base : xx00xxxx */.byte0x9a/* Code e/r, Presen
24、t, DPL0 */.byte0xcf/* limit: fxxxx, Page Gra, 32bit */.byte0x00/* base : 00xxxxxx */* 4(selector=0x0020): Code descriptor, for the nesting interrupt */.word0xffff/* limit: xffff */.word0x0000/* base : xxxx0000 */.byte0x00/* base : xx00xxxx */.byte0x9a/* Code e/r, Present, DPL0 */.byte0xcf/* limit: f
25、xxxx, Page Gra, 32bit */.byte0x00/* base : 00xxxxxx */.balign 16,0x90romInit2:/进入rominit2处cli关中断movl$ ROM_STACK,%esp 设置esp成rom_stack 0x8000h#ifdefined (TGT_CPU) & defined (SYMMETRIC_IO_MODE) movl $ MP_N_CPU, %eax lock incl (%eax)#endif定义 TGT_CPU 和SYMMETRIC_IO_MODE#ifdefINCLUDE_WINDML/* WindML + Vesa
26、BIOS initialization */Movl $ VESA_BIOS_DATA_PREFIX,%ebx/* move BIOS prefix addr to EBX */movl$ VESA_BIOS_KEY_1,(%ebx)/* store BIOS */addl $4, %ebx/* increment EBX */movl$ VESA_BIOS_KEY_2,(%ebx)/* store DATA */movl$ VESA_BIOS_DATA_SIZE,%ecx/* load ECX with nBytes to copy */shrl $2,%ecx/* get nLongs t
27、o copy */movl$0,%esi/* load ESI with source addr */movl$ VESA_BIOS_DATA_ADDRESS,%edi/* load EDI with dest addr */repmovsl/* copy BIOS data to VRAM */#endif/* INCLUDE_WINDML */ */定义INCLUDE_WINDML 并初始化VesaBIOS #ifndef INCLUDE_IACSFLcallFUNC(romA20on)/* enable A20 */cmpl$0, %eax/* is A20 enabled? */jne
28、romInitHlt/* no: jump romInitHlt */#endif定义 INCLUDE_IACSFL warm:/进入warm处ARCH_REGS_INIT/* 初始化 DR0-7 标志位寄存器 */#if(CPU = PENTIUM) | (CPU = PENTIUM2) | (CPU = PENTIUM3) | (CPU = PENTIUM4)xorl%eax, %eax/* zero EAX */movl%eax, %cr4#endif如果是奔腾系列初将cr4清零movl$romGdtr,%eax加载前面的romGdt表subl$ FUNC(romInit),%eaxad
29、dl$ ROM_TEXT_ADRS,%eaxpushl%eaxcallFUNC(romLoadGdt)movl$ STACK_ADRS, %esp/* initialise the stack pointer */movl $ ROM_TEXT_ADRS, %esi/* get src addr(ROM_TEXT_ADRS) movl $ romInit, %edi/* get dst addr(romInit) */cmpl%esi, %edi/* is src and dst same? */je romInit4 如果rom_text_adrs和rominit处相同的话就跳转到 romi
30、nit4处执行movl $ FUNC(end), %ecx/* get end addr */subl %edi, %ecx/* get nBytes to copy */shrl $2, %ecx/* get nLongs to copy */cld /* clear the direction bit */rep/* repeat next inst ECX time */movsl 如果rom_text_adrs和rominit处不相同的话将rom_text_adrs处拷贝至(end-rominit)处romInit4:进入rominit4处xorl%ebp, %ebpebp清零push
31、l$0popfl /清标志寄存器 pushl%ebxebx进栈movl$ FUNC(romStart),%eax/* jump to romStart */call*%eax /跳转到romStart处执下一步。romInitHlt:pushl%eaxcallFUNC(romEaxShow)/调用romEaxShow显示eax寄存器的内容hltjmpromInitHlt 死循环,当机.balign 16,0x90FUNC_LABEL(romA20on) /进入romA20on处:打开a20以扩大寻址范围空间callFUNC(romWait) /调用romwait等待movl$0xd1,%eax
32、/* 向64h寄存器写入 d1h*/outb%al,$0x64callFUNC(romWait)movl$0xdf,%eax/* Enable A20 */outb%al,$0x60 向60h寄存器写入 dfh*/callFUNC(romWait) 调用romwait等待movl$0xff,%eax向64h寄存器写入 ffh*/outb%al,$0x64callFUNC(romWait) 调用romwait等待movl$0x000000,%eax/* Check if it worked */movl$0x100000,%edxpushl(%eax)pushl(%edx)movl$0x0,(%
33、eax)movl$0x0,(%edx)movl$0x01234567,(%eax)cmpl$0x01234567,(%edx)popl(%edx)popl(%eax)jneromA20on0以上代码是检查A20地址线是否打开,就是检查000000h和100000是否相同,即当超过20位时看看会不会和第0位时相同,如果不相同说明打开了A20地址线。否则没打开。尝试别一种方法打开/* another way to enable A20 */movl$0x02,%eaxoutb%al,$0x92 /向92H寄存器写入02xorl%ecx,%ecx ecx清零romA20on1:/接着上面向92H寄存
34、器写入02后inb$0x92,%alandb$0x02,%alloopzromA20on1 /确保92H为02movl$0x000000,%eax/* Check if it worked */movl$0x100000,%edxpushl(%eax)pushl(%edx)movl$0x0,(%eax)movl$0x0,(%edx)movl$0x01234567,(%eax)cmpl$0x01234567,(%edx)popl(%edx)popl(%eax)jneromA20on0movl$ 0xdeaddead,%eax/* error, cant enable A20 */ret以上代码是
35、检查A20地址线是否打开,就是检查000000h和100000是否相同,即当超过20位时看看会不会和第0位时相同,如果不相同说明打开了A20地址线。否则没打开。不再尝试 无法打开A20romA20on0:xorl%eax,%eaxret /成功打开A20同进EAX清零 .balign 16,0x90FUNC_LABEL(romLoadGdt)movlSP_ARG1(%esp),%eaxlgdt(%eax)movw$0x0010,%ax/* a selector 0x10 is 3rd one */movw%ax,%dsmovw%ax,%esmovw%ax,%fsmovw%ax,%gsmovw%
36、ax,%ssret把DS, ES, FS, GS, SS寄存器设为0x0010,即指向GDT的第2项(从0开始),DPL=0。它们都指向一个段。FUNC_LABEL(romWait) /等待状态xorl%ecx,%ecx ecx清零romWait0:movl$0x64,%edx/* Check if it is ready to write */inb%dx,%alandb$2,%alloopnzromWait0 /确保64h寄存器的值为02ret.balign 16,0x90 同上一次FUNC_LABEL(romEaxShow) /显示eax寄存器的内容/* show EAX registe
37、r in your display device available */ret4.2.2 bootInit.c:romStart()函数1. romStart()函数的功能romStart()函数包含在bootInit.c文件中,且用C语言编写。它是系统最先执行的第一个C语言程序,为ROM映像执行必要的代码重定位、解压和RAM初始化操作。其具体操作如下:(1) 拷贝适当的ROM映像段到RAM中;(2) 清理没有被使用的那部分内存(冷启动时);(3) 执行解压操作(如果需要);(4) 调用预内核的通用初始化程序usrInit()。2. romStart()函数相关知识(1) ROM的布局。如图
38、4.2所示。从上图中可以发现压缩型映像和非压缩型映像处于不同的ROM空间。(2) RAM的布局。如图4.3所示。从图4.3中可以发现用户保留区处于RAM空间的顶端和底部,映像重新定位区位于保留堆栈区的上面。3. 代码分析(1) 代码综述bootrom有三种类型:ROM_RESIDENT、UMCOMPRESS和COMPRESS。第一种是一直运行在rom中的映象,只把data段拷贝到ram里面;第二种是非压缩方式的映象,data段和text段都要拷贝到ram里面,并在ram里面运行;第三种是压缩方式的映象,生成的时候编译器会把除掉romInit.s和bootInit.c之外的目标文件压缩并“汇编”
39、成一个bootrom.Z.s,最后和romInit.o,bootInit.o,version.o进行链接,生成bootrom映象。所以它也是要全部拷贝到ram中,且必须要进行压缩的工作。而这些工作基本上都是在bootInit.c中进行的。bootInit.c里面主要就是romStart()这个函数,让我们来分析一下它。它的入口参数是startType,是一个启动类型标志,如BOOTCLEAR、BOOTNORMAL等,这在后面清内存时会用到。函数一开始定义了一个函数指针变量absEntry,它最后指向的就是ursInit()或compressedEntry()函数。接下来就是对三种bootrom
40、映象类型进行不同的操作,下面我们以arm为例来分别说明。1ROM_RESIDENT:它要拷贝的只是data段。直接调用“ copyLongs (UINT *)(etext + 4), (UINT *) RESIDENT_DATA, (UINT) edata - (UINT) RESIDENT_DATA) / sizeof (long);”来实现。这时系统是运行在rom上的,链接器把所有的函数都定位在rom空间上,所以调用copyLongs时没有计算偏移,而拷贝的目标地址是RESIDENT_DATA,对于arm而言RESIDENT_DATA就是sdata,这是在romInit.s中定义的。通过o
41、bjdumparm这个工具可以看到sdata定位在RAM_HIGH_ADRS0x4这个位置上。而etext + 4则是rom上data段的起始地址。这样,完成data段的拷贝。然后如果startType为冷启动,那么清零SYS_MEM_BOTTOM到栈底(RESIDENT_DATA - STACK_SAVE)以及data段结束之后(edata到SYS_MEM_TOP)的内存空间。然后将函数开始定义的指针absEntry指向usrInit(在rom中):absEntry = (FUNCPTR)usrInit;并带上startType跳过去运行:(absEntry)(startType),完成。2
42、UMCOMPRESS:一开始它将text段和data段都拷贝到ram中:(FUNCPTR)ROM_OFFSET(copyLongs)(ROM_TEXT_ADRS,(UINT)romInit,ROM_COPY_SIZE / sizeof (long)。这时的ROM_TEXT_ADRS就是代码段在rom上的开始位置,而romInit则是由链接器定位到了RAM_HIGH_ADRS的地址上,所以这时的确是按我们常规的思路拷贝的。并且由于copyLongs函数是定位到ram空间的,所以要计算它在rom上的偏移ROM_OFFSET(copyLongs)。然后象ROM_RESIDENT一样,它也要清零栈底(
43、romInit - STACK_SAVE)以下和映象之上(SYS_MEM_TOP - (romInit + ROM_COPY_SIZE))的内存空间。然后将函数开始定义的指针absEntry指向usrInit(在ram中):absEntry = (FUNCPTR)usrInit;并带上startType跳过去运行:(absEntry)(startType),完成。3COMPRESS:开始的时候把从ROM_TEXT_ADRS起始的长度为romInit到binArrayStart的内容拷贝到romInit位置上。注意,由于romInit被链接器定位到RAM_LOW_ADRS的位置上,这时相当于把r
44、omInit.o、bootInit.o和version.o的内容拷贝到了RAM_LOW_ADRS上。然后和UMCOMPRESS一样清零栈底(romInit - STACK_SAVE)以下的内存空间,不同之处是它接下来清除binArrayStart之上(SYS_MEM_TOP - binArrayStart)的内存空间:fillLongs (UINT *)binArrayStart,(UINT)SYS_MEM_TOP - (UINT)binArrayStart) / sizeof (long), 0)。然后调用解压程序inflate将在rom上的(binArrayEnd - binArrayStart)之间的内容解压到RAM_DST_ADRS(RAM_HIGH_ADRS)的位置上:binArrayStart(absUncompress) (UCHAR *)ROM_OFFSET(binArrayStart),(UCHAR *)R