1. TOC是什么
TOC:Table Of Contents, OEM on disk structure.
2. 为什么要进行全局变量重定位
Bootloader的主控制函数BootloaderMain()调用的第一个函数是KernelRelocate (pTOC),WINCE操作系统OAL模块的启动过程中调用的ARMInit()函数页调用了KernelRelocate (pTOC)。这两次对KernelRelocate函数的调用把全局变量重定位到RAM中,只不过它们操作的对象不同。OAL模块的ARMInit函数调用的KernelRelocate函数是对WINCE操作系统镜像的全局变量进行重定位,而BootloaderMain函数调用KernelRelocate函数是对Bootloader的全局变量重定位。
那么为什么要对bootloader及操作系统的全局变量进行重定位呢? bootloader及WINCE操作系统的源代码中,需要定义一些全局变量,当这两者的源代码分别编译链接成可执行的二进制文件后,这些全局变量被放在可执行文件的一个数据段中。此二进制代码被烧写到目标设备上,包括全局变量所在的数据段,而很多种情况下,bootloader是在只读的目标设备(比如nor flash)上运行,如果是这种情况,那么bootloader的代码要对全局变量进行写操作就会失败。因此,需要把bootloader的全局变量所在的数据段移到RAM中,来确保全局变量可写。而在操作系统启动过程中,需要把镜像的数据从目标设备(比如nand flash)读入到config.bib文件中定义的RAMIMAGE的区域内,这块区域是SDRAM
我们知道boot的方式一般有nandboot和nor boot这两种,其中nor flash支持XIP执行的方式,但因为nor flash价格相对nand flash高许多,所以现在很多系统采用的boot方式是nand boot方式,但不管使用哪种方式,保存着bootloader的镜像和WINCE镜像的nand flash或是norflash
3. 如何实现全局变量重定位
相关的代码如下:
// Gets replaced by RomLoader with realaddress
ROMHDR * volatile const pTOC = (ROMHDR*)-1;
/**********************************************/
可知它是一个指向ROMHDR结构体的指针。此指针描述了整个ROM的几乎所有信息。但是也可看到在变量声明时,此变量被赋予了一个非法值-1.那么这个变量到底是在什么时候、什么地方被初始化的呢?
此指针指向包含ROM信息的结构体,在编写代码时,代码本身不可能知道自己将会被烧写到什么样的ROM里。因此代码本身是不包含ROM信息的,那么唯一可以确定ROM信息的就是ROM的制作工具—RomImage.exe(MakeImage.exe调用RomImage.exe来打包所有的文件,RomImage.exe接受BIB文件作为参数,根据BIB(eboot.bib,config.bib,platform.bib)文件的内容打包,在该步中,MakeImage.exe会调用命令:romimage config.bib(这个文件是我的理解),从而根据config.bib的内容来把EBOOT打包为ROM文件(eboot.nb0)时初始化的。)。
RomImage.exe把文件打包为ROM文件时,读取一些特殊的全局变量或函数的符号表,然后对这些符号表进行一些操作。pTOC全局变量就是这些特殊的全局符号之一。RomImage.exe保证代码在ROM运行时,pTOC指针已经指向了正确的位置。
/************************************************/
void BootloaderMain (void)
{
……………………
// relocate globals to RAM
if (!KernelRelocate (pTOC))
{
// spin forever
HALT (BLERR_KERNELRELOCATE);
}
……………………
}
KernelRelocate的函数体如下所示:
KernelRelocate
//KernelRelocate: move global variables to RAM
static BOOL KernelRelocate (ROMHDR *const pTOC)
{
ULONG loop;
COPYentry *cptr;
if (pTOC == (ROMHDR*const) -1)
{
return (FALSE);// spin forever!
}
// This is where the datasections become valid... don't read globals until after //this
for (loop = 0; loop< pTOC->ulCopyEntries; loop++)
{
cptr = (COPYentry*)(pTOC->ulCopyOffset + loop*sizeof(COPYentry));
if(cptr->ulCopyLen)
memcpy((LPVOID)cptr->ulDest,(LPVOID)cptr->ulSource,cptr->ulCopyLen);
if(cptr->ulCopyLen != cptr->ulDestLen)
memset((LPVOID)(cptr->ulDest+cptr->ulCopyLen),0,
cptr->ulDestLen-cptr->ulCopyLen);
}
return (TRUE);
}
4. ROMHDR结构体
我们知道pTOC是一个指向ROMHDR结构体的指针,这个结构体在xxx:\WINCE500\PUBLIC\COMMON\OAK\INC的romldr.h下定义,内容如下
typedefstruct ROMHDR {
ULONG dllfirst; // firstDLL address
ULONG dlllast; // lastDLL address
ULONG physfirst; // first physical address
ULONG physlast; // highestphysical address
ULONG nummods; // numberof TOCentry's
ULONG ulRAMStart; // startof RAM
ULONG ulRAMFree; // startof RAM free space
ULONG ulRAMEnd; // end ofRAM
ULONG ulCopyEntries; // numberof copy section entries
ULONG ulCopyOffset; // offsetto copy section
ULONG ulProfileLen; // lengthof PROFentries RAM
ULONG ulProfileOffset; // offsetto PROFentries
ULONG numfiles; // numberof FILES
ULONG ulKernelFlags; //optional kernel flags from ROMFLAGS .bib config option
/************************************************************/
Percentageof RAM used for filesystem fromFSRAMPERCENT .bib config option
byte0 = #4K chunks/Mbyte of RAM for filesystem 0-2Mbytes 0-255
byte1 = #4K chunks/Mbyte of RAM for filesystem 2-4Mbytes 0-255
byte2 = #4K chunks/Mbyte of RAM for filesystem 4-6Mbytes 0-255
byte3 = #4K chunks/Mbyte of RAM for filesystem > 6Mbytes 0-255
/************************************************************/
ULONG ulFSRamPercent; //
ULONG ulDrivglobStart; // devicedriver global starting address
ULONG ulDrivglobLen; // devicedriver global length
USHORT usCPUType; // CPU(machine) Type
USHORT usMiscFlags; //Miscellaneous flags
PVOID pExtensions; // pointerto ROM Header extensions
ULONG ulTrackingStart; //tracking memory starting address
ULONG ulTrackingLen; //tracking memory ending address
}ROMHDR;
所用的结构体COPYentry的内容如下:
typedefstruct COPYentry {
ULONG ulSource; // copysource address
ULONG ulDest; // copydestination address
ULONG ulCopyLen; // copylength
ULONG ulDestLen; // copydestination length
// (zero fill toend if > ulCopyLen)
}COPYentry;
但Romimage.exe如何去初始化这些结构体的呢?首先我们通过下图的命令所得到的文本文件内容如下:
图1
所得到的文本文件的部分内容:
图2
打包工具Romimage.exe根据config.bib
图3
根据“NK 8C200000 01B00000 RAMIMAGE”,Romimage.exe在编译的时候初始化ROMHDR结构体的成员physfirst=0x8C200000,也就是图2中的“Physical First : 0x8C200000 ”,但是操作系统镜像的实际大小是图2的“length = 0x0158537C”,所以可以得出0x8C200000+0x0158537C=图2的“Physical Last : 0x8D78537C ”,这就初始化了成员physlast=0x8D758537C,其他的成员的初始化可以根据.bib文件来推导。
5. 深入认识TOC数据
WINCE操作系统nk.bin文件是最常用的记录格式类型的操作系统镜像文件(还有nk.nb0)。镜像数据nk.bin格式:最开始是7个字节的文件头,内容为42 30 30 30 46 46 0A,即“B000FF\x0A”,我用记事本打开时看到的内容如下:
图4
接下来的4字节是操作系统镜像数据的目的起始地址(Image Start),再4字节是以字节为单位的镜像数据长度(length),再接下来就是nk.bin的逐条记录(Record),也就是操作系统镜像的数据正文,其中每一条记录由4字节起始地址(Start)、4字节数据长度(Length)、4字节校验码(Chksum)和Length个字节的记录数据(RecordData)组成,见下图:
图5
接下来,我们通过图6可知pTOC=0x8d783764,它所指向的内存区域的操作系统镜像数据保存在Record[144]中,这条记录的长度为0x54(=84个字节),正好是ROMHDR类型的大小 (19*4+2*2+4=84)吻合
图6.