一 基本概念及引导概述
1 bootloader/bootrom/bootstrap
CPU上电经自复位的过程(由硬件时序逻辑决定的Power-On StrappingSequence)后,指令指针指向一个固定的地址(after-reset startingpoint)。这个固定地址因体系架构而异:在一个基于 ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址0x00000000 处开始执行;在一个MIPS体系架构的嵌入式系统中,系统在上电或复位时通常都从地址0xBFC00000(0x1FC00000)处开始执行。
无论是ARM中的0x00000000,还是MIPS中的0x1FC00000,这个入口地址处往往存放的就是系统的Boot Loader程序。这个地址往往映射(可参考具体datasheet的Memory Map)为Boot Rom的地址空间,这里的Boot Rom通常为NOR Flash或者SPI Flash(早期的可能为EEPROM等非易失性存储介质)。由此可见,bootloader(引导加载程序)是系统上电后运行的第一段软件代码。bootrom则是用来存储bootloader的ROM/Flash芯片。
当然bootrom(ROM/Flash)上不仅仅只是存储bootloader,往往还存储着系统映像、应用程序资源和用户配置数据等信息。这样,嵌入式设备每次重新上电后,总能加载系统和恢复上一次的配置。因为嵌入式设备中往往没有配备磁盘,SDRAM又是易失的,因此bootrom上往往还存放着系统映像等数据(可能是压缩的)。
在嵌入式系统中,通常没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动微码),因此整个系统的加载启动任务就完全由Boot Loader来完成。此时,CPU只认得指令,因此bootloader一般以纯汇编指令开始,而不应该包含任何ELF格式的文件头信息(这些信息只有特定OS才能识别)。
bootstrap是bootloader的一部分,是最初级的引导,旨在初始化CPU、内存控制器、时钟、堆栈,目标是让CPU正常运作起来。我们可以认为bootloader = bootstrap program + boot image。boot image往往初始化最小OS内核,搭建网络下载通道,提供一个可以交互的命令行,以便自我更新(update boot)或下载更新系统映像(update vxWorks)。bootloader通常都分为Stage1和Stage2两个阶段,这里的bootstrap program对应Stage1,boot image则对应Stage2。下面从代码构成角度入手,阐述VxWorks下的bootstrap program和boot image的含义。
在vxWorks中,若含有bootlader(即下文提到的Boot Image + Loadable Image组合类型),则bootstrap program主要是指romInit.s+bootInit.c,boot image主要是指有bootConfig.c、sysAlib.s、sysLib.c以及BSP目录下的其他文件编译链接而成。通常为了节省空间,boot image部分被压缩作为数据段存储在bootstrap program之后——bootrom.bin。bootstrap program是在ROM/Flash中运行的,当然为了执行更快,其后半部分也可能拷贝到RAM中执行。很显然,bootstrap program是不能压缩的,否则无法完成自举。
对于没有bootloader的引导型映像(即下文提到的ROM-based Image类型,vxWorks_rom*.bin),其固化在ROM/Flash的第一部分bootstrap program可能有romInit.s+romStart.c组成,后面无boot image,紧随其后的为vxWorks映像(可能被压缩)。
2 vxWorks的bootrom类型
bootrom的类型
前缀或后缀 说明 bootrom 压缩的bootrom映像(除bootstrap代码外) bootorm_uncmp 非压缩的bootrom映像 bootrom_res ROM驻留型的bootrom映像 无后缀 ELF可执行文件格式 .hex Motorola S-record文件格式 .bin 裸的二进制格式(一般烧到ROM/Flash零地址处)
3 引导流程概述
系统上电后,CPU开始从预先安排的地址上开始执行一段指令,这段指令的作用首先是将可执行程序所需的最小环境搭建起来。这个初始化过程包括初始化CPU、内存控制器及各种必需输入/输出设备、磁盘控制器等等。以X86体系结构来说,需要初始化CPU、北桥、南桥,常说的BIOS就是这样一段初始化程序。在那些没有BIOS的架构中,这一工作由系统的bootloader完成。
建立了最小可运行系统,操作系统的内核就可以运行了。于是,BIOS或bootloader程序将操作系统从能够永久保存内容的存储介质中读取出来加载到内存中。这些可永久保存内容的存储介质可能是NVROM、磁盘、光盘等,例如我们常说的操作系统光盘。
通常,操作系统在加载过程中还会对硬件环境做一次重新初始化,这一次初始化就不仅仅是最小运行环境了。包括CPU在内的全部计算机所包含的硬件都会被操作系统依次初始化,同时初始化的还包括操作系统内核本身。这样,整个系统就处于一种可控状态,可以开始执行用户应用程序了。
从引导到系统启动是一个bottom-up的过程;启动后,用户交互到系统响应则是一个top-down的过程。
二 VxWorks映象
1 VxWorks映象的组成
VxWorks映象由文本段(.text/.code),数据段(.data)和BSS段(.bss)组成。文本段相当于代码段,是由一些指令组成的;数据段就是由一些初始化过的全局和静态变量组成;BSS段也是由全局变量和静态变量组成,只不过他们都没有经过初始化。
2 VxWorks映象的类型
(1).VxWorks(Loadable binary VxWorks image)
RAM based VxWorks image, linked to RAM_LOW_ADRS. It is loaded into RAM via some external program such as abootROM.This is the default development image.
(2).vxWorks_rom(Uncompressed ROMable binary VxWorks image)
RAM based image that starts in ROM. The ROM startup code copies the entireimage to RAM and then jumps to it. This image generally has a slower startup time, but faster execution time, than vxWorks_romResident.
(3).vxWorks_romCompress(compressed ROMable binary VxWorks image)
Compressed RAM based image thatstartsin ROM. This image can fit almost twice the code as other ROM images.But it has the slowest boot time, since the image must be uncompressed. The run-time speed is the same as for vxWorks_rom.
(4).vxWorks_romResident(ROM-resident version binary VxWorks image)
ROM resident image. The program textremains in ROM, only the data iscopied to RAM. This image has the fastest boot time and uses the least amount of RAM, but runsslower on boards with slow ROM access.
相关后缀的含义同bootrom。
我们可将VxWorks Image的文件类型划分为两类三种:
A.加载型映象(VxWorks类型)
(1)Loadable Image是包含用户程序的VxWorks操作系统映象,其不具备引导功能,需要借助bootloader引导程序通过网口或串口下载到RAM中。bootloader在此扮演了“搬运工”的角色。
B.可引导型映象
可引导型(Bootable)映象包含含有用户程序的VxWorks操作系统映象,并包括完整的引导代码,可以在系统上电后自动完成自身的引导。我们在前面已经分析过,该类映像往往由bootstrap program(romInit.s+romStart.c)和紧随其后的为vxWorks映像(可能被压缩)组成。
(2)ROM-based Image(压缩/没有压缩):即将Image直接烧入ROM/flash,运行时将Image拷入(如果压缩则需解压)RAM中运行。
(3)ROM-resident Image:Image的指令部分驻留在ROM中运行,仅将数据段部分拷入RAM。
注意,以上三种映象都是包含完整VxWorks操作系统的映象,其中后两种可以直接启动并运行起来,但是第一种不行,它必须借助另一个叫做Boot Image的映象(可以在Tornado中的build->build bootrom中生成)才能运行起来,也就是利用Boot Image引导起来后通过网口或串口下载真正包含VxWorks的Loadable Image,然后才能运行起来。也即Boot Image往往和Loadable Image结合起来使用。
现在看来一共有四种映象文件,让我们看看它们的组成:
(1)Boot Image:包含一段起始引导程序(BootStrap Program)和一段ROM引导程序(ROM Boot Program)。
(2)Loadable Image:由操作系统VxWorks和应用组成的映象。
(3)ROM-based Image(压缩/没有压缩):包含一段叫做BootStrap Program的程序+Loadable Image(即有操作系统VxWorks和应用组成的映象)。
(4)ROM-Resident Image:同上。
通过上面我们可以看出,ROM-based Image,ROM-Resident Image,Boot Image三种映象都包含一段叫做BootStrap Program的程序,可以把ROM引导程序的代码段和数据段拷贝到RAM中。同时,它具有启动功能。
三 VxWorks映象启动流程
下面让我们看看三种VxWorks的启动过程:
1 BootImage+LoadableImage
注意:这里和下文的Boot Image是指存放在bootrom中的bootloader映像。
前面提到LoadableImage是依靠Boot Image加载启动的,首先由Boot Image中的BootStrap Program程序把ROM引导程序(ROM Boot Program)加载(如果压缩则需解压)到RAM中的RAM_HIGH_ADRS处,然后跳转到此处执行ROM引导程序,由ROM引导程序负责一系列简单的硬件初始化(网口,串口等),开始下载Loadable Image(即包含应用的VxWorks操作系统)到RAM_LOW_ADRS,然后跳转到此处启动VxWorks操作系统。
下面的图一是一个简单的流程图,后面的图二是更为详细的流程图。
图一
图二(1)
图二(2)
引导过程成功以后,RAM中ROM引导程序占用的空间(从RAM_HIGH_ADRS开始)可以重新被系统利用。
2 ROM-basedImage(压缩/没有压缩)
这种映象由起始引导程序(BootStrap Program)和基于ROM的VxWorks映象组成。因此,这种bootrom的体积较大。BootStrap Program把基于ROM的VxWorks映象加载到内存的RAM_LOW_ADRS处,然后直接启动VxWorks操作系统(如果压缩则需解压)。图三是一个简图,图四是更为详尽的流程图。
图三
图四
3 ROM-residentImage
这种映象由起始引导程序(BootStrap Programs)和驻留ROM的VxWorks映象组成。VxWorks系统文本段(代码段)驻留在ROM,搬移程序负责将data段和bss段搬移到内存的RAM_LOW_ADRS处,直接启动VxWorks映像(含符号表)。此时,RAM_LOW_ADRS是VxWorks映象的加载点,也是VxWorks数据段的起始点。
四 VxWorks映象函数级启动过程
上一节主要是从映象的分类和各种映象的大致加载流程上看VxWorks的启动过程,这一节让我们从函数级看一下VxWorks的启动过程。
1 BootImage+LoadableImage
VxWorks借鉴了传统PC操作系统的引导原理,其将整个引导过程分为两个阶段:
1.1 BOOTROM启动
起始引导程序(BootStrap Program)驻留在ROM中,主要包含:
(1)汇编级的硬件初始化程序romInit.s,用于系统的基本初始化,设置一些重要寄存器的初始值,进行存储器的映射
(2)搬移程序bootInit.c,将ROM引导程序拷贝至RAM的高端地址RAM_HIGH_ADRS,然后跳转到此处执行ROM引导程序。
ROM引导程序运行,将可加载的VxWorks映象下载到内存的指定地址RAM_LOW_ADRS处。
1.2 启动VxWorks内核
下面是具体的流程图:
图五
其中第一阶段的执行流程使用的是上图的左边的源文件中的那些函数(romInit->romStart->usrInit->sysHwinit->usrKernelinit->usrRoot);第二阶段执行流程使用的是上图中右边源文件中的那些函数(sysInit->usrInit->sysHwinit->usrKernelinit->usrRoot->usrAppInit)。下面具体解释:
Stage 1:
(1)romInit.s : romInit() /*entry point for VxWorks in ROM*/
系统上电之后,首先调用的函数就是romInit(),其主要完成两个操作:将CPU设置为正确的工作状态,包括各个寄存器的值以及CPU的工作模式;初始化系统内存并建立堆栈。由于需要设置CPU模式,所以这里的代码必须由汇编代码完成,并且汇编也是建立堆栈的唯一选择。
• 禁止中断,避免陷入不确定混乱状态。
• 清除cache。
• 初始化CPU基本寄存器,调用SDRAM初始化函数初始化UPM。
• 初始化系统堆栈,在内存中建立起堆栈后,系统就具备了高级语言的执行条件。后面的代码可以用C实现。
• 把启动类型(冷启动/热启动)放在堆栈上。
• 直接跳转到bootInit.c : romStart()
(2)bootInit.c : romStart()/*generic ROM initialization*/
这是VxWorks中所执行的第一段C语言代码,但仍在ROM中执行。
• 把ROMBoot Program的代码段和数据段从ROM复制到RAM中。
• 完成程序映象的解压缩(如果映象是压缩版本的)。
• 跳转到bootConfig.c: usrInit()开始执行ROM引导程序。
从这里开始,可引导型映象和加载型映象走上了相同的初始化道路。
(3)bootConfig.c : usrInit()
对于bootrom_uncmp和bootrom_res,在romStart()中直接跳转到usrInit()。对于压缩的bootrom,romStart()跳转到解压缩点,而bootConfig.c中的第一个函数为compressedEntry(),故压缩型bootrom解压后将跳转到compressedEntry()入口处,紧接着调用usrInit()。至于为什么压缩的bootrom需要compressedEntry这个桩函数(Stub Routine),详情参考后文《VxWorks/MIPS运行期的gp重定位》。
/
* compressedEntry – compressed entry point after decompression。
* This routine is the entry point after the bootroms decompress, if
* compression is utilized. This routine must be the first item of the
* text segment of this file.*/
• VxWorks中第一个在RAM中执行的函数。执行操作系统内核所必须的初始化程序。
• Cache程序库的初始化。
• 清零系统的BSS段。
• 初始化中断向量表。
• 使硬件工作在一个“安静”的状态,尽量不产生各种中断或者异常。
• 调用sysHwInit()初始化硬件。
• 调用usrKernelInit()初始化内核的必要组件。
• 调用KernelInit(),初始化VxWorks内核并产生usrRoot根任务。
• 在usrRoot根任务中解析Bootline,产生bootCmdLoop任务,用于启动、加载VxWorks映象。
此时,调试超级终端会有如下打印信息(printBootLogo()):
VxWorks System Boot
Copyright 1984-1998 Wind River Systems, Inc.
CPU: MPC860
Version: 5.4
BSP version: 1.2/0
Creation date: Aug 22002, 09:19:47
Press any key to stopauto-boot…
3
接着,将调用自动引导程序autoboot(timeout),在指定时间timeout内,按任意键可停止自动启动,修改启动行参数(read and execute the ROM commands)。下一步将调用bootload()将指定主机目录下的VxWorks映象下载到目标板的RAM地址RAM_LOW_ADRS处,并跳转(go)到此处执行指令代码,启动VxWorks操作系统。
[VxWorks Boot]: p
boot device : cpm
unit number : 0
processor number : 0
host name : Michel
file name : c:/ftpRoot/vxWorks
inet on ethernet (e) :168.2.7.27:ffffff00
host inet (h) : 168.2.7.10
user (u) : target
Passwd(pw) : target
flags (f) : 0x0
―――――――――――――――――――――――――――
[VxWorks Boot]: @
boot device : cpm
unit number : 0
processor number : 0
host name : Michel
file name : c:/ftpRoot/vxWorks
inet on ethernet (e) :168.2.7.27:ffffff00
host inet (h) : 168.2.7.10
user (u) : target
Passwd(pw) : target
flags (f) : 0x0
Attached TCP/IPinterface to cpm0.
Attaching networkinterface lo0… done.
Stage 2:
(1)bootConfig.c :bootLoad()
加载VxWorks映象,并跳转到它的加载地址,具体流程如下。
usrBootLineInit()中strcpy (BOOT_LINE_ADRS,DEFAULT_BOOT_LINE);从FLASH中读出DEFAULT_BOOT_LINE配置的引导方式及映象文件。
bootload()->usrBootLineCrack (BOOT_LINE_ADRS,¶ms)获取BOOT_PARAMS,通过params.bootDev类型来决定从硬盘、软盘、闪存加载或通过网口、串口下载VxWorks映象。
bootLoad (BOOT_LINE_ADRS, &entry);
{
scsiLoad();/* loada vxWorks image from a local SCSI disk */
fdLoad(); /* loada vxWorks image from a local floppy disk */
ideLoad();/* loada vxWorks image from a local IDE disk */
ataLoad();/* loada vxWorks image from a local ATA disk */
pcmciaLoad();/* loada vxWorks image from a PCMCIA disk device */
tffsLoad();/* loada vxWorks image from a TFFS Flash disk */
tsfsLoad();/* loada vxWorks image from a Target Server File System (TSFS) */
netLoad();/*downLoad a file from a remote machine via the network */
bootLoadModule();/*bootstrap load an object module into memory */
}
下载完成后,直接跳转到VxWorks系统映象起始地址(RAM_LOW_ADRS)处,从系统入口点(VxWorks image entrypoint)开始执行,入口函数为sysInit():
go (entry); /*… and never return */
(2)sysALib.s : sysInit()
与romInit.s: romInit()的初始化过程类似,但不再初始化SDRAM。
(3)usrConfig.c :usrInit()
设置cache的工作模式,板级硬件初始化,初始化Wind内核,启动usrRoot()根任务。
(4)usrConfig.c : usrRoot()
初始化内存,系统时钟,I/O系统,标准输入输出错,异常处理,最后会调用usrAppInit.c中的usrAppInit()进行用户级应用模块的初始化。
此时调试超级终端打印如下信息,Boot引导完成。
/*VXWORKS Image GettingLoaded*/
Loading… 881680
Starting at 0x10000…
Attached TCP/IPinterface to cpm unit 0
Attaching networkinterface lo0… done.
VxWorks
Copyright 1984-1998 WindRiver Systems, Inc.
CPU : MPC860
vxWorks : 5.4
BSP version: 1.2/0
Creation date: Aug 22000
WDB : Ready
2 ROM-based Image(压缩/没有压缩)
和上面那种启动方式相比,这种启动方式省去了一些步骤,执行完romStart()之后就已经将VxWorks加载到RAM中了,因此,下一步就是把控制权交给VxWorks,由VxWorks从usrEntry()开始执行即可。下面是具体的流程:
(1)romInit.s : romInit()
同上。
(2)romStart.c : romStart()
rules.vxWorks中根据映像类型是vxWorks_romCompress、vxWorks_rom或vxWorks_romResident,将ROM_FLAGS_EXTRA定义为CC_ROM_CMP_FLAGS、CC_ROM_CPY_FLAGS或CC_ROM_RES_FLAGS(defs.vxWorks中定义),其中分别定义了ROM_COMPRESS、ROM_COPY或ROM_RESIDENT宏。项目wpj文件在romInit.o和romStart.o的BUILDRULE中引入了ROM_FLAGS_EXTRA。
• 把基于ROM的VxWorks映象的代码段(如果是VxWorks_romResident映象,则不拷贝代码段)和数据段从bootrom复制到RAM当中。
• 完成程序映象的解压缩(如果映象是压缩版本的)
• 跳转到usrEntry.c: usrEntry()
(3)usrEntry.c : usrEntry()
• usrEntry – entry point for _romCompress and _rom images.
rules.vxWorks中定义了partialImage.o的编译规则,当非DEFAULT_RULE(vxWorks)类型时,usrEntry.o将会安插到$(PRJ_OBJS_FOR_LD_PARTIAL)之前。而PRJ_OBJS_FOR_LD_PARTIAL=PRJ_OBJS=prjObjs.lst,其中第一个目标文件为sysAlib.o,这样usrEntry()取代sysInit()了成为vxWorks的入口。
——————————————————————————————————–
partialImage.o: $(LDDEPS) $(PRJ_OBJS) $(EXTRA_MODULES) $(COMPONENT_LIBS) \
$(patsubst -l%,lib%.a,$(LIBS)) $(CC_LIB) $(PRJ_OBJS_FILE)
– @ $(RM) $@
$(CC) $(CFLAGS) $(OPTION_OBJECT_ONLY) $(VERSION_C)
ifneq ($(findstring vxWorks_rom, $(DEFAULT_RULE)),)
$(CC) $(CFLAGS) $(OPTION_OBJECT_ONLY) $(USR_ENTRY_C)
$(LD_PARTIAL) usrEntry.o $(PRJ_OBJS_FOR_LD_PARTIAL) \
$(EXTRA_MODULES) version.o $(LD_PARTIAL_START_GROUP) \
$(COMPONENT_LIBS) $(LD_LINK_PATH) $(LIBS) \
$(LD_PARTIAL_END_GROUP) $(CC_LIB) -o $@
else
$(LD_PARTIAL) $(PRJ_OBJS_FOR_LD_PARTIAL) $(EXTRA_MODULES) \
version.o $(LD_PARTIAL_START_GROUP) $(COMPONENT_LIBS) \
$(LD_LINK_PATH) $(LIBS) $(LD_PARTIAL_END_GROUP) $(CC_LIB) -o $@
endif——————————————————————————————————–
• 控制权移交给 usrInit()
(4)usrConfig.c : usrInit()
执行操作系统内核所必须的初始化程序。
• Cache程序库的初始化。
• 清零系统的BSS段。
• 初始化中断向量表。
• 使硬件工作在一个“安静”的状态,尽量不产生各种中断或者异常。
• 控制权移交给KernelInit(),产生usrRoot根任务,最后会调用usrAppInit.c中的usrAppInit()进行用户级应用模块的初始化。
此时,调试超级终端会有如下打印信息:
/*VXWORKS Image GettingLoaded*/
Loading… 881680
Starting at 0x10000…
Attached TCP/IPinterface to cpm unit 0
Attaching networkinterface lo0… done.
VxWorks
Copyright 1984-1998 WindRiver Systems, Inc.
CPU : MPC860
vxWorks : 5.4
BSP version: 1.2/0
Creation date: Aug 22000
WDB : Ready
3 ROM-residentImage
.text代码段驻留在ROM/.Flash中运行,.data数据段和.bss段需要拷贝重定位到RAM中,且所使用的堆栈空间亦在RAM中。启动流程和vxWorks_rom*映像基本相同,稍微不同的是romStart()中直接跳转到usrInit,而不是usrEntry。至于为什么vxWorks_rom(Compress)需要usrEntry这个桩函数(Stub Routine),详情参考后文《VxWorks/MIPS运行期的gp重定位》。
五 使用Tornado编译VxWorks映象
Tornado2.2\target\config\all下的bootConfig.c/bootInit.c/usrConfig.c为通用的bootrom/vxworks代码模版。其中,bootConfig.c/bootInit.c是用来生成bootrom的,是通过工具栏里的Build->Build BootRom生成的,相当于命令行;usrConfig.c是用于生成vxworks的。
需要注意的是如果使用手动编译,则编译的是usrConfig.c文件;如果在Tornado开发环境下编译,则编译的是Tornado根据用户配置自动生成的prjConfig.c文件。
/* prjConfig.c – dynamicaly generatedconfiguration file */
工程下编译就要按照prjConfig.c里各个程序的封装顺序执行。
说明:
本文主要内容来自《VxWorks启动过程详解》,结合自己的理解重新整合组织,以方便日后查询。
参考1:
《VxWorks BSP for Intel’s IXDP425(ARM)》
《VxWorks BSP for Philips’ LPC2210(ARM)》
《VxWorks BSP for SAMSUNG’s S3C2440X(ARM)》
《VxWorks BSP for Motorola’s MPC8260(PowerPC)》
《VxWorks BSP for AMD’s AU1500(MIPS) and Motorola’s MPC8260(PowerPC)》
参考2:
《Tornado BSP Training Workshop》
《VxWorks 5.4 Programmer’s Guide》
《vxworks_bsp_developers_guide_6.0》
2 Overview of BSP
《VxWorks下设备驱动程序及BSP开发指南》
第12章 VxWorks映像及启动顺序
《VxWorks内核、设备驱动与BSP开发详解》
2.1.2 编译BootRom
4.3.1 编译规则
第23章 多样化的主板——板级支持包BSP
《嵌入式实时操作系统VxWorks及其开发环境Tornado》
10.4VxWorks的初始化
《VxWorks程序开发实践》
第9章 建立开发环境
参考3:
《VxWorks启动过程描述及主要宏开关含义》
《MVME162 FLASHRAMResident Bootable VxWorks System》
参考4:
《基于vxWorks的mpc8260 BSP启动过程实例分析》
《源码分析vxWorks6.x的ARM板BSP启动过程(上)》
《源码分析vxWorks6.x的ARM板BSP启动过程(下)》
参考5:
《对于编译vxWorks_rom中的生成vxWorks的疑问》
《VxWorks的bootrom 到u-boot的移植心得》
参考6:
《Windows深入剖析-内核篇》
第3章 Windows内核引导过程
《Microsoft Windows Internals, FourthEdition》
Chapter5. Startup and Shutdown