先声明一下,图片太大,显示效果不佳,可以拖拽到新窗口中看。
     在做好下载前的准备后,开始正式下载镜像。 DownloadImage 函数定义在文件 blcommon.c 中,下面是的源代码:
WinCE6.0 BootloaderMain源码分析之DownloadImage_第1张图片
WinCE6.0 BootloaderMain源码分析之DownloadImage_第2张图片
WinCE6.0 BootloaderMain源码分析之DownloadImage_第3张图片
WinCE6.0 BootloaderMain源码分析之DownloadImage_第4张图片
WinCE6.0 BootloaderMain源码分析之DownloadImage_第5张图片
WinCE6.0 BootloaderMain源码分析之DownloadImage_第6张图片
WinCE6.0 BootloaderMain源码分析之DownloadImage_第7张图片
 
三个参数都是输出参数,分别输出镜像的开始位置、镜像的大小以及启动镜像的地址。这里注意,镜像的开始处会有一些头信息,所以开始位置与启动镜像的地址是不一样的。
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\\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 p_w_picpath 中的署名并检查其有效性。 pCurDownloadFile->dwRegionStart 为被下载 p_w_picpath 的存储起始地址, g_dwROMOffset config.bib 文件中定义的 ROMOFFSET 的值, *pdwLaunchAddr 为被加载的地址,最后一个参数为 TURE 表示是被下载的 p_w_picpath 文件。
482 493 行,调用函数 OEMWriteFlash 将暂存在 RAM 内存缓冲区中的一个或者多个操作系统镜像区段的数据写入 Flash 存储设备。