天天看点

WinCE6.0 BootloaderMain源码分析之DownloadImage

   先声明一下,图片太大,显示效果不佳,可以拖拽到新窗口中看。

     在做好下载前的准备后,开始正式下载镜像。DownloadImage函数定义在文件blcommon.c中,下面是的源代码:

三个参数都是输出参数,分别输出镜像的开始位置、镜像的大小以及启动镜像的地址。这里注意,镜像的开始处会有一些头信息,所以开始位置与启动镜像的地址是不一样的。

217到222行通过OEMReadData函数读取镜像的前面7个字节,这7个字节代表了镜像文件的格式。每一个镜像文件在文件数据的起始位置都有7个字节的特征码,与镜像文件的格式一一对应:

"N000FF\x0A"----- 该类型的镜像文件的数据是多区段的记录型镜像文件的区段信息 

"X000FF\x0A"----- 该类型只在WinCE5.0及以前的版本中用于多区段的镜像文件 

"B000FF\x0A"----- 最普通常用的记录型镜像文件格式,以.bin为文件名后缀 

"S000FF\x0A"----- 带有数字签名的镜像文件,以.bin为文件名后缀 

"R000FF\x0A"----- 带有数字签名的镜像文件,以.nk0为文件名后缀 

无特征码        ----- 这时当做原始镜像文件进行处理

OEMReadData函数在上面的源代码中使用了很多次,由OEM用户自己实现,其定义在文件main.c中,根据下载镜像的方式不同(以太网或USB),调用不同的接口读取镜像数据,实现的源码如下:

BOOL OEMReadData(DWORD dwData, PUCHAR pData) 

        BOOL ret; 

         OALMSG(OAL_FUNC, (TEXT("+OEMReadData.\r\n"))); 

        //OALMSG(TRUE, (TEXT("\r\nINFO: dwData = 0x%x, pData = 0x%x \r\n"), dwData, pData)); 

        if ( g_bUSBDownload == FALSE ) 

        { 

                ret = EbootEtherReadData(dwData, pData); 

        } 

        else if ( g_bUSBDownload == TRUE ) // jylee 

                ret = UbootReadData(dwData, pData); 

        }         

        return(ret); 

}

227到269行表示使用的是多区段的记录型镜像文件格式,多区段的镜像起始就是操作系统或者BootLoader的运行时二进制数据分散在不连续的物理存储区间。多区段意味着多文件,即需要下载多个文件,所以在267行中用nNumDownLoadFiles来记录下载的文件数量。该类型的镜像文件并不是真正的WinCE操作系统或者BootLoader的二进制运行时数据,只是供下载多区段型镜像所使用的头信息,称为Manifest前导数据。当操作系统的镜像只有一个区段,或者是使用自身包含有存储位置头信息的记录型镜像文件格式时,就可以不使用Manifest前导数据。这点要注意,否则下面的一些代码就看不明白了。该类型的镜像文件使用专门的DownloadManifest型的全局变量g_DownloadManifest来存放。下面看看这种类型的镜像文件的数据结构。紧随7字节的格式类型特征码后是4字节的校验码,然后是4字节的区段个数。接下来便是存放在g_DownloadManifest.Region数组各个元素中的数据。RegionInfo结构体有3个成员dwRegionStart、dwRegionLength、szFileName,分别为镜像区段在物理存储的起始地址、以字节为单位的区段长度和与镜像区段对应的镜像文件的文件名。

231行开始首先读取4字节的校验码,然后读取区段数量,存储到g_DownloadManifest.dwNumRegions中,然后把该镜像区段的数据都读取到g_DownloadManifest结构体中,最后调用VerifyChecksum函数对数据执行校验。

263行的OEMMultiBINNotify函数是当BootLoader要下载多区段的操作系统镜像时调用来向用户发出通知。

272到278行表示的是WinCE5.0及之前版本支持的多区段的镜像文件,由于分析的平台是以WinCE6.0为操作系统的,所以这里直接返回FALSE。

281到292行表示的是最常用最普通的记录型镜像文件格式,下面对这种.bin的镜像文件的内部结构进行描述。最初的7字节特征码之后是镜像文件在目标系统中的目的地的物理存储起始位置和以字节为单位的长度,各占4字节,对应RegionInfo结构体的dwRegionStart和dwRegionLength两个成员。再接下来便是具体存放操作系统二进制数据的拥有相同结构的多条Record记录,每条Record包括4字节的内存起始地址dwRecAddr、4字节的记录长度dwRecLen、4字节的校验码dwRecChk和dwRecLen个字节的记录数据组成。校验无误以后将去除头信息的记录数据存放到dwRecAddr指定地址的物理存储位置。如果记录的目的存储位置是Flash存储设备,则要先缓存到RAM内存中,待整个镜像文件下载完以后再一起写入Flash。在源代码中,281到292行只读出了dwRegionStart和dwRegionLength。

305到317行是当仅仅下载了单个的.bin文件时,没有填充g_DownloadManifest结构体,无法通知用户,所以此时要人工手动填充DownloadManifest结构体,只是没有镜像区段对应的文件名。

324到329行调用函数EMVerifyMemory检测当前下载的镜像对应的虚拟内存地址区域是否映射到了用户可以使用的物理存储区域。

332到338行表示当下载的镜像的目的存储地址是Flash时,擦除Flash。函数OEMIsFlashAddr和OEMStartEraseFlash的定义都在文件\WINCE600\PLATFORM\<BSP name>\SRC\BOOTLOADER\EBOOT\flash.h中。OEMIsFlashAddr用来判断一个给定的地址值是否落在系统的Flash存储设备范围内。OEMStartEraseFlash负责初始化并启动Flash存储器的擦除进程。本平台的源码中,这两个函数都是空的,没有任何操作。

342到356行是当g_bBINDownload为FALSE时进行的处理,即下载的镜像是最普通常用的原始型镜像文件,以.nb0为文件后缀的二进制文件。这里需要解释的是为何要调用OEMMapMemAddr函数之后才开始读取镜像文件?由于Flash操作速度比RAM慢,在片擦除的时候可能会使读写操作停滞,这样在每次下载操作系统镜像文件的时候可能是下载停滞。调用OEMMapMemAddr将Flash地址映射到RAM地址中,这样向Flash写数据实际上先写到RAM中,然后再写到Flash中,就不会感觉到下载停滞了。

      358到416行是用来下载.bin文件,按照一定的顺序依次下载镜像的所有记录。结合该类型镜像文件格式,相信大家很容易明白。同样这里也用到了OEMMapMemAddr函数进行Flash地址映射来防止下载停滞现象。391行到406行是用来寻找操作系统镜像文件中的全局变量数据信息TOC的,首先比较数据长度dwRecLen与sizeof(ROMHDR)的大小,以此来判断该条记录是否为TOC记录,dwTempOffset取得的是RAM内存的偏移地址,398到401行的if语句是进一步通过TOC的成员判断该条记录确实是TOC记录的。ROM_SIGNATURE_OFFSET和ROM_SIGNATURE两个宏定义都在头文件\WINCE600\PUBLIC\COMMOM\OAK\INC\romldr.c中。 

430到454行根据下载的bin文件是否包含有TOC和内核分别对三个输出参数进行设置。433行是判断bin文件是够有TOC数据,如果有调用IsKernelRegion函数判断文件中是否有内核(通过查找是否有nk.exe的文件名),如果有则返回NK.bin的起始地址,长度和跳转地址。447行表示bin镜像只含有一个区段,同样返回镜像的起始地址、大小和启动地址。这里需要注意的是启动地址赋值为dwRecLen,因为通常bin文件的最后一条记录是地址dwRecAddr和校验和dwRecChk都为0,而dwRecLen标示的其实是实际的入口点。

455到460行是当镜像文件为原始的二进制文件时,对三个输出参数进行赋值。

463到477行表示如果镜像文件的目的存储地址是Flash,则调用OEMFinishEraseFlash函数结束Flash存储器的擦除进程,而且即将进入写镜像文件到Flash的处理阶段。在写镜像到Flash之前,调用g_pOEMCheckSignature对镜像进行校验。该函数用于验证WinCE image中的署名并检查其有效性。pCurDownloadFile->dwRegionStart为被下载image的存储起始地址,g_dwROMOffset为config.bib文件中定义的ROMOFFSET的值,*pdwLaunchAddr为被加载的地址,最后一个参数为TURE表示是被下载的image文件。

482到493行,调用函数OEMWriteFlash将暂存在RAM内存缓冲区中的一个或者多个操作系统镜像区段的数据写入Flash存储设备。

本文转自jazka 51CTO博客,原文链接:http://blog.51cto.com/jazka/605776,如需转载请自行联系原作者