先声明一下,图片太大,显示效果不佳,可以拖拽到新窗口中看。
在做好下载前的准备后,开始正式下载镜像。
DownloadImage
函数定义在文件
blcommon.c
中,下面是的源代码:
三个参数都是输出参数,分别输出镜像的开始位置、镜像的大小以及启动镜像的地址。这里注意,镜像的开始处会有一些头信息,所以开始位置与启动镜像的地址是不一样的。
217
到
222
行通过
OEMReadData
函数读取镜像的前面
7
个字节,这
7
个字节代表了镜像文件的格式。每一个镜像文件在文件数据的起始位置都有
7
个字节的特征码,与镜像文件的格式一一对应:
"N000FF\x0A"----- 该类型的镜像文件的数据是多区段的记录型镜像文件的区段信息
"X000FF\x0A"----- 该类型只在WinCE5.0及以前的版本中用于多区段的镜像文件
"B000FF\x0A"----- 最普通常用的记录型镜像文件格式,以.bin为文件名后缀
"S000FF\x0A"----- 带有数字签名的镜像文件,以.bin为文件名后缀
"R000FF\x0A"----- 带有数字签名的镜像文件,以.nk0为文件名后缀
无特征码 ----- 这时当做原始镜像文件进行处理
"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);
}
{
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
存储设备。