2008年7月30日星期三

Sharing Hardware Registers: When to replace multiple writers with a shared resource driver.

Sharing Hardware Registers: When to replace multiple writers with a shared resource driver.
Posted by Jeremy Cooke
翻译自
http://blogs.msdn.com/ce_base/archive/2007/03/29/sharing-hardware-registers-when-to-replace-multiple-writers-with-a-shared-resource-driver.aspx

Hi, I am cpuwolf。这篇来自微软的建议,写的是关于多个驱动共享同一个硬件的问题。这里给出了很好的建议。我尝试用尽可能贴切的专业词语翻译,有什么不对的地方,请指出。
当设计复杂的系统时,偶尔会必需协调对某些的共享资源的访问。一般这些同步问题都由操作系统所提供的例如critical section或mutex等来解决 。这些技术在它是一个简单的共享资源,并且较少的对它的访问时,是无可挑剔的。但对于复杂的资源访问多个threads ,进程,驱动程序,或多个应用程序,新的规范可能更为合适。一个很好的共享资源的驱动程序的例子,就是GPIO驱动程序。每个驱动可能共用一个GPIO模块的寄存器,或者共同读-修改-写,每个驱动如果不同步,那么就会产生问题,
比起直接访问共享资源,把这些访问操作抽象到一个独立的驱动程序去做,可能会更好。所有会访问共享资源的家伙,可以采用尽量少的open这个驱动或者较少使用它的接口。同时,这个驱动程序在内部去处理将同步问题和处理任何可能出现的错误。调用者可以不用再担心同步问题,因为所有这些麻烦都已经交给了别的驱动。
通过使用这一技术,程序员,往往能够避免使用critical section,否则这样的结构会充斥整个原本的代码。现在每一次对共享资源的访问,现在只需通过一个用户设计的API,这些API抽象了访问动作,并且增加程序的可读性。
共享资源的驱动程序也应该预处理和检查错误的输入,以及很好的处理共享资源的错误。由于错误码现在是集中处理的,所有程序员大可认为共享资源已经被很好的保护。这是team开发时的很重要的一点。
例子学习
这个嵌入式系统具有多通道ADC转换器,并且每个通道都有相应的设备使用。多个不相干的驱动程序都试图访问各自的通道。软件可以将hardware所有通道的转换结果放在一块内存中。如果多个线程请求ADC转换,同时没有适当的同步 那么这里就潜藏着一个污染到另一个的情况。
在这特别的系统里有和ADC转换相关的battery驱动程序, USB驱动程序,以及系统的背光驱动程序。battery驱动程序是要关心当前的电压、电池的温度、以及充电电压和充电电流。 USB驱动程序需要监控USB vbus的电压,而背光驱动器需监测环境光线与光电二极管。由于这些功能是在自己的驱动中处理,所有共享资源的访问,都必须以一个更先进的方式控制。以下图1描绘了每个驱动程序都要存取硬件的ADC 。
模拟向数字转换器,将被抽象,并使用一个专门的驱动程序。驱动程序允许多个writer使用所有通道的转换功能,同时返回结果放在一个用户指定的内存中(见图2下文) 。
这个驱动暴露出一个函数,其中包括A / D转换功能并返回结果。如下:
enum ADChannels
{
AD_CHANNEL_BAT_VOLTAGE = 0,

AD_CHANNEL_BAT_CHG_VOLTAGE,
AD_CHANNEL_BAT_CHG_CURRENT,

AD_CHANNEL_BAT_TEMP,
AD_CHANNEL_USB_VBUS,
AD_CHANNEL_PHOTODIODE,
TOTAL_AD_CHANNELS

};


BOOL PerformADConversion(USHORT *pOutBuffer, DWORD OutSize);
在这里pOutBuffer 指向用户的buffer,在函数返回的大小必须等于TOTAL_AD_CHANNELS。必须注意,当向用户数据区拷贝数据时,建议使用类似CeSafeCopyMemory 的函数来处理,它可以处理非法内存的问题。
该PerformADConversion 函数可以实例化一个critical section同步使用,可以看出在图3 :

在这个设计中,所有硬件访问都被保护在一个集中的代码区,只使用仅仅一个critical section。系统性能得以提升,因为使用了轻量级的critical section,而是一个缓慢的同步对象(如mutex) 。程序员现在可以进一步优化系统性能,每个驱动程序可以同时进行。
虽然使用一个专门的驱动程序来完成这些事情有很多的好处,但是,事实上,这种做法可能会稍微有点慢。因为通过标准的Windows CE的函数访问驱动程序是有一些开销。因此,最好的方法是建立一种结构是驱动的访问开销最小。但是无论怎样,使用这种方法的收益是远远大于这里的开销的。

挖掘WinCE 5系统调用过程

int shellcode[] =
{
0xE59F0014, // ldr r0, [pc, #20]
0xE59F4014, // ldr r4, [pc, #20]
0xE3A01000, // mov r1, #0
0xE3A02000, // mov r2, #0
0xE3A03000, // mov r3, #0
0xE1A0E00F, // mov lr, pc
0xE1A0F004, // mov pc, r4
0x0101003C, // IOCTL_HAL_REBOOT
0xF000FE74, // trap address of KernelIoControl
};
这是黑客常用的缓冲攻击代码形式,这里的32为常数,都是ARM机器指令。
我就要想你展示的不是如何攻击WinCE内核,而是看看WinCE的系统调用是如何实现的。WinCE一个常用的API,KernelIoControl,它的实现体实在内核NK.exe,而function caller只是一个trusted的user级别的application。
你可以想象,这样的API的实现,必然要经历CPU从user processer mode切换到supervisor processer mode。
BOOL KernelIoControl(
DWORD dwIoControlCode,
LPVOID lpInBuf,
DWORD nInBufSize,
LPVOID lpOutBuf,
DWORD nOutBufSize,
LPDWORD lpBytesReturned
);
KernelIoControl超过了4个参数,那么在参数传递时,超过的部分使用堆栈完成的。IOCTL_HAL_REBOOT的参数都没什么用,所以这个我们忽略,传0就可以了,因此r1,r2,r3赋值0.
IOCTL_HAL_REBOOT的数值必须放在寄存器r0。
上面的代码中r4的内容会变为0xF000FE74,代码最后就是跳转到0xF000FE74,你可以看到这个地址很大,在整个内存空间的高地址。同时这个地址是经过编码得到,公式是:
0xf0010000-(256*apiset+apinr)*4
对于KernelIoControl,apiset是0,apinr是99。0xF000FE74是这样得到的。
当代码跳转的这样一个高地址时,会引发prefetch abort,这样exception会被内核 ,也就是NK.exe抓到。然后再对这个出错地址进行解析,就可以知道应用程序想访问那个system cal。
综上,WinCE并不是靠SWI这样的软中断实现system call,而是prefetch abort。

2008年7月24日星期四

WinCE5 kernel高地址都放了什么东西


typedef struct ARM_HIGH {
ulong firstPT[4096]; // 0xFFFD0000: 1st level page table
char reserved2[0x20000-0x4000];

char exVectors[0x400]; // 0xFFFF0000: exception vectors
char reserved3[0x2400-0x400];

char intrStack[0x400]; // 0xFFFF2400: interrupt stack
char reserved4[0x4900-0x2800];

char abortStack[0x700]; // 0xFFFF4900: abort stack
char reserved5[0x6800-0x5000];

char fiqStack[0x100]; // 0xFFFF6800: FIQ stack
char reserved6[0xC000-0x6900];

char kStack[0x800]; // 0xFFFFC000: kernel stack
struct KDataStruct kdata; // 0xFFFFC800: kernel data page
} ARM_HIGH;

Post-Processing flash.dio


windows mobile最终生成image通常叫做flash.bin。请记住其实是flash.dio,这个文件是一个真正的数据镜像,flash.bin是flash.dio在后期处理,也就是post-processing阶段生产的。
Flash.bin中会包含NAND flash每个sector info的附加信息,并且以block的大小分段。其实也就是把flash.dio中的原始数据加以分段,打包。

值得注意的是微软上面提到:
In addition, NandPostProc.exe also truncates TFAT by 4 blocks, and then adds two compaction blocks into IMGFS and into TFAT. These compaction blocks provide wear–leveling, and help lengthen the life of the flash hardware.

Flash.bin中imgfs分区和tfat分区会被加上gap,反正我是没理解微软的用意。他这样一改,会导致开头的MBR中的分区表信息和这里对不上,因为两个分区后移了。除非这4块区域被标记为未使用,那么查找分区时会自然向后查找,否则就会出错。下面是我用KITL抓到的错误:
Loaded 'cecompr.dll', no matching symbolic information found.
2928 PID:a7a9f512 TID:e7a7a85e ERROR: IMGFS!CVolume::LoadCompressionEngine: unable to load decompressor type "yyyyyyyyyyyyyyyy" from dll "CECOMPR.DLL"... expect failures!!!
Unloaded symbols for 'cecompr.dll'

这样的错误,我花了1个多月,才发现就是上面4 blocks导致。最后我临时换了方案,改烧flash.dio就OK。
所以如果你在porting platform时,遇到这样的问题,先参考一下我的经历。

2007年11月28日星期三

DRAM precharge power down mode

我们知道 DRAM中的记忆体,或者说电容,打开新行的操作就是precharge(预充电)。预充电可以通过命令控制,也可以通过辅助设定让芯片在每次读写操作之后自动进行预充电。而从预充电到真正发送行有效命令需要一些时间

DRAM中prechar

2007年10月25日星期四

UART hardware flow control

串口,RS232UART这些概念总是让人头晕。如果你也有同感,那么就对了。因为串行通讯这一块从来就没有统一过,连接的方式和协议更是百家争鸣。

不过现在用于嵌入式领域的UART通讯基本是统一的。一般只用rx,tx,cts,rts这几个信号。下图是一个典型的SOC连接蓝牙芯片的连接。其中包含了发送和接受的流控制机制。


2007年7月29日星期日

操作系统内核的中神奇的代码

最近free download一个Linux kernel 2.6.22。由于工作的原因,大概有2年没有碰触Linux了,这些天又有生活无目标的感觉。花了2年时间才大概弄清楚WinCE整体的结构,基本能够理解微软的设计思想,也尝试看WinCE5的内核代码,但是有时候会很不爽,一个函数跟着跟着就丢失了。尤其是GWES,我最想知道的部分,完全没有代码。内核代码很多类似的部分,其中几乎都是C语言构成,C语言的组织基本架构就是函数,我们平时写应用程序时,也经常用函数,可是内核中的函数有时充满了玄机,有的是不归路,有的切换,有的是交错运行,对了,此时让我突然想起硬件文境切换代码,有空大家一定要看看绝对长见识。不过今天不看那些东西,我随便读,代码到哪里就说到哪里。看内核启动时,初始化终端的函数

void __init console_init(void)

{

initcall_t *call;

/* Setup the default TTY line discipline. */

(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);

/*

* set up the console device so that later boot sequences can

* inform about problems etc..

*/

call = __con_initcall_start;

while (call <>

(*call)();

call++;

}

}

__con_initcall_start这个变量你就是找遍所有C和汇编代码你也找不到它的原型。它是出现在内核编译的链接阶段,是一个编程语言中的标记,或者说是一个地址,在这里也确实表现为地址,__con_initcall_start__con_initcall_end一起前后界定了了代码段中.con_initcall.init

#define console_initcall(fn) \

static initcall_t __initcall_##fn \

__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn

搞这个macro声明了很多已赋值的函数指针,如32CPU那么就占用4个字节,console_init就是循环运行这些函数指针多对应的函数。这个架构真是太好了,很多驱动程序只要用这个macro就可以声明自己,而内核初始化部分就无须直接关心有多少console驱动程序做这样的声明,靠着强大的编译器就可以帮助你了解到。还是GCCLD厉害,微软的编译器功能太少,灵活度不高(至少微软对外放出的编译器是这样,说不定他们内部有很高级的)。不过毕竟微软不希望你利用他们的操作系统再去开发别的系统。呵呵。

所以分析内核代码是一定要想象这段代码的运行环境,否则你很难理解作者到底要干什么。文境切换代码更是如此,经过schedule函数直接就到另外一个进程去了,很神奇。还有在SMP环境下,一个函数直接会分叉,编程同时在多个CPU运行。今天就看了这点东西,以后再遇到什么好玩的东西,再和大家分享。

2007年7月28日星期六

程序员你有资格骄傲么?

我遇到过太多太多自以为是的例子,有时候包括自己。

超级解霸之父梁肇新在他书中提到程序员进阶的几个阶段,我非常同意这位大师的观点。

其中有个阶段就是学习驱动程序和理解操作系统的内核,从1990年一直有以研究linux内核所引发的研究操作系统内核的一股风潮。Ring0Ring3,虚拟内存,系统调用,进程同步,缺页等等,有一些听起来很炫的专业词汇,更有甚者,以为自己能说出这些词,自己就是高手了。

Ring0/3,如果你不懂Intel IA-32体系架构,那么请你谦虚一点。

虚拟内存,page poolpage fileswap partition,如果你没有仔细研究过当代CPU中的MMU的一些共性,当虚拟页面不在时,CPU如何响应,CPU特权级如何切换。这些都是硬件知识。虚拟内存的管理涉及ring0内核的管理,和ring3普通进程管理,他们是关系?这些你都不懂,那么请你谦虚一点。

系统调用,Intel CPUtrap 0x80是实现linux系统调用的入口,然后ring0内核再进行一次派发,最后到达实现体。windows也是类似,他们运行在同一个CPU上,不可能有太大区别。当然这里有安全机制,不可能让你这么容易就进入ring0胡作非为。如果这些你都不懂,那么你就很难想象windows如此多的DLL是干什么的,他们之间的堆叠关系,Windows丰富的API都做了些什么,在哪个ring运行,更很难想象,很多API会引发一场轰轰烈烈的ring3ring0的切换。ntDll.dll这个连接ring0ring3的使者在干什么。Glibclinuxz这个系统中的地位。如果你不懂,那么请你谦虚一点。

进程同步,进程调度算法,这个是现在的大学最喜欢教学生的部分,还会出很多颠过来倒过去的题让学生作答。这些东西没他们宣扬的那么重要。Tanenbaum教授在<>中就是这样说,“而实际系统中很重要的部分,如IO系统和文件系统又以为缺乏理论性而被忽略”。在1969年贝尔实验室第一个PDP-11系统,后来被认为是unix操作系统第一版中,当时已经可以多进程同时运行,但是multiprogramming就不可以,就是因为还没有设计出完善的文件系统,这样的问题直到1972UNIX系统被C重写时,问题才被真正解决。

缺页,现代物理内存有限,只好用虚拟内存,没进程2GB,或者4GB空间,说起来很容易,我只想问你理解么?为什么WINCE5.0进程是32MB6.02GB?微软会做什么调整?你能想象一下么?如果你是微软,那么你怎么做这个调整?createprocess底层实现中,进程空间内存分配原则就要改掉。.exe有两个模型,一个是在文件系统中的模型,一个是在进程空间的模型,他们往往会有差异,微软建立在COFF文件结构基础上的的PE文件结构是如何来说明她们的不同。Process loader在操作系统中的地位。有次引发的page on demand策略的产生。多任务不一定要实现缺页,只要进程足够小。但是多任务,而且每进程空间都是CPU所能承受的最大空间,这就必须有缺页机制。如果你都不懂,那么请你谦虚一点。

一个操作系统是在另一个操作系统里面编译完成的,你信么?还有很多很多的知识,你懂么?如果你说你都懂,好,那么我告诉你,你也没什么了不起。哦你说你可以用C+汇编实现上面东西,那么我告诉你,你同样没什么值得骄傲的,这个世界上很多人都能写自己的内核,注意我说的是“内核”,仅仅指“内核”。你能写只能说明你的编程功底很深,你能预见这些技术难题。补充一下,这些“技术难题”在现代根本不是难题,但确实是几十年前的技术难题。说明你把“古人”的解决这些问题的方法掌握了,计算机对你已经很通透。但是程序员的最高境界你仍未达到。内核只是整个操作系统中的一部分,是核心部分,注意不要被“核心”二字迷惑,它只是指地理位置的核心,并不是指重要性的核心。你可知道微软的office套件比他的XP professional贵很多。相信不用我说你也可以理解。说明微软在OFFICE上所花的心血绝对不亚于一个操作系统。从这些“高手”认为,内核最牛,可是你看office这样应用程序会比你的内核复杂,你信么?有幸您写一个能和微软这么强大的OFFICE出来,在下一定跪拜师学艺。就不说office,就说XP操作系统,内核是什么,ntoskrnl.exe一个2MB的文件,好了不用我多说,你也能很清楚掂量内核的地位。内核以外,操作系统要提供友好的界面,丰富的API,安全机制,大量可用的应用程序等等这些才是大头。一个不懂电脑的人,你和他说操作系统,他不会懂。你说对他重要么?不重要。“能用,好用”这才是重要的,你那些后台努力的骄傲就收起来吧。享受一下用户说这个东西好的感觉。

程序员最高境界不会再关心这些,什么语言?什么系统?什么内核?什么驱动?什么应用?那是一种对大局的把握。对一个庞大软件的设计能力。不过没有前一阶段的积累,你也决不可能到达这个阶段。因为假如让你来设计这个庞大系统,你会有很多自己的技术问题还没有解决,你根本不敢乱想。很多人或者公司也做大系统,为企业定制。那也许不是我说的庞大系统。你注意到,你的系统里只涉及若干东西,数据库操作,浏览器等等。我想大概oracle算是这样庞大软件了。当然office3DmaxmayaProEapache等等应该都算。他们的设计者是真正的高手。

工作经历告诉我,对硬件的控制并不是什么难事,一个驱动程序你能做,往往只是指你能做硬件控制部分,和操作系统的部分也往往是别人都写好的代码,并不是你设计的,如果这部分你也可以设计,那你也是高手,可以小骄傲一下。所以收起骄傲,停止说这个驱动简单,那个驱动简单,我看哪怕是一个完整串口驱动的设计者,也绝对是行业中的高高手。奉劝那些为windowsLinux或者WINCE写驱动程序的程序员,谦虚点,不要一搞就瞧不起做应用程序的。去看看apache之父的言谈,就知道什么才是真正的高手。

2007年7月26日星期四

Windows Mobile bus driver

Windows Mobile的驱动程序程序实际是一个树状结构。Device.exe
负责加载几乎所有的驱动程序,而驱动程序的各种信息是存在于注册表里面。在系统启动?
锥危琩evice.exe是不知道有多少驱动是要加载的。他只是找到bus driver
,然后由它继续加载所有的驱动,所以bus driver是很重要的驱动程序,是桥梁驱动。
注册表中,HKEY_LOCAL_MACHINE\Drivers的键值RootKey指示了bus driver
的位置,一般你会看到Drivers\BuiltIn。那么HKEY_LOCAL_MACHINE\Drivers\BuiltIn下
Dll说明的是bus driver的名称。
微软的Ak中是默认有个驱动的BusEnum.dll(在public下有源代码)。这个驱动会枚举
BuiltIn下的所有键值,将这些驱动都加载进device.exe。
bus driver OEM是可以替换的。你此时可能回想既然已经有现成的,为什么还要自己写?
在很多时候,SOC的许多module的clock
开关都会放在一个统一的寄存器组里,那么驱动程序在想开/关这些寄存器时,都要操作?
桓黾拇嫫髯椋庵止蚕碜试吹姆梦适强赡芤鸪逋坏摹?

每个驱动要访问这个寄存器组,他们也就需要映射这个物理空间。如果每个驱动都映射,?
驮斐尚槟饪占涞募罄朔选?
那么如何完成自定义的bus driver
?微软为我们提供了很完善的类库,使我们很容易做到。Class DefaultBusDriver 和
Class DeviceFolder就是这样基础类。详细的代码细节自己看吧。Bus driver
是也是标准的流驱动。它需要在xxx_Init中引发一系列枚举动作,将驱动加载进来。

2007年6月21日星期四

Stay in Unattended mode(written by Mike)

I believe the fastest way to get the screen to turn off is this. As soon as your app detects the phone ring, first call:

PowerPolicyNotify(PPN_UNATTENDEDMODE, TRUE);

Then call

PowerPolicyNotify(PPN_POWERBUTTONPRESSED, 0);

Now handle the call. You need to be calling SystemIdleTimerReset(); every 30 seconds to keep the system from falling asleep while you're processing the call.

When the call is over, do:

PowerPolicyNotify(PPN_UNATTENDEDMODE, FALSE);

The first PPN puts the system into a state where, if the power button is pressed, it will turn the screen off but not suspend.

The second PPN simulates the press of the power button. Assuming the screen was on at the time, this will tell the system to suspend, but will instead go to the Unattended (screen off) state because of the first call. (If the screen was off when you did the PowerButtonPressed, it would turn the screen on.)

When you're done with the call, the last PPN tells the system to suspend immediately (as long as there isn't another app in Unattended).

SD卡关于WINCE睡眠的思考

blogs.msdn.com的Mike这样说:
On most devices, the SD hardware is powered down when the system suspends. So it needs to be re-initialized on wakeup. Then, after it is initialized, it needs to find the SD card and mount it. Depending on the SD bus hardware and the particular SD card, this process can take a few seconds.
在大多数的设备上,当系统进入睡眠时,SD设备是断电的。因此当系统醒来时,它需要重新的初始化。很自然地,初始化后,要查找SD卡和挂装文件系统。由于SD设备和SD卡的不同,这个过程需要一些时间。

由此想到以下的测试要求是不合理的:
1.SD卡复制文件时,susend和wakeup系统,不能要求他还能续传
2.Media player在播放SD卡上的音乐文件时,susend和wakeup系统,不能要求还能继续播放

2007年6月19日星期二

Windows Mobile 5 虚拟内存空间危机

虚拟内存空间这个概念我们常常听见,也有很多人能说出个一二。但是其实真正能理解这个概念的人并不多。今天就以Windows Mobile 5这个嵌入式操作系统理解一下什么叫Virtual Memory Space

Windows Mobile 5的虚拟内存模型如下图:

我们会看见这样的概念:kernel space user space。看似简单,你真的理解么?分析这样的memory space概念的时候一定分清视角,通俗说就是你是以user进程的角度,还是以微软操作系统内核的角度看这个空间。这点非常重要。不信你试试看。

0x8000,00000xFFFF,FFFF这个叫内核空间,也就是只有内核代码可以用这样的地址,他们是高特权级的区域。你的应用程序是访问不到的。如果你要“硬”来,操作系统一定会把你destroy掉,并给你一个类似非法访问的警告提示。除非你是“黑客”,用类似“缓冲溢出”等攻击方法来访问。

那么对于0x8000,0000以下的空间,叫做用户空间。什么?你做就知道了?!哈哈!那我问你,用户空间是不是只有用户程序可以访问的空间?你要是回答是,那就错了。用户空间是用户程序可以访问的空间,但不是“只有”。内核是老大,他当然也可以访问。所以你看,我们定义user space的概念一般只是针对user来说,而定义kernel space只是针对kernel来说,如果你只是单单把user spacekernel space放在一起,实际上没什么可比性。

小总结一下,kernel可以访问整个4GB空间,而user“最多”只能直接访问下2GB空间。我这里又多强调了一个“最多”。下面将详细讲解。

“最多”也就是说用户程序不能整个空间都是他的,一个多任务分时操作系统上同时有很多的用户的程序在“奔跑”,而且彼此独立,互不打扰。内存空间这样宝贵的资源,内核不可能让某个进程独占,因此,Windows Mobile5将下2GB空间均匀划分成64块,每一块32MB,并且slot0~slot32用于用户程序,slot32-slot63用于shared memory,所以WM5最多能有32个进程同时在运行。我们可以看见,每个单独的process是有可怜的32MB的大小。这个数值才是我们真正该关心的东西。每个进程的内存是不允许其他空间随便访问的。可怜可怜,你只有有32MB可以“使用”。“使用”这个概念再落实一下,就是如你调用VirtualAlloc()这样的内存分配函数。

我这篇文章叫“危机”,不是光介绍一下这些概念。想想,像device.exe这样的“进程大户”,身上挂装了n多的DLL,而且每个驱动程序都要类似调用VirtualAlloc的函数来映射寄存器空间,也就是CPU的整个寄存器空间几乎都要映射到这个狭小的32MB空间里,自己本身还有很多的代码与数据要占用空间,这个进程真是很吃力呀。随着SOCsystem on chip)的茁壮成长,一个chip里集成的module数量越来越多,随之而来的寄存器的增多,也就是寄存器组空间的增大,Windows Mobile5将不再可以使用。

幸好2007年我们迎来了Windows CE 6,虚拟空间的问题得以解决。可是微软相继发布的CrossBow主要是UI的变化,内核依然采用windows CE 5。不知什么时候微软会发布使用CE6内核的Windows Mobile

我们现在还要继续使用Windows CE5 kernel,那就要想办法解决这个问题。

前面提到slot32-slot63用于shared memory,如果能使用这块空间,那么虚拟内存不足的问题就可以解决。微软告诉我们mapped file是使用这块空间的,所以解决方案自然是
h_map=CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, Size, NULL);
VirBase=MapViewOfFile(h_map, FILE_MAP_WRITE, 0, 0, 0);

这样分配下来的虚拟地址会在0x4200,0000-0x7E00,0000区域内。此时只有虚拟空间被分配,实际的物理内存还并没有映射。因此这个时候你不能类似这样:

*(volatile DWORD *)VirBase=123;

写内存,这个动作会分配实际的物理内存,从而直接导致下面的VirtualCopy的失败。

这样虚拟空间有了,实际的物理内存可以这样映射


VirtualCopy((void*)VirBase, (void *)(PhyBase >> 8), fbSize, PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)

这个三个函数的结合使用,使我们可以充分利用user memory 的空间。

2007年6月13日星期三

Windows Mobile文件系统相关注册表设置

在工作过程中,很多同事都会问起文件系统注册表的设定。今天我就总结一下贴在这里。下面是一个典型的注册表的设置:

[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SMFLASH]

"Prefix"="DSK"

"Dll"="ONDisk.dll"

"Index"=dword:1

"Order"=dword:10

"Profile"="SMFLASH"

"IClass"="{A4E7EDDA-E575-4252-9D6B-4195D48BB865}"

"BmlVolumeId"=dword:0

"BmlPartitionId"=dword:3

[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SMFLASH]

"DefaultFileSystem"="IMGFS"

"PartitionDriver"="mspart.dll"

"AutoMount"=dword:1

"AutoPart"=dword:1

"AutoFormat"=dword:1

"Name"="Microsoft Flash Disk"

"Folder"="Storage"

[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SMFLASH\IMGFS]

"MountHidden"=dword:1

"MountAsROM"=dword:1

"XIP"=dword:0

[HKEY_LOCAL_MACHINE\System\StorageManager\Profiles\SMFLASH\FATFS]

"Flags"=dword:10

"FormatTfat"=dword:1

"CheckForFormat"=dword:1

"EnableWriteBack"=dword:1

"MountAsBootable"=dword:1

"MountAsRoot"=dword:1

红色的是经常被质疑的部分,他们虽然在微软的document上可以找到相关解释,可是还是有很多歧义,让我们一起仔细看看。下面我会用到很多PC的概念来帮助大家理解。

第一段代码是说明“硬盘”驱动是那个DLLProfile键值说明profile的名字,也就是【HKEY_LOCAL_MACHINE\System\StorageManager\Profiles】之后跟着的名字,这个名字你随便取,只要不超过7个字符都可以。其实profile可以理解为在“硬盘”驱动之上的软件层,属于文件系统这个领域的概念。而第一段是驱动程序领域的概念。

第二段是这个profile的总体的信息,他一定会有分区的概念,所以我们需要mspart.dll来识别分区信息。

"AutoMount"=dword:1

"AutoPart"=dword:1

"AutoFormat"=dword:1

这几个你就一起用,没关系的。我知道你担心什么。那就是担心分区的内容会不会每次重启都会重建,分区内容会丢失。实际上不会的。Autoformat是当发现分区的文件系统没有格式化时,才回去format,反之,如果已经formated,那么就不会有任何动作。Autopart是在你的profile没有分区时,系统自动帮你分区,不过他没有那么智能,只能将整个profile分成一个区,从头到尾。当然同上,如果你已经有分区信息,系统不会做任何动作。Automount是如果分区没有安装,就会自动安装。这3个键值,你就一起把他们加上,肯定没问题。

第三段定义了profile之上的特定分区imgfs分区,这是一定要有的分区。MountHidden是将这个分区安装为不可见。这样你在资源管理器里就不会看见这个目录。MountAsROM是以只读方式安装,这样分区的文件是不可写的。

第四段代码是真正的用户空间,是fat文件系统。FORMATTFAT是强制以tfat文件系统格式化,tfatfat更加的强壮。CheckForFormat键值是可选的,加上之后,这个分区在每次重启是系统都会用

IOCTL_HAL_QUERY_FORMAT_PARTITION ioctl来检查是否需要格式话。如果没有这个键值,文件系统就不会调用这个ioctl

"MountAsBootable"=dword:1

"MountAsRoot"=dword:1

这两个键值一般都一起用,就是指明是该分区是root file system

好了,到此就基本说完了。如果有什么问题可以mail我。

2007年5月30日星期三

dio nb0 bin的关系


在wm533中,生成的image的扩展名很多,dio,nb0,bin等等,他们都是什么关系。看一个例子。
Memory.cfg.xml的内容如下

我们可以看见image由4个分区组成,分别为ULDR,NK,OS,Storage(最多只能有4个分区)。这4个分区都放在一个NAND flash上面,叫做FLASH,所以build完整个image会生成一个flash.dio。
Flash.dio完全是一个flash的镜像,如果用于工厂的量产,可以直接映射在NAND flash上面用。
同时会生成flash.dio.nb0,看似只多了一个扩展名,可是内容就完全不同了,这是为第三方的flash用的,会在每个512字节后面,也是是一个扇区后面,添加8个字节的sectorinfo。所以它会比flash.dio大。要强调的是,这个文件用flash.dio处理而来。
最后会生成flash.bin,它由flash.dio.nb0处理而来,这个文件是有格式的,也就是说有文件头,文件信息之类。
总结一下,flash.dio -> flash.dio.nb0 -> flash.bin,如此的生成过程。

2007年5月28日星期一

天蝎和水瓶

男天蝎和女水瓶
你俩是“刹那触电型”搭配,速配指数7.0。你严谨且实际而她崇尚自由,也很理想化。彼此会有着不可捉摸的吸引力,有时也会有一瞬间的触电感。但你俩在各 方面的差异都很大,你会觉得她的博爱是不专一,还认为她只会有不切实际的空想;而她也会觉得你善妒、占有欲也极强,束缚着她喘不过气来。若真心想在一块, 还是很有希望的。其实,你俩都有着坚定的信心,尤其对自己意志贯彻执守的程度,及超越自我的企图心强,最好还能创造一些共同的目标和兴趣。抓住这些,你俩 还是很有机会成双成对的咧!

男水瓶和女天蝎
你俩是“刹那触电型”搭配,速配指数7.0。她严谨且实际而你崇尚自由,也很理想化。彼此会有着不可捉摸的吸引力,有时也会有一瞬间的触电感。但你俩在各 方面的差异都很大,她会觉得你的博爱是花心,还认为你只会有不切实际的空想;而你也会觉得她妒忌心强、占有欲也极强,束缚着你喘不过气来。若真心想在一 块,还是很有希望的。其实,你俩都有着坚定的信心,尤其对自己意志贯彻执守的程度,及超越自我的企图心强,最好还能创造一些共同的目标和兴趣。抓住这些, 你俩还是很有机会成双成对的咧!




天蝎座—水瓶座 3 Back 3


水——固定型——消极的 气——固定型——积极的
由冥王星制约 由天王星制约
象征:蝎子和鹰 象征:水瓶
夜晚力量——阴性 白天力量——阳性

这两种人很难接近,都太野性。在这一章里,我们尽量保持不偏不倚,坚持中立。不过要保持中立也不容易,因为从文学角度讲,就没办法让人保持冷静,超然局外,尤其看到两人缠到一起,一会儿爱得死去活来,一会儿暴跳如雷地争吵,谁能不动心?

有人会想:这样两个人怎么会相识的呢?还有的人会想:这样两个人相处是明智的做法吗?他们之间的4—10日宫型决定他们总会处于紧张的关系中。问题是:这两人要在这些紧张关系中干些什么呢?他们是需要把这些紧张化作争执冲突,还是化为一种能量去完成共同的心愿呢?

在天蝎座人看来,水瓶座人穿着标新立异,与众不同,挺有意思,而在水瓶座人看来,天蝎座温文有礼,安静沉着,不知整天想些什么,也够让人好奇。

于是两人走近了,但都抱着很强的防范意识。由于他们一人属气,一人属水,结果反而不易相互伤害,甚至令双方受益匪浅。这两人最热衷的业余消遣便是刺探彼此 的秘密。这两人都是天生的侦探,谁也不能忍受对对方的隐秘有所不知。他们喜欢研究对方,把对方像一个钟一样在想象中一一分解。不过,天蝎座人干起来老道, 不太显山露水,而水瓶座人干起来则不太知掩饰。当然水瓶座人读到这里时一定会抗议,说他们压根不爱和人说三道四,但他们喜欢凡事都找个究竟。因此,建议由 天蝎座人和水瓶座人组成的伴侣,最好各自锁好自己的记事本、日记和抽屉,以免产生不快。

他们另一个必须逾越的障碍是由于天蝎座和水瓶座都是固定型,所以他们都很固执。不过,固定型也意味着稳定长久,所以未尝不好。

他们中的差异之一是天蝎座人记忆力太好,而水瓶座人总心不在焉,所以对什么都悄会久存在心底。天蝎座人可以极好控制自己;而水瓶座人自制力极差,这点可真该好好向对方学习。

天蝎座人花钱谨慎,而水瓶座人大手大脚,甚至连钱是被偷还是花了也弄不清。明白这点后,天蝎座人最好去管家,但也不要让水瓶座人手头太紧。

天蝎 vs 水瓶

很麻烦的一对,基本大家都不是雷同的东西,你们一齐只有一个原因:贪新鲜的水瓶座偶然被深情的天蝎魅力所吸引,一见就闪出电光来,但结果只有两个:就是水 瓶座顶不顺天蝎座这一种情绪高低起伏及独霸性,一声不向就走了,其二就是天蝎座支持不了水瓶座那种不是一般人可以接受到的奇异行为,根本无办法控制他而黯 然引退。

水瓶座觉得自己不属于任何人,只是属于自己,但天蝎座就要他只是属于天蝎座一个人,你说这样怎么办。

对于家庭的概念亦有不同,水瓶座没有天蝎座那么实际,只要想拥有,不惜任何代价,对天蝎座来说总是费解,在屋企、衣食住行问题方面就会出乱子了。

勾 引:天蝎座要找紧水瓶座贪新鲜,求新的心理,要捉住他,不妨用多些自己天然的性感魅力,多做勾引性的举动,日日给一些新惊喜,这样或者可以吸引住他的心, 还有行事之时,要多一些神秘感,太多的保留,配合你收收埋埋的个性,你知道吗,你愈神秘,他愈想起你底子,吊起来买是最好的方法。

发烂:这一套是两个人都要避忌的行为,因为你们都会有的倾向,水瓶座好急燥,见到不满的事,就会即刻发烂,而天蝎座可以忍住忍住,但一有机会就乘机发烂,结果伤的就系两个人的感情,说到侵略性,你不是天蝎座的班底。

2007年5月8日星期二

微软引起的while不归路

如果你是windows mobile的驱动程序开发人员,又恰好写了类似的这样一句话。
while(dev->devState!=5);
说明一下,dev是个结构体指针。你希望等待它的成员变量变成5,继续执行。同时

有可能你在中断服务程序中改变这个成员变量的值。
哈哈!我幸运的告诉你你已经进入了“死循环”,永远跳不出去啦。

这是个编译器优化产生的问题,看看编译器产生的汇编片段。
|$L39064|
cmp r3, #5
bne |$L39064|
它将dev->devState优化为了一个寄存器r3,这样的比较是跳不出循环的。

那么要解决这个问题就是将dev这个指针声明为volatile,之后,看编译器产生的汇编片段,变成了
|$L39064|
ldr r3, [r6, #4]
cmp r3, #5
bne |$L39064|
这才是我们想要的结果。汗!!

我在WM533做的实验,确实有这个问题。我觉得这个优化,微软做的太离谱了。不知道算不算编译器bug