WinCE上BINFS实现详解
网上不少介绍三星24x0系列的BINFS启动方式实现,有些内容上描述的不是非常全面
下面就WinCE6上的BINFS实现,从基本原理到修改BSP,再到如何烧录启动做一个较全面的讲解
一 BINFS到底是什么?
其实BINFS就是MS给CE做的一种存放系统镜像的一个文件系统。
一说文件系统,大家可能比较头大。那么这么说,其实就是一个结构体(说是文件系统确实比较勉强,结构体可能更合适),里面有记录各模块的起始地址,大小等的信息,你要找模块可以根据这个信息到后面找。
二 为什么要用BINFS?有什么好处?
其实我觉得最大的好处是“按需加载系统模块到内存”,这里衍生出两个优点:
1 开机需要加载的核心部分很少,大大减少开机读取系统的时间
2 不需要为操作系统预留全尺寸的内存大小,只需预留最小开机核心的大小,剩下的作为通用内存空间,所以使系统内存使用有了很大灵活性,可用系统内存大大增加
三 BINFS的实现基础
这里不得不说WinCE的烧写和引导方式
在默认情况下,大家都知道WinCE会生成nk.bin和nk.nb0
bin其实就是nb0的压缩形式
nb0怎么可以压缩?
因为nb0中有很多空白区域
bin是如何压缩的?
很简单的描述下,就是用一个结构体记录一下,起始地址,大小,校验和,然后后面就是实际的内容
这样的一个一个结构体顺序排在一起就组成了bin这个简单的压缩格式,实际上只是压缩了nb0里面不必要的0,而不是对数据做了算法压缩,所以很好理解,所以你用winrar等去压缩bin,发现可以更小,呵呵
最简单最简单的烧写和启动WinCE的方法是:
直接把nk.bin烧到存储设备上,比如NAND上
假设nk.bin是30MB,那么就在NAND上顺序留下30MB给NK.bin
启动的时候把nk.bin按照那个结构体,解压到内存中,跳转到起始地址,就可以运行了
但是我发现有趣的是,很多开发板厂家并不是这么做的
他们实际上是用了BINFS来存放解压出来的系统镜像
这里大家可能比较疑惑一个问题,明明很多开发板都没有用“BINFS”,你怎么说他们用了BINFS?
实际上这里有一个重要的误会
BINFS是一个文件系统,但是大家关注BINFS是因为想实现上面说的加快开机和节省内存的好处,实际上需要完成这种好处,BINFS是一个文件系统基础,真正做到将系统镜像分为两个部分还需要用到multi-bin技术
所以这个就是误会所在,很多开发板确实用了BINFS文件系统,但是他们只是把系统镜像全部写入到BINFS分区,启动时再全部读出来,这样就没有达到前面说的“好处”
怎么确定我的系统现在有没有用BINFS?
看看自己的烧写代码,如果用了bootpart的库进行分区,写入,比如
BP_OpenPartition(NEXT_FREE_LOC, dwNumSectors, PART_BINFS, TRUE, PART_OPEN_ALWAYS);
这里就是典型的建立了一个BINFS分区
然后使用BP_WriteData将镜像内容写入到这个分区
如果你的程序没有使用这么一套机制,那么说明你的系统没有使用BINFS
前面说到除了BINFS作为文件系统的支持,还需要multi-bin技术,那么这个multi-bin技术是做什么的呢
实际上,我们想到,系统可以分为多块,我们只需要把核心启动的内容放到内存中,然后其它用到的从固态存储器中(使用了BINFS文件系统)读出来就可以了,这个就是我们前面说到的“好处”的实现原理
那么如何将系统镜像分开来,这个就是需要一些BSP里面的修改了,后面在实现方法中介绍
那么最简单的分配就是将系统镜像分为两块
一块用来存放系统启动时候需要在内存中运行的“最小内核”
一块用来存放系统后续需要“按需加载”的模块文件
我们一般也都是这么做的
编译的时候我们会通过配置文件告诉系统什么地址范围是在内存中运行的内核,什么地址范围是在存储器(使用BINFS存放镜像)上的系统其它模块,然后启动时候只需要加载最小内核,然后由最小内核使用BINFS文件系统驱动来读取存储器BINFS分区上的其它所需模块
系统怎么知道那些文件在什么地址?怎么知道哪些文件是在最小内核里还是存储器BINFS分区上的?
系统有记录各文件地址的结构体,有了文件地址就自然知道是放在什么地方的了,别忘了前面说的配置文件,里面已经告诉了各块的起止地址,很容易就可以判断到。关于结构体是如何记录模块地址等内容,不是本文讨论范围,这里就先放下,大家知道系统有方法知道文件的地址和大小就可以了。
所以为什么一些开发板系统用了BINFS,却没有那些“好处”,就是因为虽然用了BINFS文件系统,但是把全部内容都放进了最小内核,所以没有达到任何的优势效果。
就是说我们在实现了BINFS文件系统之后需要做的就是将“最小内核”和“系统其它文件”分离开来
四 BINFS的实现
前面已经较详细的说明了原理,其实说到这里文章题目已经需要有些改变了,我们实际上讨论的是“使用BINFS实现multi-bin来节省系统内存”,而不只是实现BINFS文件系统。因为我怕标题太长,大家有些费解,而且很多人把这个就称为BINFS,所以才取了这个题目。实际上将这种技术单单叫做BINFS是不准确的。
1 首先说说如何实现BINFS文件系统
前面说了,BINFS文件系统是基础,为什么?因为分离系统镜像之后,系统“最小内核”会使用BINFS去读系统其它模块,所以我们必须将系统镜像使用BINFS文件系统写入固态存取器(后面无特别说明,所有"存储器"均指“固态存储器”,内存指"SDRAM"或"DDR"等程序运行介质)
那么我们可不可以不用BINFS,用FAT?理论上应该是可以的,不过需要修改MS的系统加载模块的程序,我不知道是不是所有涉及到的模块都有开源,所以这个我们不讨论
其实MS对WinCE设备的存储器分区启动管理有点像PC的
在WinCE中首先在存储器一开始,存放的是MBR(master boot record,我没记错的话...),熟悉文件系统的朋友一看就明白了MBR是干什么的。MBR在CE中主要是记录后面的存储器空间是如何分区的,这个记录应该和PC上是一样的,起始sector号码,总共有几个Sectors,分区使用什么文件系统。但是不同的是,MBR不需要承担引导代码的功能
在MBR之后,按照分区记录中的起始地址,就是各个分区,大家不妨想象为PC上的分区,只不过我们现在C盘不用FAT或者NTFS,而是使用一种叫做BINFS的文件系统。
我们需要创建,修改MBR,增加删除,查询,读取分区信息和内容。这些功能都是bootpart库里面的,大家可以查询相关内容,这里不详细说明bootpart库是如何使用的。
只要了解过磁盘分区概念的朋友,应该很快就可以操作bootpart函数,而且一般的EBoot在烧写这里都是有例子的。大家可以参考,这里只是列出基本步骤
a) BP_LowLevelFormat 用来格式化所有存储空间
b) BP_OpenPartition 打开某个分区,如果不存在可以创建该分区,比如BINFS
c) BP_WriteData 往分区里写数据
d) BP_ReadData 读取分区里的数据
这些是一些基本的操作函数,具体实现细节大家需要多参考EBoot里面的代码,实际需要考虑的问题还可能包括整个存储器的布局
2 往BINFS分区中写入镜像
如果创建好了BINFS分区,那么下面要做的就是把镜像写入BINFS分区
如果大家下载的是BIN文件,首先需要把BIN文件解压缩出来,解压缩代码可以参考EBoot里面的代码
然后调用BP_WriteData可以写入BINFS分区
详细步骤大家还是多看示例代码,过程并不复杂
3 如何将系统镜像分成多个部分
这里就是说的multi-bin的,把镜像分为“最小内核”与“系统其它文件”
MS帮助文件里面有How to Implement BinFS ,这里将大致流程介绍了一下
首先就是需要修改config.bib文件,对CE比较了解的朋友知道,CE的地址等配置都是在config.bib里面修改的,所以我们要去改config.bib文件
这里可以看到现在系统对于镜像的划分,例如
Name Address Size Type
NK 88200000 03000000 RAMIMAGE
RAM 8B200000 04E00000 RAM
这个很容易理解,NK是唯一的一个bin文件,存放RAMIMAGE
这里面RAMIMAGE是关键字,告诉系统这个是需要启动的“最小内核”,现在是48MB...
RAM是告诉系统可用的内存起始地址和大小
首先我们需要把NK给分开成为两部分,即“最小内核”与“系统其它文件”
修改如下
- Name Address Size Type
- XIPKERNEL 88200000 00400000 RAMIMAGE
- CHAIN 88600000 00001000 RESERVED
- NK 88601000 02BFF000 NANDIMAGE
- RAM 88601000 079FF000 RAM
我们好好看下这个配置,这里完成的是将系统镜像内容分为两部分
XIPKERNEL,就是我们的“最小内核”,就是RAMIMAGE
NK,就是“系统其它文件”,就是NANDIMAGE(关键字,不可改)
好了,这里有些问题大家可能要问。CHAIN是什么?CHAIN是实现multi-bin的必须部分,是告诉系统各bin文件位置的信息。一定需要了解CHAIN的内容?
其实CHAIN也是结构体...,大家如果把chain.bin解压出来,会发现是这个结构体
- {
- bin个数
- bin信息描述结构体[bin个数]
- }
这里说的bin就是把系统镜像拆分开的子区块,我们这里就是2个,分别是XIPKERNEL和NK
信息描述会记录各个bin的起始位置,实际大小和最大大小,分区名称等
实际大小就是编译出来的实际大小,最大大小就是我们在config.bib里面指定的大小
还有一些其它的配置也需要修改
AUTOSIZE=ON
COMPRESSION=ON
DLLADDR_AUTOSIZE=ON
KERNELFIXUPS=ON
PROFILE=OFF
RAM_AUTOSIZE=OFF
ROMFLAGS=0
ROM_AUTOSIZE=OFF
ROMSIZE=03000000
ROMSTART=88000000
ROMWIDTH=32
XIPSCHAIN=88600000
这里你能订制的就是后面4项,我这里ROMSTART=88000000,因为前面还有一些地址配置因为与系统镜像生成无关,所以我就没有贴上来,不过为了防止大家搞不清这个88000000是怎么来的,还是贴一下
Name Address Size Type
ARGS 88000000 00001000 RESERVED
VPU 88001000 000FF000 RESERVED
FRAMEBUFFER 88100000 00100000 RESERVED
88000000就是我内存映射到的起始地址
这里需要注意XIPSCHAIN,这个一定要设置为CHAIN的起始地址
带了XIPSCHAIN,才会生成后面需要的xip.bin
MS的帮助文件里面的例子是用的NK作为RAMIMAGE的名字,EXT作为NANDIMAGE的名字
当然名字不是最重要的,但是实际使用中,大家会发现使用NK作为RAMIMAGE的名字亏大了
为什么呢?
这就是我们下面需要做的把各分区都配置好之后,下面需要决定把哪些内容放到什么分区下了
如前面所述,我们需要把启动需要的“最小内核”放到RAMIMAGE里面
首先定义一下哪些是“最小内核”
其实说白了就是系统可以使用BINFS读取在存储器上文件所需要的最小系统文件
这里其实需要了解WinCE的启动过程,但是我们并不深入到函数,只是模块级的系统启动后需要初始化,所以关于初始化的OAL层都需要放进来,然后我们需要使用device.exe来加载我们的存储器驱动,这样才能访问存储器,所以关于设备驱动加载,总线枚举的模块都要加上,还有我们需要的BINFS驱动模块,还有
mspart分区模块,不然无法找到BINFS分区的位置。这些都是较通用的模块,还有一些模块你一定要注意,就是你的存储器驱动所依赖的驱动模块,比如ceddk?你的bsp里的gpio驱动?dma驱动?等等。如果没有加入全的话就会出现加载不了存储器驱动,也就没法找到分区,没法访问BINFS...
有人说如果需要用HIVE的话,还要加载FAT驱动,实际上并不需要,因为我们的目的是要完成“可以访问BINFS”,
boot.hv一定是要放到RAMIMAGE里的,所以基础注册表都是有的,可以让你完成对存储器驱动的加载,而这之后都可以用BINFS访问“系统其它文件”了,所以FAT驱动是不需要放到RAMIMAGE里面的,在用到FAT时,BINFS已经好用了
同时default.hv,user.hv也都是不需要放到RAMIMAGE里面的
如果你使用非英文的系统,还有一个文件不要忘记
wince.nls,少了这个你也会启动不了
下面给出例子,我相信可以涵盖大部分平台应用了
MODULES
nk.exe $(_FLATRELEASEDIR)\oal.exe XIPKERNEL SHZ
kernel.dll $(_FLATRELEASEDIR)\kern.dll XIPKERNEL SHZ
coredll.dll $(_FLATRELEASEDIR)\coredll.dll XIPKERNEL SH
k.coredll.dll $(_FLATRELEASEDIR)\k.coredll.dll XIPKERNEL SHMK
oalioctl.dll $(_FLATRELEASEDIR)\oalioctl.dll XIPKERNEL SHK
filesys.dll $(_FLATRELEASEDIR)\filesys.dll XIPKERNEL SHK
fsdmgr.dll $(_FLATRELEASEDIR)\fsdmgr.dll XIPKERNEL SHMK
mspart.dll $(_FLATRELEASEDIR)\mspart.dll XIPKERNEL SHK
romfsd.dll $(_FLATRELEASEDIR)\romfsd.dll XIPKERNEL SHK
binfs.dll $(_FLATRELEASEDIR)\binfs.dll XIPKERNEL SHK
fpcrt.dll $(_FLATRELEASEDIR)\fpcrt.dll XIPKERNEL SH
k.fpcrt.dll $(_FLATRELEASEDIR)\fpcrt.dll XIPKERNEL SHMK
ceddk.dll $(_FLATRELEASEDIR)\ceddk.dll XIPKERNEL SHQ
device.dll $(_FLATRELEASEDIR)\device.dll XIPKERNEL SHMK
udevice.exe $(_FLATRELEASEDIR)\udevice.exe XIPKERNEL SHM
devmgr.dll $(_FLATRELEASEDIR)\devmgr.dll XIPKERNEL SHMK
regenum.dll $(_FLATRELEASEDIR)\regenum.dll XIPKERNEL SHK
busenum.dll $(_FLATRELEASEDIR)\busenum.dll XIPKERNEL SHK
pm.dll $(_FLATRELEASEDIR)\pm.dll XIPKERNEL SHMK
nandfmd.dll $(_FLATRELEASEDIR)\nandfmd.dll XIPKERNEL SHK
FILES
boot.hv $(_FLATRELEASEDIR)\boot.hv XIPKERNEL SH
wince.nls $(_FLATRELEASEDIR)\wince.nls XIPKERNEL SHU
这里的nandfmd.dll是我的NAND驱动,可以改成你自己的
可能有一些还是可以优化掉的,但是我还没有尝试,大家可以给出意见
上面这段代码放到哪里?
放到config.bib最后!没错,是config.bib,不是platform.bib
这里再回头来说说使用NK当作RAMIMAGE名称的缺点
大家看到,我们只要把需要的模块改成XIPKERNEL就好了,但是系统默认的都是用的NK这个名字,如果我们使用NK当作RAMIMAGE的名字的话,我们要把其他所有不要加进来的改名字...这个工作量太大了,所以我们把NK当作了NANDIMAGE的名字
4 配置注册表
到了这一步还没有完成,我们需要配置些注册表
其实我默认的注册表还真没有什么需要改的
大家看看MS的帮助文件里面关于注册表的配置,这里列出一些设置
[HKEY_LOCAL_MACHINE\System\StorageManager\PartitionTable]
"21"="BINFS"
[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\FlashDisk\BINFS]
"MountHidden"=dword:1
"MountAsROM"=dword:1
这里不得不说一下RAM and ROM file system和ROM-Only file system
很多帖子都说用BINFS一定要用RAM and ROM file system,实际上并不是这样
使用ROM-Only file system一样可以,但是大家需要记住,在使用ROM-Only file system时,把FAT mount为根目录
五 BINFS的烧写和启动
上面把BSP那里该修改的都修改了,只要编译就可以了,关于如何编译系统,其实是一个非常非常重要的问题,一定要重视,参考http://www.armce.com/bbs/thread-804-1-1.html
不正确的编译,会影响你本来应该正确的结果!
下面又是一个问题,现在生成了4个可以用的bin,分别是
XIKERNEL.bin,nk.bin,chain.bin,xip.bin
那么到底该怎么烧写呢?
实际上大家从大小就可以看出XIP包含了所有其他bin的内容,所以大家直接把XIP.bin下载解压,然后用bootpart的函数烧写到存储器上就可以了
启动的时候怎么引导?和原来启动系统一样,但是记住,不需要把全部的xip.bin里面指定的大小都读取出来了,最多只要读你在config.bib里面指定的RAMIMAGE大小就可以了
RAMIMAGE大小怎么确定的?这个你可以先放的比较大,比如10MB,然后看编译出来的RAMIMAGE分区有多大,再调整,一般都是3MB以内吧,我这里保守的用4MB
六 调试技巧
调试BINFS的multi-bin启动,需要反复的烧写,设置有一点不正确经常就是卡在某个地方,对于我们用中文镜像的就更痛苦了,release版都有30-40MB,debug版不改存储布局,烧都烧不进去
我建议大家新建一个项目,然后选择thin client,不妨将显示驱动也加入,其他驱动都不要,然后一定选择debug版
本,debug版本的调试信息非常重要
花这点时间去编译一个thin client是非常值得的,调试速度很快
下面给一些调试建议
1 引导之后就死机,什么消息都没有
估计是内存配置有问题,或者烧写的有问题,或者没有把需要的内核模块加进去
2 死在OEMInit之后
后面应该开始加载NAND驱动了,看看你的关于设备管理,驱动加载和总线枚举的模块有没有加全
如果已经有NAND驱动的信息出来了,看看NAND驱动是不是因为什么原因没有加载起来,比如缺少某个dll支持
3 其他
如果NAND正常了,BINFS没问题了,大家需要根据自己的BSP来看看其他的问题