新闻中心

EEPW首页>嵌入式系统>设计应用> S3C2410下WinCE6.0的启动过程详解

S3C2410下WinCE6.0的启动过程详解

作者: 时间:2016-11-20 来源:网络 收藏
通过前两篇文章的介绍,我们已经知道NBOOT用来引导EBOOT,继而EBOOT加载并引导WinCE操作系统(NK)。那么,WinCE6.0启动过程又是怎样的呢?本文基于S3C2410的平台做一个详细的分析。需要说明的是,WinCE6.0的整个启动过程对于同一类型的MCU来说大同小异,如S3C2410和PXA270同属ARM平台的MCU,所以他们的启动过程是类似的,可以说唯一的不同就在OAL处,而WinCE操作系统的启动正是从OAL开始的。

OAL(OEM Adaptation Layer)即OEM适配层,它的主要作用是在移植WinCE到新的硬件平台时减少操作系统的修改,通俗的说就是为WinCE操作系统抹平MCU的差异,使其能很方便的在不同MCU上运行。所以,OAL包括了和系统硬件通讯的最底层代码。内核则通过OAL跟硬件进行交互。逻辑上,OAL是介于CE内核和设备硬件之间的一个代码层,是一个抽象的概念。物理上,OAL和其他一些库一起链接成可执行文件,在WinCE6.0中对应的文件是OAL.exe,这是OAL的客观存在。WinCE6.0中的OAL跟先前的OAL比,是有一些变化的,它从内核中分离出来成为OAL.exe,而内核则变成了Kernel.dll。这样做的好处是可以单独升级OAL。但整体的OAL结构并没有改变,OEM函数保持一致,OAL和Kernel的接口由共享结构NKGLOBAL实现。这一部分的具体内容下一篇再做介绍。下图所示为WinCE6.0的OAL设计。

本文引用地址://m.amcfsurvey.com/article/201611/318750.htm

在移植WinCE到新的硬件平台时,创建OAL是最复杂的任务之一。一般来说,最简单的方法是拷贝一个跟新的硬件平台类似的且成熟的OAL,然后根据硬件的不同进行修改,使其满足目标硬件的特定要求。这里不展开说明,回头再单独整理。
从EBOOT到OAL.exe的跳转是从OEMLaunch()开始的,函数OEMLaunch()中调用Launch(dwPhysLaunchAddr),它的实现代码如下:



Code
LEAF_ENTRYLaunch

ldrr2,=PhysicalStart
ldrr3,=(VIR_RAM_START-PHY_RAM_START)

subr2,r2,r3

movr1,#0x0070;DisableMMU
mcrp15,0,r1,c1,c0,0
nop
movpc,r2;JumptoPStart
nop

;MMU&cachesnowdisab led.

PhysicalStart

movr2,#0
mcrp15,0,r2,c8,c7,0;FlushtheTLB
movpc,r0;Jumptoprogramwearelaunching.

函数Launch()的参数为物理地址,因为在跳转之前已将MMU关闭。该地址可通过VIEWBIN来查看,如下图所示:


如何确定这个地址对应的是NK.bin中的哪一个文件呢,先前说是OAL.exe,证据何在。在PB6.0中增加了浏览NK.bin的功能,我们可以利用此功能查看NK.bin的详细情况,如下图所示:



从上图中可以看出0x80205394处对应的是NK.exe,而这里的NK.exe即为OAL.exe。
至此,我们已经知道EBOOT是如何跳转到OAL.exe中的了。接下来继续看OAL.exe的执行过程。
OAL的启动代码如下:

Code
LEAF_ENTRYStartUp

;ComputetheOEMAddressTablesphysicaladdressand
;loaditintor0.KernelStartexpectsr0tocontain
;thephysicaladdressofthistable.TheMMUisnt
;turnedonuntilwellintoKernelStart.

addr0,pc,#g_oalAddressTable-(.+8)
blKernelStart

OAL的启动代码和EBOOT的启动代码经常复用,但为了代码的简洁,最好还是分开实现,而且在EBOOT中如果已经初始化了相关硬件,那么OAL的启动代码就可以省去那部分工作,可以很简练,如上面的代码所示。

可以看出,OAL的启动代码又调用了函数KernelStart(),而这个函数是在文件C:WINCE600PRIVATEWINCEOSCOREOSNKLDRARMarmstart.s中实现的,代码如下:

Code
LEAF_ENTRYKernelStart

movr11,r0;(r11)=&OEMAddressTable(savepointer)

;figureoutthevi rtualaddressofOEMAddressTable
movr1,r11;(r1)=&OEMAddressTable(2ndargumenttoVaFromPa)
blVaFromPa
movr6,r0;(r6)=VAofOEMAddressTable

;convertbaseofPTstoPhysicaladdress
ldrr4,=PTs;(r4)=virtualaddressofFirstPT
movr0,r4;(r0)=virtualaddressofFirstPT
movr1,r11;(r1)=&OEMAddressTable(2ndargumenttoPaFromVa)
blPaFromVa

movr10,r0;(r10)=ptrtoFirstPT(physical)

;Zerooutpagetables&kerneldatapage

movr0,#0;(r0-r3)=0stostore
movr1,#0
movr2,#0
movr3,#0
movr4,r10;(r4)=firstaddresstoclear
addr5,r10,#KDEnd-PTs;(r5)=lastaddress+1
18stmiar4!,{r0-r3}
stmiar4!,{r0-r3}
cmpr4,r5
blo%B18

;readthearchitectureinformation
blGetCpuId
movr5,r0LSR#16;r5>>=16
andr5,r5,#0x0000000f;r5&=0x0000000f==architectureid

;Setup2ndlevelpagetabletomapthehighmemoryareawhichcontainsthe
;firstlevelpagetable,2ndlevelpagetables,kerneldatapage,etc.
;(r5)=architectureid

addr4,r10,#HighPT-PTs;(r4)=ptrtohighpagetable

cmpr5,#ARMv6;v6orlater?
;ARMV6_MMU
orrger0,r10,#PTL2_KRW+PTL2_SMALL_PAGE+ARMV6_MMU_PTL2_SMALL_XN
;(r0)=PTEfor4K,kr/wu-/-page,uncachedunbuffered,nonexecutable
;PREARMV6_MMU
orrltr0,r10,#PTL2_KRW+(PTL2_KRW<<2)+(PTL2_KRW<<4)+(PTL2_KRW<<6)
;NeedtoreplicateAPbitsintoall4fields
orrltr0,r0,#PTL2_SMALL_PAGE+PREARMV6_MMU_PTL2_SMALL_XN
;(r0)=PTEfor4K,kr/wu-/-page,uncachedunbuffered,nonexecutable
strr0,[r4,#0xD0*4];storetheentryinto4slotstomap16Kofprimarypagetable
addr0,r0,#0x1000;steponthephysicaladdress
strr0,[r4,#0xD1*4]
addr0,r0,#0x1000;steponthephysicaladdress
strr0,[r4,#0xD2*4]
addr0,r0,#0x1000;steponthephysicaladdress
strr0,[r4,#0xD3*4]

addr8,r10,#ExceptionVectors-PTs;(r8)=ptrtovectorpage
orrr0,r8,#PTL2_SMALL_PAGE;constructthePTE(C=B=0)

;;Theexceptionstacksandthevectorsaremappedasasinglekr/wpage.
;;Anyalternativewillusemorephysicalmemory.
;;Multiplemappingsdontprovideanyrealprotection:ifthevectorswereinar/opage,
;;theycouldstillbecorruptedviathekr/wsettingr equiredforthestacks.
cmpr5,#ARMv6;v6orlater?
;ARMV6_MMU
orrger0,r0,#PTL2_KRW
;PREARMV6_MMU
orrltr0,r0,#PTL2_KRW+(PTL2_KRW<<2)+(PTL2_KRW<<4)+(PTL2_KRW<<6)
;NeedtoreplicateAPbitsintoall4fieldsforpre-V6MMU

strr0,[r4,#0xF0*4];storeentryforexceptionstacksandvectors
;other3entriesnowunused

addr9,r10,#KPage-PTs;(r9)=ptrtokdatapage
orrr0,r9,#PTL2_SMALL_PAGE;(r0)=PTEfor4K(C=B=0)

;ARMV6_MMU(conditioncodesstillset)
orrger0,r0,#PTL2_KRW_URO;Nosubpageaccesscontrol,sowemustsetthisalltokr/w+ur/o
;PREARMV6_MMU
orrltr0,r0,#(PTL2_KRW<<0)+(PTL2_KRW<<2)+(PTL2_KRW_URO<<4)
;(r0)=setpermskr/wkr/wkr/w+ur/or/o
strr0,[r4,#0xFC*4];storeentryforkerneldatapage
orrr0,r4,#PTL1_2Y_TABLE;(r0)=1stlevelPTEforhighmemorysection
addr1,r10,#0x4000
strr0,[r1,#-4];storePTEinlastslotof1stleveltable

;Fillinfirstlevelpagetableentriestocreate"staticallymapped"regions
;fromthecontentsoftheOEMAddressTablearray.
;
;(r5)=architectureid
;(r9)=ptrtoKDatapage
;(r10)=ptrto1stlevelpagetable
;(r11)=ptrtoOEMAddressTablearray

addr10,r10,#0x2000;(r10)=ptrto1stPTEfor"unmappedspace"

movr0,#PTL1_SECTION
orrr0,r0,#PTL1_KRW;(r0)=PTEfor0:1MB(C=B=0,kernelr/w)
20movr1,r11;(r1)=ptrtoOEMAddressTablearray(physical)


25ldrr2,[r1],#4;(r2)=virtualaddresstomapBankat
ldrr3,[r1],#4;(r3)=physicaladdresstomapfrom
ldrr4,[r1],#4;(r4)=numMBtomap

cmpr4,#0;Endoftable?
beq%F29

ldrr12,=0x1FF00000
andr2,r2,r12;VAneeds512MB,1MBaligned.

ldrr12,=0xFFF00000
andr3,r3,r12;PAneeds4GB,1MBaligned.

addr2,r10,r2,LSR#18
addr0,r0,r3;(r0)=PTEfornextphysicalpage

28strr0,[r2],#4
addr0,r0,#0x00100000;(r0)=PTEfornextphysicalpage

subr4,r4,#1;DecrementnumberofMBleft
cmpr4,#0
bne%B28;MapnextMB

bicr0,r0,#0xF0000000;ClearSectionBaseAddressField
bicr0,r0,#0x0FF00000;ClearSectionBaseAddressField
b%B25;Getnextelement


29
subr10,r10,#0x2000;(r10)=restoreaddressof1stlevelpagetable

;Theminimalpagemappingsaresetup.InitializetheMMUandturniton.

;therearesomeCPUswithpipelineissuesthatrequiresidentitymappingbeforeturningonMMU.
;WellcreateanidentitymappingfortheaddresswelljumptowhenturningonMMUonandremove
;themappingafterweturnonMMUandrunningonVirtualaddress.


ldrr12,=0xFFF00000;(r12)=maskforsectionbits
andr1,pc,r12;physicaladdressofwhereweare
;NOTE:weassumethattheKernelStartfunctionneverspamacross1Mboundary.
orrr0,r1,#PTL1_SECTION
orrr0,r0,#PTL1_KRW;(r0)=PTEfor1Mforcurrentphysicaladdress,C=B=0,kernelr/w
addr7,r10,r1,LSR#18;(r7)=1stlevelPTentryfortheidentitymap
ldrr8,[r7];(r8)=savedcontentofthe1st-levelPT
strr0,[r7];createtheidentitymap

movr1,#1
mtc15r1,c3;Setupaccesstodomain0andclearother
mtc15r10,c2;setuptranslationbase(physicalof1stlevelPT)

movr0,#0
mcrp15,0,r0,c8,c7,0;FlushtheI&DTLBs

mfc15r1,c1
orrr1,r1,#0x007F;changedtoread-mod-writeforARM920Enable:MMU,Align,DCache,WriteBuffer

cmpr5,#ARMv6;r5stillset
;ARMV6_MMU
orrger1,r1,#0x3000;vectoradjust,ICache
orrger1,r1,#1<<23;V6-formatpagetables
orrger1,r1,#ARMV6_U_BIT;V6-setUbit,letAbitcontrolunalignmentsupport
;PREARMV6_MMU
orrltr1,r1,#0x3200;vectoradjust,ICache,ROMprotection

ldrr0,VirtualStart
cmpr0,#0;makesurenostallon"movpc,r0"below
mtc15r1,c1;enabletheMMU&Caches
movpc,r0;&jumptonewvirtualaddress
nop

;MMU&cachesnowenabled.
;
;(r10)=physcialaddressof1stlevelpagetable
;(r7)=entryin1stlevelPTforidentitymap
;(r8)=saved1stlevelPTsaveat(r7)
VStartldrr2,=FirstPT;(r2)=VAof1stlevelPT
subr7,r7,r10;(r7)=offsetinto1st-levelPT
strr8,[r2,r7];restorethetemporaryidentitymap
mcrp15,0,r0,c8,c7,0;FlushtheI&DTLBs

;
;setupstackforeachmodes:currentmode=supervisormode
;
ldrsp,=KStack
addr4,sp,#KData-KStack;(r4)=ptrtoKDataStruct

;setupABORTstack
movr1,#ABORT_MODE:OR:0xC0
msrcpsr_c,r1;switchtoAbortModew/IRQsdisabled
addsp,r4,#AbortStack-KData

;setupIRQstack
movr2,#IRQ_MODE:OR:0xC0
msrcpsr_c,r2;switchtoIRQModew/IRQsdisabled
addsp,r4,#IntStack-KData

;setupFIQstack
movr3,#FIQ_MODE:OR:0xC0
msrcpsr_c,r3;switchtoFIQModew/IRQsdisabled
addsp,r4,#FIQStack-KData

;setupUNDEFstack
movr3,#UNDEF_MODE:OR:0xC0
msrcpsr_c,r3;switchtoUndefinedModew/IRQsdisabled
movsp,r4;(sp_undef)=&KData

;switchbacktoSupervisormode
movr0,#SVC_MODE:OR:0xC0
msrcpsr_c,r0;switchtoSupervisorModew/IRQsdisabled
ldrsp,=KStack

;continueinitializationinC
addr0,sp,#KData-KStack;(r0)=ptrtoKDataStruct
strr6,[r0,#pAddrMap];storeVAofOEMAddressTableinKData
blARMInit;callCfunctiontoperformtherestofinitializations
;uponreturn,(r0)=entrypointofkernel.dll

movr12,r0
ldrr0,=KData
movpc,r12;jumptoentryofkernel.dll

从上面的代码可以看出,KernelStart()通过OEMAddressTable初始化了MMU,然后通过调用函数ARMInit()获得kernel.dll的入口点,最后跳转到kernel.dll的入口点处。

为了找到Kernel.dll的入口点,用IDA反汇编kernel.dll文件,可以看到,Kernel.dll的入口点为NKStartup。

NKStartup()的实现在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELARM mdarm.c中,代码如下:

Code
//
//NKStartup-entrypointofkernel.dll.
//
//NKLoadersetuponlytheminimalmappings,whichincludesARMHigharea,andthecachedstaticmappingarea,
//with*EVERYTHINGUNCACHED*.Interruptvectorsarenotsetupeither.So,theinitsequencereqiures:
//(1)pickupdatapassedfromnkloader
//(2)Findentrypointofoal,exchangeglobals,findoutthecachemode.
//(3)fillintherestofstaticmappedarea(0xa0000000-0xbfffffff),PSLfaultingaddress, interruptvectors,
//modstacks,etc.Then,changethecachedstaticmappingareatousecache,andflushI&DTLB.
//(4)continuenormalloadingofkernel(findKITLdll,callOEMInitDebugSerial,etc.)
//
voidNKStartup(structKDataStruct*pKData)
{
PFN_OEMInitGlobalspfnInitGlob;
PFN_DllMainpfnKitlEntry;
DWORDdwCpuId=GetCpuId();

//(1)pickupargumentsfromthenkloader
g_pKData=pKData;
pTOC=(constROMHDR*)pKData->dwTOCAddr;
g_pOEMAddressTable=(PADDRMAP)pKData->pAddrMap;

/*getarchitectureidandupdatepageprotectionattributes*/
pKData->dwArchitectureId=(dwCpuId>>16)&0xf;
if(pKData->dwArchitectureId>=ARMArchitectureV6){
//v6orlater
pKData->dwProtMask=PG_V6_PROTECTION;
pKData->dwRead=PG_V6_PROT_READ;
pKData->dwWrite=PG_V6_PROT_WRITE;
pKData->dwKrwUro=PG_V6_PROT_URO_KRW;
pKData->dwKrwUno=PG_V6_PROT_UNO_KRW;

}else{
//pre-v6
pKData->dwProtMask=PG_V4_PROTECTION;
pKData->dwRead=PG_V4_PROT_READ;
pKData->dwWrite=PG_V4_PROT_WRITE;
pKData->dwKrwUro=PG_V4_PROT_URO_KRW;
pKData->dwKrwUno=PG_V4_PROT_UNO_KRW;
}

//initializenkglobals
FirstROM.pTOC=(ROMHDR*)pTOC;
FirstROM.pNext=0;
ROMChain=&FirstROM;
KInfoTable[KINX_PTOC]=(long)pTOC;
KInfoTable[KINX_PAGESIZE]=VM_PAGE_SIZE;

g_ppdirNK=(PPAGEDIRECTORY)&ArmHigh->firstPT[0];
pKData->pNk=g_pNKGlobal;

//(2)findentryofoal
pfnInitGlob=(PFN_OEMInitGlobals)pKData->dwOEMInitGlobalsAddr;

//nocheckinghere,ifOALentrypointdoesntexist,wecantcontinue
g_pOemGlobal=pfnInitGlob(g_pNKGlobal);
g_pOemGlobal->dwMainMemoryEndAddress=pTOC->ulRAMEnd;
pKData->pOem=g_pOemGlobal;

//setupglobals
pVMProc=g_pprcNK;
pActvProc=g_pprcNK;

g_pNKGlobal->pfnWriteDebugString=g_pOemGlobal->pfnWriteDebugString;

//(3)setupvectors,UCmappings,modestacks,etc.
ARMSetup();

//
//cacheisenabledfromhereon
//

//(4)commonstartupcode.

//trytoloadKITLifexist
if((pfnKitlEntry=(PFN_DllMain)g_pOemGlobal->pfnKITLGlobalInit)||
(pfnKitlEntry=(PFN_DllMain)FindROMDllEntry(pTOC,KITLDLL))){
(*pfnKitlEntry)(NULL,DLL_PROCESS_ATTACH,(DWORD)NKKernelLibIoControl);
}

#ifdefDEBUG
CurMSec=dwPrevReschedTime=(DWORD)-200000;//~3minutesbeforewrap
#endif

OEMInitDebugSerial();

//debugchkonlyworksafterwehavesomethingtoprintto.
DEBUGCHK(pKData==(structKDataStruct*)PUserKData);
DEBUGCHK(pKData==&ArmHigh->kdata);

OEMWriteDebugString((LPWSTR)NKSignon);

/*Copyinterlockedapicodeintothekpage*/
DEBUGCHK(sizeof(structKDataStruct)<=FIRST_INTERLOCK);
DEBUGCHK((InterlockedEnd-InterlockedAPIs)+FIRST_INTERLOCK<=0x400);
memcpy((char*)g_pKData+FIRST_INTERLOCK,InterlockedAPIs,InterlockedEnd-InterlockedAPIs);

/*setupprocessorversioninformation*/
CEProcessorType=(dwCpuId>>4)&0xFFF;
CEProcessorLevel=4;
CEProcessorRevision=(WORD)dwCpuId&0x0f;
CEInstructionSet=PROCESSOR_ARM_V4I_INSTRUCTION;

RETAILMSG(1,(L"ProcessorType=%4.4xRevision=%drn",CEProcessorType,CEProcessorRevision));
RETAILMSG(1,(L"OEMAddressTable=%8.8lxrn",g_pOEMAddressTable));

OEMInit();//initializefirmware

//flushI&DTLB
OEMCacheRangeFlush(NULL,0,CACHE_SYNC_FLUSH_TLB);

KernelFindMemory();

DEBUGMSG(1,(TEXT("NKStartupdone,startingupkernel.rn")));

KernelStart();

//neverreturned
DEBUGCHK(0);
}

NKStartup()的代码就不多解释了,注释已经很详细。该函数的最后又调用了KernelStart ()函数。注意这里的KernelStart()跟上面曾提到的KernelStart()是不一样的。这里KernelStart()的实现在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELARMarmtrap.s中,代码和反汇编的对比如下图所示。
可以看到,这里调用了KernelInit()和FirstSchedule()这两个函数。先说FirstSchedule(),它开始了WinCE6.0的第一个调度。它的实现跟KernelStart()在同一文件中,而实现代码跟WinCE5.0中完全一样。接下来,我们继续跟踪KernelInit()函数,其实现在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELnkinit.c中,代码如下:

Code
//------------------------------------------------------------------------------
//KernelInit-Kernelinitializationbeforeschedulingthe1stthread
//------------------------------------------------------------------------------

voidKernelInit(void)
{
#ifdefDEBUG
g_pNKGlobal->pfnWriteDebugString(TEXT("WindowsCEKernelInitrn"));
#endif
APICallInit();//setupAPIset
HeapInit();//setupkernelheap
InitMemoryPool();//setupphysicalmemory
PROCInit();//initializeprocess
VMInit(g_pprcNK);//setupVMforkernel
THRDInit();//initializethreads
MapfileInit();

#ifdefDEBUG
g_pNKGlobal->pfnWriteDebugString(TEXT("Schedulingthefirstthread.rn"));
#endif
}

这段代码跟WinCE5.0中的结构基本一致,但实际上有很大的不同。跟WinCE6.0启动最紧密的函数是THRDInit (),这之前都是做相应的初始化。THRDInit ()的实现在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELthread.c中,代码如下:

Code
//------------------------------------------------------------------------------
//THRDInit-initializethreadhandling(calledatsystemstartup)
//------------------------------------------------------------------------------
voidTHRDInit(void)
{
LPBYTEpStack;

DEBUGLOG(1,g_pprcNK);

//dontallowthreadcreateonememorydropbelow1%available
if(g_cMinPageThrdCreate g_cMinPageThrdCreate=PageFreeCount/100;
}

//mapW32threadpriorityifOEMchooseto
if(g_pOemGlobal->pfnMapW32Priority){
BYTEprioMap[MAX_WIN32_PRIORITY_LEVELS];
inti;
memcpy(prioMap,W32PrioMap,sizeof(prioMap));
g_pOemGlobal->pfnMapW32Priority(MAX_WIN32_PRIORITY_LEVELS,prioMap);
//validatethethepriorityismono-increase
for(i=0;i if(prioMap[i]>=prioMap[i+1])
break;
}

DEBUGMSG((MAX_WIN32_PRIORITY_LEVELS-1)!=i,(L"ProcInit:InvalidprioritymapprovidedbyOEM,Ignored!rn"));
if((MAX_WIN32_PRIORITY_LEVELS-1)==i){
memcpy(W32PrioMap,prioMap,sizeof(prioMap));
}
}

//allocatememoryforthe1stthread
pCurThread=AllocMem(HEAP_THREAD);
DEBUGCHK(pCurThread);

dwCurThId=(DWORD)HNDLCreateHandle(&cinfThread,pCurThread,g_pprcNK)&~1;
DEBUGCHK(dwCurThId);

InitThreadStruct(pCurThread,(HANDLE)dwCurThId,g_pprcNK,THREAD_RT_PRIORITY_ABOVE_NORMAL);

if(g_pOemGlobal->cbCoProcRegSize){

DEBUGCHK(g_pOemGlobal->pfnInitCoProcRegs);
DEBUGCHK(g_pOemGlobal->pfnSaveCoProcRegs);
DEBUGCHK(g_pOemGlobal->pfnRestoreCoProcRegs);

//checkthedebugregisterrelatedvalues.
if(g_pOemGlobal->cbCoProcRegSize>MAX_COPROCREGSIZE){
g_pOemGlobal->cbCoProcRegSize=g_pOemGlobal->fSaveCoProcReg=0;
}else{
PNAMEpTmp=AllocName(g_pOemGlobal->cbCoProcRegSize);
DEBUGCHK(pTmp);
g_dwCoProcPool=pTmp->wPool;
FreeName(pTmp);
}
}else{
g_pOemGlobal->fSaveCoProcReg=FALSE;
}
DEBUGMSG(ZONE_SCHEDULE,(TEXT("cbCoProcRegSize=%drn"),g_pOemGlobal->cbCoProcRegSize));

AddToDListHead(&g_pprcNK->thrdList,&pCurThread->thLink);
g_pprcNK->wThrdCnt++;


#ifdefSHx
SetCPUGlobals();
OEMCacheRangeFlush(0,0,CACHE_SYNC_ALL);
#endif


if(!OpenExecutable(NULL,TEXT("NK.EXE"),&g_pprcNK->oe,TOKEN_SYSTEM,NULL,0)){
LoadE32(&g_pprcNK->oe,&g_pprcNK->e32,0,0,0);
g_pprcNK->BasePtr=(LPVOID)g_pprcNK->e32.e32_vbase;
UpdateKmodVSize(&g_pprcNK->oe,&g_pprcNK->e32);
}

//create/setupstack
pStack=VMCreateStack(g_pprcNK,KRN_STACK_SIZE);
pCurThread->dwOrigBase=(DWORD)pStack;
pCurThread->dwOrigStkSize=KRN_STACK_SIZE;
pCurThread->tlsSecure=pCurThread->tlsNonSecure=pCurThread->tlsPtr=TLSPTR(pStack,KRN_STACK_SIZE);
pCurThread->hTok=TOKEN_SYSTEM;

//Saveoffthethreadsprogramcounterforgettingitsnamelater.
pCurThread->dwStartAddr=(DWORD)SystemStartupFunc;

MDSetupThread(pCurThread,(LPVOID)SystemStartupFunc,0,TH_KMODE,0);

CELOG_ThreadCreate(pCurThread,g_pprcNK,NULL);

MakeRun(pCurThread);
DEBUGMSG(ZONE_SCHEDULE,(TEXT("Scheduler:Createdmasterthread%8.8lxrn"),pCurThread));

}

可以看到,这里开始了一个线程,线程处理函数为SystemStartupFunc(),其实现在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELschedule.c,实现代码如下:

Code
//------------------------------------------------------------------------------
void
SystemStartupFunc(
ulongparam
)
{
HANDLEhTh;

//recordPendEventaddressforSetInterruptEvent
KInfoTable[KINX_PENDEVENTS]=(DWORD)&PendEvents1;

KernelInit2();

//adjustalarmresolutionifititsnotinbound
if(g_pOemGlobal->dwAlarmResolution g_pOemGlobal->dwAlarmResolution=MIN_NKALARMRESOLUTION_MSEC;
elseif(g_pOemGlobal->dwAlarmResolution>MAX_NKALARMRESOLUTION_MSEC)
g_pOemGlobal->dwAlarmResolution=MAX_NKALARMRESOLUTION_MSEC;

VERIFY(LoaderInit());

//initializethecompiler/GScookie-thismusthappenbeforeotherthreads
//startrunning
__security_init_cookie();

PagePoolInit();

//Thiscanonlybedoneaftertheloaderinitialization
LoggerInit();//InitializationforCeLog,profiler,code-coverage,etc.
SysDebugInit();//initializeSystemDebugger(HWDebugstub,Kerneldumpcapture,SWKernelDebugstub)

//dothisnow,sothatwecontinuerunningafterwevecreatedthenewthread
#ifdefSTART_KERNEL_MONITOR_THREAD
hTh=CreateKernelThread(Monitor1,0,THREAD_RT_PRIORITY_ABOVE_NORMAL,0);
HNDLCloseHandle(g_pprcNK,hTh);
#endif

pCleanupThread=pCurThread;
hAlarmThreadWakeup=NKCreateEvent(0,0,0,0);
DEBUGCHK(hAlarmThreadWakeup);
InitializeCriticalSection(&rtccs);
IntrEvents[SYSINTR_RTC_ALARM-SYSINTR_DEVICES]=LockIntrEvt(hAlarmThreadWakeup);
DEBUGCHK(IntrEvents[SYSINTR_RTC_ALARM-SYSINTR_DEVICES]->phdIntr);

//GivetheOEMafinalchancetodoamorefull-featuredinitbeforeany
//appsarestarted
KernelIoctl(IOCTL_HAL_POSTINIT,NULL,0,NULL,0,NULL);

InitMsgQueue();
InitWatchDog();

//createthepowerhandlereventandguardthread
hEvtPwrHndlr=NKCreateEvent(NULL,FALSE,FALSE,NULL);
DEBUGCHK(hEvtPwrHndlr);
hTh=CreateKernelThread(PowerHandlerGuardThrd,NULL,THREAD_PWR_GUARD_PRIORITY,0);
HNDLCloseHandle(g_pprcNK,hTh);

//dirtypageevent,initiallyset
hEvtDirtyPage=NKCreateEvent(NULL,FALSE,TRUE,NULL);
DEBUGCHK(hEvtDirtyPage);

//wedontwanttowasteathreadhere(createaseparateforcleaningdirtypages).
//Instead,RunAppsthreadwillbecome"CleanDirtyPage"threadoncefilesysstarted
hTh=CreateKernelThread(RunApps,0,THREAD_RT_PRIORITY_NORMAL,0);
HNDLCloseHandle(g_pprcNK,hTh);

#defineONE_DAY86400000

while(1){
KCall((PKFN)SetThreadBasePrio,pCurThread,dwNKAlarmThrdPrio);
NKWaitForSingleObject(hAlarmThreadWakeup,ONE_DAY);
NKRefreshKernelAlarm();
PageOutIfNeeded();
}
}

这里创建了一个内核线程,处理函数为RunApps,继续跟踪RunApps,其实现在文件C:WINCE600PRIVATEWINCEOSCOREOSNKKERNELkmisc.c中,代码如下:

Code
DWORDWINAPI
RunApps(
LPVOIDparam
)
{
HMODULEhFilesys;
DEBUGMSG(ZONE_ENTRY,(L"RunAppsstartedrn"));

CELOG_LaunchingFilesys();

hFilesys=(HMODULE)NKLoadLibraryEx(L"filesys.dll",MAKELONG(LOAD_LIBRARY_IN_KERNEL,LLIB_NO_PAGING),NULL);

if(hFilesys){
FARPROCpfnMain=GetProcAddressA(hFilesys,(LPCSTR)2);//WinMainoffilesys
HANDLEhFSReady,hTh;

DEBUGCHK(pfnMain);

hFSReady=NKCreateEvent(NULL,TRUE,FALSE,TEXT("SYSTEM/FSReady"));
hTh=CreateKernelThread((LPTHREAD_START_ROUTINE)pfnMain,hFilesys,THREAD_RT_PRIORITY_NORMAL,0);

DEBUGCHK(hTh&&hFSReady);
HNDLCloseHandle(g_pprcNK,hTh);

//IfpSignalStartedisNULL,wedonthavefilesys(tinykern).Dontbotherwaitingforit.
if(pSignalStarted){
NKWaitForSingleObject(hFSReady,INFINITE);

DEBUGCHK(SystemAPISets[SH_FILESYS_APIS]);

//InitializeMUI-Resourceloader(requiresregistry)
InitMUILanguages();

//Readsystemsettingsfromregistry
InitSystemSettings();

//signalfilesysthatweredone
(*pSignalStarted)(0);
}
HNDLCloseHandle(g_pprcNK,hFSReady);

}else{
RETAILMSG(1,(L"Filesysdoesntexist,noappstartedrn"));
}

//insteadofexiting,weremakethisthreadcleaningdirtypagesinthebackground.
CleanPagesInTheBackground();

//shouldveneverreturned
DEBUGCHK(0);
NKExitThread(0);

return0;
}
终于启动filesys.dll了。这个过程简单说明一下,启动filesys.dll后等待其执行的情况,在完成了文件系统的相应的初始化之后,这里继续初始化MUI和系统设置,完成后再通知filesys这边的工作已经完成,filesys继续启动。这一部分的具体内容请参考MSDN,File System Boot Process:http://msdn.microsoft.com/en-us/library/aa912276.aspx。总之,filesys会完成WinCE的最后启动过程,包括gwes.dll和explorer.exe等。至此,WinCE6.0启动完成,如果有LCD且驱动能正常工作,现在就应该能看见可爱的WinCE6.0的界面了。

呵,没想到WinCE6.0的启动过程竟然这么繁长。不过,弄清楚这个启动流程对于移植BSP相当有好处。总结一下整个过程,如下图所示。


本文通过跟踪代码的方式,介绍了WinCE6.0的启动流程。流于表面了一点,很多细节应该进一步研究,以后再慢慢看吧。文中有不确切的地方,也请您不吝赐教.



评论


技术专区

关闭