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

 

通过前两篇文章的介绍,我们已经知道NBOOT用来引导EBOOT,继而EBOOT加载并引导WinCE操作系统(NK)。那么,WinCE6.0的启动过程又是怎样的呢?本文基于S3C2410的平台做一个详细的分析。需要说明的是,WinCE6.0的整个启动过程对于同一类型的MCU来说大同小异,如S3C2410PXA270同属ARM平台的MCU,所以他们的启动过程是类似的,可以说唯一的不同就在OAL处,而WinCE操作系统的启动正是从OAL开始的。
     
OALOEM 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函数保持一致,OALKernel的接口由共享结构NKGLOBAL实现。这一部分的具体内容下一篇再做介绍。下图所示为WinCE6.0OAL设计。
     S3C2410下WinCE6.0的启动过程详解_第1张图片
 

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


LEAF_ENTRY Launch

    ldr    r2, 
= PhysicalStart
    ldr     r3, 
= (VIR_RAM_START - PHY_RAM_START)

    sub     r2, r2, r3

    mov     r1, #
0x0070             ; Disable MMU
    mcr     p15, 
0, r1, c1, c0, 0
    nop
    mov     pc, r2                  ; Jump to PStart
    nop

    ; MMU 
& caches now disabled.

PhysicalStart

    mov     r2, #
0
    mcr     p15, 
0, r2, c8, c7, 0   ; Flush the TLB
    mov     pc, r0            ; Jump to program we are launching.

    函数Launch()的参数为物理地址,因为在跳转之前已将MMU关闭。该地址可通过VIEWBIN来查看,如下图所示:
     
S3C2410下WinCE6.0的启动过程详解_第2张图片
     如何确定这个地址对应的是
NK.bin中的哪一个文件呢,先前说是OAL.exe,证据何在。在PB6.0中增加了浏览NK.bin的功能,我们可以利用此功能查看NK.bin的详细情况,如下图所示:     

 

S3C2410下WinCE6.0的启动过程详解_第3张图片
     从上图中可以看出
0x80205394处对应的是NK.exe,而这里的NK.exe即为OAL.exe
     
至此,我们已经知道EBOOT是如何跳转到OAL.exe中的了。接下来继续看OAL.exe的执行过程。
     
OAL的启动代码如下:OAL的启动代码和EBOOT的启动代码经常复用,但为了代码的简洁,最好还是分开实现,而且在EBOOT中如果已经初始化了相关硬件,那么OAL的启动代码就可以省去那部分工作,可以很简练,如上面的代码所示。


LEAF_ENTRY StartUp

        ; Compute the OEMAddressTable
's physical address and 
        ; load it into r0. KernelStart expects r0 to contain
        ; the physical address of 
this table. The MMU isn'
        ; turned on until well into KernelStart.  

        add     r0, pc, #g_oalAddressTable 
- (. + 8)
        bl      KernelStart

 

可以看出,OAL的启动代码又调用了函数KernelStart(),而这个函数是在文件C:/WINCE600/PRIVATE/WINCEOS/COREOS/NK/LDR/ARM/armstart.s中实现的,代码如下:从上面的代码可以看出,KernelStart()通过OEMAddressTable初始化了MMU,然后通过调用函数ARMInit()获得kernel.dll的入口点,最后跳转到kernel.dll的入口点处。


LEAF_ENTRY KernelStart

        mov     r11, r0                         ; (r11) 
= &OEMAddressTable (save pointer)

        ; figure 
out the virtual address of OEMAddressTable
        mov     r1, r11                         ; (r1) 
= &OEMAddressTable (2nd argument to VaFromPa)
        bl      VaFromPa
        mov     r6, r0                          ; (r6) 
= VA of OEMAddressTable

        ; convert 
base of PTs to Physical address
        ldr     r4, 
=PTs                        ; (r4) = virtual address of FirstPT
        mov     r0, r4                          ; (r0) 
= virtual address of FirstPT
        mov     r1, r11                         ; (r1) 
= &OEMAddressTable (2nd argument to PaFromVa)
        bl      PaFromVa

        mov     r10, r0                         ; (r10) 
= ptr to FirstPT (physical)

;       Zero 
out page tables & kernel data page

        mov     r0, #
0                          ; (r0-r3) = 0's to store
        mov     r1, #0
        mov     r2, #
0
        mov     r3, #
0
        mov     r4, r10                         ; (r4) 
= first address to clear
        add     r5, r10, #KDEnd
-PTs             ; (r5) = last address + 1
18      stmia   r4!, {r0-r3}
        stmia   r4
!, {r0-r3}
        cmp     r4, r5
        blo     
%B18

        ; read the architecture information
        bl      GetCpuId
        mov     r5, r0 LSR #
16                  ; r5 >>= 16
        and     r5, r5, #
0x0000000f             ; r5 &= 0x0000000f == architecture id
        
;       Setup 2nd level page table to map the high memory area which contains the
; first level page table, 2nd level page tables, kernel data page, etc.
;       (r5) 
= architecture id

        add     r4, r10, #HighPT
-PTs            ; (r4) = ptr to high page table

        cmp     r5, #ARMv6                      ; v6 or later
?
; ARMV6_MMU
        orrge   r0, r10, #PTL2_KRW 
+ PTL2_SMALL_PAGE + ARMV6_MMU_PTL2_SMALL_XN
                                                ; (r0) 
= PTE for 4K, kr/w u-/- page, uncached unbuffered, nonexecutable
; PRE ARMV6_MMU
        orrlt   r0, r10, #PTL2_KRW 
+ (PTL2_KRW << 2+ (PTL2_KRW << 4+ (PTL2_KRW << 6)
                                                ; Need to replicate AP bits into all 
4 fields
        orrlt   r0, r0,  #PTL2_SMALL_PAGE 
+ PREARMV6_MMU_PTL2_SMALL_XN
                                                ; (r0) 
= PTE for 4K, kr/w u-/- page, uncached unbuffered, nonexecutable
        str     r0, [r4, #
0xD0*4]               ; store the entry into 4 slots to map 16K of primary page table
        add     r0, r0, #
0x1000                 ; step on the physical address
        str     r0, [r4, #
0xD1*4]
        add     r0, r0, #
0x1000                 ; step on the physical address
        str     r0, [r4, #
0xD2*4]
        add     r0, r0, #
0x1000                 ; step on the physical address
        str     r0, [r4, #
0xD3*4]

        add     r8, r10, #ExceptionVectors
-PTs  ; (r8) = ptr to vector page
        orr     r0, r8, #PTL2_SMALL_PAGE        ; construct the PTE (C
=B=0)

;; The exception stacks and the vectors are mapped 
as a single kr/w page.
;; Any alternative will use more physical memory.
;; Multiple mappings don
't provide any real protection: if the vectors were in a r/o page,
;; they could still be corrupted via the kr/w setting required for the stacks.
        cmp     r5, #ARMv6                      ; v6 or later
?
; ARMV6_MMU 
        orrge   r0, r0, #PTL2_KRW
; PRE ARMV6_MMU
        orrlt   r0, r0, #PTL2_KRW 
+ (PTL2_KRW << 2+ (PTL2_KRW << 4+ (PTL2_KRW << 6)
                                                ; Need to replicate AP bits into all 
4 fields for pre-V6 MMU

        str     r0, [r4, #
0xF0*4]               ; store entry for exception stacks and vectors
                                                ; other 
3 entries now unused

        add     r9, r10, #KPage
-PTs             ; (r9) = ptr to kdata page
        orr     r0, r9, #PTL2_SMALL_PAGE        ; (r0)
=PTE for 4K (C=B=0)
        
; ARMV6_MMU (condition codes still 
set)
        orrge   r0, r0, #PTL2_KRW_URO           ; No subpage access control, so we must 
set this all to kr/w+ur/o
; PRE ARMV6_MMU
        orrlt   r0, r0, #(PTL2_KRW 
<< 0+ (PTL2_KRW << 2+ (PTL2_KRW_URO << 4)
                                                ; (r0) 
= set perms kr/w kr/w kr/w+ur/o r/o
        str     r0, [r4, #
0xFC*4]               ; store entry for kernel data page
        orr     r0, r4, #PTL1_2Y_TABLE          ; (r0) 
= 1st level PTE for high memory section
        add     r1, r10, #
0x4000
        str     r0, [r1, #
-4]                   ; store PTE in last slot of 1st level table

;       Fill 
in first level page table entries to create "statically mapped" regions
; from the contents of the OEMAddressTable array.
;
;       (r5) 
= architecture id
;       (r9) 
= ptr to KData page
;       (r10) 
= ptr to 1st level page table
;       (r11) 
= ptr to OEMAddressTable array

        add     r10, r10, #
0x2000               ; (r10) = ptr to 1st PTE for "unmapped space"

        mov     r0, #PTL1_SECTION
        orr     r0, r0, #PTL1_KRW               ; (r0)
=PTE for 0: 1MB (C=B=0, kernel r/w)
20      mov     r1, r11                         ; (r1) = ptr to OEMAddressTable array (physical)


25      ldr     r2, [r1], #4                    ; (r2) = virtual address to map Bank at
        ldr     r3, [r1], #
4                    ; (r3) = physical address to map from
        ldr     r4, [r1], #
4                    ; (r4) = num MB to map

        cmp     r4, #
0                          ; End of table?
        beq     
%F29

        ldr     r12, 
=0x1FF00000
        and     r2, r2, r12                      ; VA needs 512MB, 1MB aligned.

        ldr     r12, 
=0xFFF00000
        and     r3, r3, r12                      ; PA needs 4GB, 1MB aligned.

        add     r2, r10, r2, LSR #
18
        add     r0, r0, r3                      ; (r0) 
= PTE for next physical page

28      str     r0, [r2], #4
        add     r0, r0, #
0x00100000             ; (r0) = PTE for next physical page

        sub     r4, r4, #
1                      ; Decrement number of MB left
        cmp     r4, #
0
        bne     
%B28                            ; Map next MB

        bic     r0, r0, #
0xF0000000             ; Clear Section Base Address Field
        bic     r0, r0, #
0x0FF00000             ; Clear Section Base Address Field
        b       
%B25                            ; Get next element


29
        sub     r10, r10, #
0x2000               ; (r10) = restore address of 1st level page table

        ; The minimal page mappings are setup. Initialize the MMU and turn it on.

        ; there are some CPUs with pipeline issues that requires identity mapping before turning on MMU.
        ; We
'll create an identity mapping for the address we'll jump to when turning on MMU on and remove
        ; the mapping after we turn on MMU and running on Virtual address.
        

        ldr     r12, 
=0xFFF00000                ; (r12) = mask for section bits
        and     r1, pc, r12                     ; physical address of 
where we are 
                                                ; NOTE: we assume that the KernelStart function never spam across 1M boundary.
        orr     r0, r1, #PTL1_SECTION
        orr     r0, r0, #PTL1_KRW               ; (r0) 
= PTE for 1M for current physical address, C=B=0, kernel r/w
        add     r7, r10, r1, LSR #
18            ; (r7) = 1st level PT entry for the identity map
        ldr     r8, [r7]                        ; (r8) 
= saved content of the 1st-level PT
        str     r0, [r7]                        ; create the identity map

        mov     r1, #
1
        mtc15   r1, c3                          ; Setup access to domain 
0 and clear other
        mtc15   r10, c2                         ; setup translation 
base (physical of 1st level PT)

        mov     r0, #
0
        mcr     p15, 
0, r0, c8, c7, 0           ; Flush the I&D TLBs

        mfc15   r1, c1
        orr     r1, r1, #
0x007F                 ; changed to read-mod-write for ARM920 Enable: MMU, Align, DCache, WriteBuffer

        cmp     r5, #ARMv6                      ; r5 still 
set        
; ARMV6_MMU
        orrge   r1, r1, #
0x3000                 ; vector adjust, ICache
        orrge   r1, r1, #
1<<23                  ; V6-format page tables
        orrge   r1, r1, #ARMV6_U_BIT            ; V6
-set U bit, let A bit control unalignment support
; PRE ARMV6_MMU
        orrlt   r1, r1, #
0x3200                 ; vector adjust, ICache, ROM protection

        ldr     r0, VirtualStart
        cmp     r0, #
0                          ; make sure no stall on "mov pc,r0" below
        mtc15   r1, c1                          ; enable the MMU 
& Caches
        mov     pc, r0                          ;  
& jump to new virtual address
        nop

; MMU 
& caches now enabled.
;
;       (r10) 
= physcial address of 1st level page table
;       (r7)  
= entry in 1st level PT for identity map
;       (r8)  
= saved 1st level PT save at (r7)
VStart  ldr     r2, 
=FirstPT                    ; (r2) = VA of 1st level PT
        sub     r7, r7, r10                     ; (r7) 
= offset into 1st-level PT
        str     r8, [r2, r7]                    ; restore the temporary identity map
        mcr     p15, 
0, r0, c8, c7, 0           ; Flush the I&D TLBs

;
; setup stack 
for each modes: current mode = supervisor mode
;
        ldr     sp, 
=KStack
        add     r4, sp, #KData
-KStack           ; (r4) = ptr to KDataStruct

        ; setup ABORT stack
        mov     r1, #ABORT_MODE:OR:
0xC0
        msr     cpsr_c, r1                      ; 
switch to Abort Mode w/IRQs disabled
        add     sp, r4, #AbortStack
-KData

        ; setup IRQ stack
        mov     r2, #IRQ_MODE:OR:
0xC0
        msr     cpsr_c, r2                      ; 
switch to IRQ Mode w/IRQs disabled
        add     sp, r4, #IntStack
-KData

        ; setup FIQ stack
        mov     r3, #FIQ_MODE:OR:
0xC0
        msr     cpsr_c, r3                      ; 
switch to FIQ Mode w/IRQs disabled
        add     sp, r4, #FIQStack
-KData

        ; setup UNDEF stack
        mov     r3,  #UNDEF_MODE:OR:
0xC0
        msr     cpsr_c, r3                      ; 
switch to Undefined Mode w/IRQs disabled
        mov     sp, r4                          ; (sp_undef) 
= &KData

        ; 
switch back to Supervisor mode
        mov     r0, #SVC_MODE:OR:
0xC0
        msr     cpsr_c, r0                      ; 
switch to Supervisor Mode w/IRQs disabled
        ldr     sp, 
=KStack

        ; 
continue initialization in C
        add     r0, sp, #KData
-KStack           ; (r0) = ptr to KDataStruct
        str     r6, [r0, #pAddrMap]             ; store VA of OEMAddressTable 
in KData
        bl      ARMInit          ; call C function to perform the rest of initializations
        ; upon 
return, (r0) = entry point of kernel.dll

        mov     r12, r0
        ldr     r0, 
=KData
        mov     pc, r12     ; jump to entry of kernel.dll

 

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

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


//
// NKStartup - entry point of kernel.dll.
//
// NK Loader setup only the minimal mappings, which includes ARMHigh area, and the cached static mapping area,
// with *EVERYTHING UNCACHED*. Interrupt vectors are not setup either. So, the init sequence reqiures:
// (1) pickup data passed from nk loader
// (2) Find entry point of oal, exchange globals, find out the cache mode.
// (3) fill in the rest of static mapped area (0xa0000000 - 0xbfffffff), PSL faulting address, interrupt vectors,
//     mod stacks, etc. Then, change the 'cached' static mapping area to use cache, and flush I&D TLB.
// (4) continue normal loading of kernel (find KITLdll, call OEMInitDebugSerial, etc.)
//
void NKStartup (struct KDataStruct * pKData)
{
    PFN_OEMInitGlobals pfnInitGlob;
    PFN_DllMain pfnKitlEntry;
    DWORD dwCpuId 
= GetCpuId ();

    
// (1) pickup arguments from the nk loader
    g_pKData            = pKData;
    pTOC                
= (const ROMHDR *) pKData->dwTOCAddr;
    g_pOEMAddressTable  
= (PADDRMAP) pKData->pAddrMap;

    
/* get architecture id and update page protection attributes */
    pKData
->dwArchitectureId = (dwCpuId >> 16& 0xf;
    
if (pKData->dwArchitectureId >= ARMArchitectureV6) {
        
// v6 or later
        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;
    }

    
// initialize nk globals
    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) find entry of oal
    pfnInitGlob = (PFN_OEMInitGlobals) pKData->dwOEMInitGlobalsAddr;

    
// no checking here, if OAL entry point doesn't exist, we can't continue
    g_pOemGlobal = pfnInitGlob (g_pNKGlobal);
    g_pOemGlobal
->dwMainMemoryEndAddress = pTOC->ulRAMEnd;
    pKData
->pOem = g_pOemGlobal;

    
// setup globals
    pVMProc         = g_pprcNK;
    pActvProc       
= g_pprcNK;

    g_pNKGlobal
->pfnWriteDebugString = g_pOemGlobal->pfnWriteDebugString;

    
// (3) setup vectors, UC mappings, mode stacks, etc.
    ARMSetup ();

    
//
    
// cache is enabled from here on
    
//

    
// (4) common startup code.

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

#ifdef DEBUG
    CurMSec 
= dwPrevReschedTime = (DWORD) -200000;      // ~3 minutes before wrap
#endif

    OEMInitDebugSerial ();

    
// debugchk only works after we have something to print to.
    DEBUGCHK (pKData == (struct KDataStruct *) PUserKData);
    DEBUGCHK (pKData 
== &ArmHigh->kdata);

    OEMWriteDebugString ((LPWSTR)NKSignon);

    
/* Copy interlocked api code into the kpage */
    DEBUGCHK(
sizeof(struct KDataStruct) <= FIRST_INTERLOCK);
    DEBUGCHK((InterlockedEnd
-InterlockedAPIs)+FIRST_INTERLOCK <= 0x400);
    memcpy((
char *)g_pKData+FIRST_INTERLOCK, InterlockedAPIs, InterlockedEnd-InterlockedAPIs);

    
/* setup processor version information */
    CEProcessorType     
= (dwCpuId >> 4& 0xFFF;
    CEProcessorLevel    
= 4;
    CEProcessorRevision 
= (WORD) dwCpuId & 0x0f;
    CEInstructionSet    
= PROCESSOR_ARM_V4I_INSTRUCTION;

    RETAILMSG (
1, (L"ProcessorType=%4.4x  Revision=%d/r/n", CEProcessorType, CEProcessorRevision));
    RETAILMSG (
1, (L"OEMAddressTable = %8.8lx/r/n", g_pOEMAddressTable));

    OEMInit();          
// initialize firmware

    
// flush I&D TLB
    OEMCacheRangeFlush (NULL, 0, CACHE_SYNC_FLUSH_TLB);

    KernelFindMemory();

    DEBUGMSG (
1, (TEXT("NKStartup done, starting up kernel./r/n")));

    KernelStart ();

    
// never returned
    DEBUGCHK (0);
}

 


//------------------------------------------------------------------------------
// KernelInit - Kernel initialization before scheduling the 1st thread
//------------------------------------------------------------------------------

void KernelInit (void
{
#ifdef DEBUG
    g_pNKGlobal
->pfnWriteDebugString (TEXT("Windows CE KernelInit/r/n"));
#endif
    APICallInit ();         
// setup API set
    HeapInit ();            // setup kernel heap
    InitMemoryPool ();      // setup physical memory
    PROCInit ();            // initialize process
    VMInit (g_pprcNK);      // setup VM for kernel
    THRDInit ();            // initialize threads
    MapfileInit ();

#ifdef DEBUG
    g_pNKGlobal
->pfnWriteDebugString (TEXT("Scheduling the first thread./r/n"));
#endif
}

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


//------------------------------------------------------------------------------
// THRDInit - initialize thread handling (called at system startup)
//------------------------------------------------------------------------------
void THRDInit (void
{
    LPBYTE      pStack;

    DEBUGLOG (
1, g_pprcNK);

    
// don't allow thread create one memory drop below 1% available
    if (g_cMinPageThrdCreate < PageFreeCount / 100) {
        g_cMinPageThrdCreate 
= PageFreeCount / 100;
    }
    
    
// map W32 thread priority if OEM choose to
    if (g_pOemGlobal->pfnMapW32Priority) {
        BYTE prioMap[MAX_WIN32_PRIORITY_LEVELS];
        
int  i;
        memcpy (prioMap, W32PrioMap, 
sizeof (prioMap));
        g_pOemGlobal
->pfnMapW32Priority (MAX_WIN32_PRIORITY_LEVELS, prioMap);
        
// validate the the priority is mono-increase
        for (i = 0; i < MAX_WIN32_PRIORITY_LEVELS-1; i ++) {
            
if (prioMap[i] >= prioMap[i+1])
                
break;
        }

        DEBUGMSG ((MAX_WIN32_PRIORITY_LEVELS
-1!= i, (L"ProcInit: Invalid priority map provided by OEM, Ignored!/r/n"));
        
if ((MAX_WIN32_PRIORITY_LEVELS-1== i) {
            memcpy (W32PrioMap, prioMap, 
sizeof (prioMap));
        }
    }

    
// allocate memory for the 1st thread
    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);

        
// check the debug register related values.
        if (g_pOemGlobal->cbCoProcRegSize > MAX_COPROCREGSIZE) {
            g_pOemGlobal
->cbCoProcRegSize = g_pOemGlobal->fSaveCoProcReg = 0;
        } 
else {
            PNAME pTmp 
= AllocName (g_pOemGlobal->cbCoProcRegSize);
            DEBUGCHK (pTmp);
            g_dwCoProcPool 
= pTmp->wPool;
            FreeName (pTmp);
        }
    } 
else {
        g_pOemGlobal
->fSaveCoProcReg = FALSE;
    }
    DEBUGMSG (ZONE_SCHEDULE,(TEXT(
"cbCoProcRegSize = %d/r/n"), g_pOemGlobal->cbCoProcRegSize));

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


#ifdef SHx
    SetCPUGlobals();
    OEMCacheRangeFlush (
00, CACHE_SYNC_ALL);
#endif


    
if (!OpenExecutable (NULL, TEXT("NK.EXE"), &g_pprcNK->oe, TOKEN_SYSTEM, NULL, 0)) {
        LoadE32 (
&g_pprcNK->oe, &g_pprcNK->e32, 000);
        g_pprcNK
->BasePtr = (LPVOID)g_pprcNK->e32.e32_vbase;
        UpdateKmodVSize(
&g_pprcNK->oe, &g_pprcNK->e32);
    }
    
    
// create/setup stack
    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;

    
// Save off the thread's program counter for getting its name later.
    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: Created master thread %8.8lx/r/n"),pCurThread));

}

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


//------------------------------------------------------------------------------
void
SystemStartupFunc(
    
ulong param
    )
{
    HANDLE hTh;

    
// record PendEvent address for SetInterruptEvent
    KInfoTable[KINX_PENDEVENTS] = (DWORD) &PendEvents1;

    KernelInit2();

    
// adjust alarm resolution if it it's not in bound
    if (g_pOemGlobal->dwAlarmResolution < MIN_NKALARMRESOLUTION_MSEC)
        g_pOemGlobal
->dwAlarmResolution = MIN_NKALARMRESOLUTION_MSEC;
    
else if (g_pOemGlobal->dwAlarmResolution > MAX_NKALARMRESOLUTION_MSEC)
        g_pOemGlobal
->dwAlarmResolution = MAX_NKALARMRESOLUTION_MSEC;
    
    VERIFY (LoaderInit ());
    
    
// initialize the compiler /GS cookie - this must happen before other threads
    
// start running
    __security_init_cookie();

    PagePoolInit ();

    
// This can only be done after the loader initialization
    LoggerInit();           // Initialization for CeLog, profiler, code-coverage, etc.
    SysDebugInit ();        // initialize System Debugger (HW Debug stub, Kernel dump capture, SW Kernel Debug stub)

    
// do this now, so that we continue running after we've created the new thread
#ifdef START_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);

    
// Give the OEM a final chance to do a more full-featured init before any
    
// apps are started
    KernelIoctl (IOCTL_HAL_POSTINIT, NULL, 0, NULL, 0, NULL);

    InitMsgQueue ();
    InitWatchDog ();

    
// create the power handler event and guard thread
    hEvtPwrHndlr = NKCreateEvent (NULL, FALSE, FALSE, NULL);
    DEBUGCHK (hEvtPwrHndlr);
    hTh 
= CreateKernelThread (PowerHandlerGuardThrd, NULL, THREAD_PWR_GUARD_PRIORITY, 0);
    HNDLCloseHandle (g_pprcNK, hTh);

    
// dirty page event, initially set
    hEvtDirtyPage = NKCreateEvent (NULL, FALSE, TRUE, NULL);
    DEBUGCHK (hEvtDirtyPage);

    
// we don't want to waste a thread here (create a separate for cleaning dirty pages).
    
// Instead, RunApps thread will become "CleanDirtyPage" thread once filesys started
    hTh = CreateKernelThread (RunApps,0,THREAD_RT_PRIORITY_NORMAL,0);
    HNDLCloseHandle (g_pprcNK, hTh);

#define ONE_DAY     86400000

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

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


DWORD WINAPI
RunApps(
    LPVOID param
    )
{
    HMODULE hFilesys;
    DEBUGMSG (ZONE_ENTRY, (L
"RunApps started/r/n"));

    CELOG_LaunchingFilesys();

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

    
if (hFilesys) {
        FARPROC pfnMain 
= GetProcAddressA (hFilesys, (LPCSTR) 2);   // WinMain of filesys
        HANDLE hFSReady, 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);

        
// If pSignalStarted is NULL, we don't have filesys (tinykern). Don't bother waiting for it.
        if (pSignalStarted) {
            NKWaitForSingleObject (hFSReady, INFINITE);

            DEBUGCHK (SystemAPISets[SH_FILESYS_APIS]);

            
// Initialize MUI-Resource loader (requires registry)
            InitMUILanguages();

            
// Read system settings from registry
            InitSystemSettings ();

            
// signal filesys that we're done
            (* pSignalStarted) (0);
        }
        HNDLCloseHandle (g_pprcNK, hFSReady);
   
    } 
else {
        RETAILMSG (
1, (L"Filesys doesn't exist, no app started/r/n"));
    }

    
// instead of exiting, we're make this thread cleaning dirty pages in the background.
    CleanPagesInTheBackground ();

    
// should've never returned
    DEBUGCHK (0);
    NKExitThread (
0);

    
return 0;
}

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

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

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

你可能感兴趣的:(S3C2410下WinCE6.0的启动过程详解)