WinCE上BINFS实现详解
作者: wwfiney@ARMCE
网上不少介绍三星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来看看其他的问题
wwfiney@ARMC