common.reg

common.reg文件下

[HKEY_LOCAL_MACHINE/SYSTEM/Explorer/Shell Folders]
 "Application Data"=LOC_PATH_APPDATA
 "Desktop"=LOC_PATH_DESKTOP
 "Favorites"=LOC_PATH_FAVORITES
 "Fonts"=LOC_PATH_FONTS
 "My Documents"=LOC_PATH_MYDOCUMENTS
 "Program Files"=LOC_PATH_PROGRAMFILES
 "Programs"=LOC_PATH_PROGRAMS
 "Recent"=LOC_PATH_RECENT
 "StartUp"=LOC_PATH_STARTUP
 "Windows"=LOC_PATH_WINDOWS
; @CESYSGEN ENDIF

 

 

 

1. Startup() -> Startup.s 

位於%_WINCEROOT%/Platform/<Hardware Platform. Name>/Src/Kernel/OAL 目錄下

這個函數是bootloader調轉到run-time鏡像後最先執行的代碼,位於我們的BSP開發包中,由OEM定製.程序最後啓動KernelStart().

2. KernelStart() -> Armtrap.s (Private)

位於 %_WINCEROOT%/Private/Winceos/Coreos/Nk/Kernel/ARM 目錄下

這個函數是內核的Main Entry 點.

    

     3.ARMInit() -> Mdarm.c (Private)


位於%_WINCEROOT%/Private/Winceos/Coreos/Nk/Kernel/ARM 目錄下

這個函數調用4,5來對Main Arm 進行初始化動作.

 

     4.OEMInitDebugSerial() -> Mdarm.c (Private)

 

位置同上.

初始化串口,調用BSP 包中的串口函數(由OEM定製的部分).

 

     5.OEMInit() -> Init.c

%_WINCEROOT%/Platform/<Hardware Platform. Name>/Src/Kernel/OAL

這個位於我們BSP開發包中,由OEM自行定製,不用多說,做過OAL開發的都知道.

 

      6.KernelInit() -> Kwin32.c (Private)

%_WINCEROOT%/Private/Winceos/Coreos/Nk/Kernel 目錄下

該函數調用7,8,9,10函數對內進行初始化動作.

 

     7.HeapInit() -> Heap.c (Private)

 %_WINCEROOT%/Private/Winceos/Coreos/Nk/Kernel 目錄下

初始化內核堆.

 

     8.InitMemoryPool() -> Physmem.c (Private)

%_WINCEROOT%/Private/Winceos/Coreos/Nk/Kernel 目錄下

初始化內核內存池

 

    9.ProcInit() -> Schedule.c (Private)

%_WINCEROOT/Private/Winceos/Coreos/Nk/Kernel

初始化內核進程.

 

    10.SchedInit() -> Schedule.c (Private)

_WINCEROOT/Private/Winceos/Coreos/Nk/Kernel

初始化調度管理器(scheduler) ,創建SystemStatupFunc 線程.

 

    11.FirstSchedule() -> Schedule.c (Private)

%_WINCEROOT/Private/Winceos/Coreos/Nk/Kernel

啓動調度管理器(scheduler), 調用SystemStartupFunc(),IOCTL_HAL_POSTINIT

 

    12.SystemStartupFunc() -> Schedule.c (Private)

%_WINCEROOT/Private/Winceos/Coreos/Nk/Kernel

當所有內核需要的初始化動作完成之後並且當系統準備啓動和運行內核線程,SystemStartupFunc被調用.SystemStartupFunc以IOCTL_HAL_POSTINIT爲參數調用OEMIoControl,OEMIoControl,

     

    13. OEMIoControl,這個我也不多說了,由我們OEM定製.

以上爲wince 基於ARM cpu 的內核啓動的過程.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在Windows CE 6.0中,內核(Kenerl)和OEM代碼被分成oal.exe、kernel.dll和kitl.dll三個部分,其中啓動代碼(startup)和 OAL層的實現部分不再與內核鏈接生成NK.exe,取而代之的是啓動代碼(startup)和硬件相關且獨立於內核的OAL層的實現部分編譯成 oal.exe,而與內核相關且獨立於硬件的OAL層代碼包含在kernel.dll中;內核無關傳輸層(KITL)的支持代碼從OAL層分離出來編譯成 kitl.dll。
    從表面上看,好像只是代碼重新組合了一下,從幫助文檔中BSP的移植過程看好像也是這麼一回事,實際上,整個Windows CE 6.0內核佈局發生了很大的改變。Windows CE 6.0的啓動過程也是如此,如果你想按照Windows CE 5.0的啓動順序去分析Windows CE 6.0的啓動順序,可能會走到一個死衚衕。主要是因爲Windows CE 6.0在啓動過程中調用了kernel.dll和kitl.dll兩個動態鏈接庫的原因,而且Windows CE6.0不再編譯生成KernKitlProf.exe內核文件。
    從Windows CE 6.0的幫助文檔可以看出,WinCE6.0的啓動只與oal.exe和kernel.dll有關,至於kitl.dll,只有將操作系統編譯成具有 KITL功能時纔用到。分析Windows CE 6.0的啓動過程實際上找到編譯oal.exe和kernel.dll的源碼位置。
    首先看一下將WinCE6.0編譯成諸如WinCE5.0所說的基本內核情況,即kern.exe。對於oal.exe源碼位置比較容易找到,因爲 oal.exe是啓動代碼與硬件相關的OAL層實現文件編譯而成,所以只需在BSP的OAL目錄中便能找到。而對於kernel.dll,在BSP目錄結構中,基本上無法找到kernel.dll的編譯文件,所以必須從其他方面着手。
    下面爲WinCE 6.0的編譯日誌輸出文件:makeimg.out在文件複製過程的一部分:
Copying E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/oal.exe   to
E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/nk.exe for debugger
Copying E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/kern.dll   to
E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/kernel.dll for debugger
    從日誌輸出文件可以看出,在文件複製過程中,WinCE6.0編譯器將oal.exe更名爲nk.exe,而將kern.dll文件更名爲 kernel.dll,也就是說,kern.dll文件的實現部分就是kernel.dll的實現體。根據前面的分析,oal.exe是與硬件相關獨立於內核的OAL層的實現部分,而kernel.dll爲內核相關獨立於硬件的OAL層的實現部分。同樣可以從最後整合後的二進制配置文件ce.bib文件中看出端倪。
; @CESYSGEN IF CE_MODULES_NK
nk.exe E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/oal.exe NK SHZ
kitl.dll E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/kitl.dll NK SHZ
kernel.dll E:/WINCE600/OSDesigns/xsbase270/xsbase270/RelDir/XSBase270_ARMV4I_Release/kern.dll NK SHZ
; @CESYSGEN ENDIF
    而kern.dll動態庫在整個Windows CE6.0中沒有顯式編譯過程,即沒有一個sources文件有kern.dll的編譯過程,所以只能從操作系統的編譯文件Makefile中尋找其編譯過程。下面看一下$(_PUBLICROOT)/common/CESYSGEN/makefile中的部分內容:
nk::$(NK_COMPONENTS) $(NK_REPLACE_COMPONENTS)
@copy $(SG_INPUT_LIB)/oemstub.pdb $(SG_OUTPUT_OAKLIB)
@copy $(SG_INPUT_LIB)/oemstub.lib $(SG_OUTPUT_OAKLIB)
set TARGETTYPE=DYNLINK
set TARGETNAME=kern
set RELEASETYPE=OAK
set DLLENTRY=NKStartup
set DEFFILE=NO_DEF_FILE
set TARGETLIBS=
set SOURCELIBS=%%NKLIBS%% $(SG_INPUT_LIB)/nkmain.lib $(SG_INPUT_LIB)/fulllibc.lib
$(MAKECMD) /NOLOGO NOLIBC=1 kern.dll
    從上述代碼中可以發現,原來kern.dll動態庫是從oemstub.lib編譯而來,而且與nkmain.lib有關。
    在理順了上述文件的相互之間的關係之後,再來分析Windows CE 6.0的啓動過程可能就比較容易啦。

    在理清了上述文件的關係之後,便可以分析任意一款基於ARM微處理器的Windows CE 6.0的啓動過程,現在以深圳億道電子技術有限公司開發的基於PXA270 ARM開發平臺爲例,分析Windows CE 6.0操作系統啓動過程。
1、Startup函數:
    從Windows CE 6.0的幫助文檔可以看出,WinCE6.0的啓動只與oal.exe和kernel.dll有關,至於kitl.dll,只有將操作系統編譯成具有 KITL功能時纔用到。分析Windows CE 6.0的啓動過程實際上找到編譯oal.exe和kernel.dll的源碼位置。
oal.exe的通過Startup函數完成硬件的初始化。Startup.s代碼與該硬件平臺的Bootloader啓動代碼共用,其中PreInit 函數主要完成將ARM處理器工作模式切換到管理員模式、同時關閉MMU,並檢測系統啓動原因,如果是熱啓動、即在該函數調用之前已經啓動了 Bootloader程序,相當基本硬件初始化已經完成,則直接跳轉到OALStartUp函數中;否則需要進行硬件中斷屏蔽、內存、系統時鐘頻率、電源管理等硬件的基本初始化過程。(具體過程見代碼的分析)
$(_PLATFORMROOT)/xsbase270/src/common/Startup/Startup.s
LEAF_ENTRY StartUp
    bl PreInit
    tst r10, #RCSR_HARD_RESET
    beq OALStartUp
    tst r10, #RCSR_GPIO_RESET
    bne Continue_StartUp
    bl xlli_mem_init ;初始化內存控制器
    ldr r0, =xlli_PMRCREGS_PHYSICAL_BASE;
    ldr r0, [r0, #xlli_PSPR_offset];
    mov r1, r10;
    bl XllpPmValidateResumeFromSleep;
    cmp r0, #0;
    bne Failed_Sleep_Resume;
    Sleep_Reset
    ldr r0, =xlli_PMRCREGS_PHYSICAL_BASE;
    ldr r0, [r0, #xlli_PSPR_offset];
    mov r1, r10;
    b XllpPmGoToContextRestoration;
    Failed_Sleep_Resume
    ldr r1, =xlli_RCSR_SMR
    bic r10, r10, r1
    Continue_StartUp
    bl xlli_intr_init; ;初始化中斷控制器
    bl EnableClks; ;使能內核時鐘(內存/OS定時器/FFART時鐘之需)
    bl OALXScaleSetFrequencies ;設置系統頻率
    bl xlli_mem_Topt
    bl xlli_mem_restart ;復位內存,使其處於工作模式
    bl xlli_ost_init ;初始化操作系統定時器
    bl xlli_pwrmgr_init ;初始化電源管理
    bl xlli_IMpwr_init ;初始化內部存儲器
    b
ENTRY_END

2、OALStartUp函數:
    在系統硬件初始化完畢之後,Startup調用OALStartUp函數,OALStartUp函數主要完成將OEMAddressTable表傳遞給內核;然後調用KernelStart函數跳轉到內核OEMAddressTable表的主要作用表的每一個入口都定義了一個內存中的物理位置、內存的大小以及映射這物理地址的靜態虛擬地址;
◆靜態虛擬內存地址被定義在緩衝存儲器的範圍之內;
◆內核可以創建非緩衝的內存地址指向到相同的物理地址;
◆對於同一物理地址,既有一個緩衝的虛擬內存地址,也有一個非緩衝的虛擬內存地址;
◆OEMAddressTable最後必須以0結尾;
◆對於MIPS和SHx類型的CPU,物理地址與虛擬地址的映射由CPU完成,無需創建OEMAddressTable
$(_PLATFORMROOT)/xsbase270/src/Inc/ Oemaddrtab_cfg.inc):
ALIGN g_oalAddressTable
    DCD 0x80000000, 0xA0000000,64; XSBASE270: SDRAM (64MB).
    DCD 0x84000000, 0x5C000000,1; BULVERDE: Internal SRAM (64KB bank 0).
    DCD 0x84100000, 0x58000000,1; BULVERDE: Internal memory PM registers.
    DCD 0x84200000, 0x4C000000,1; BULVERDE: USB host controller.
    DCD 0x84300000, 0x48000000,1; BULVERDE: Memory controller.
    DCD 0x84400000, 0x44000000,1; BULVERDE: LCD controller.
    DCD 0x84500000, 0x40000000,32; BULVERDE: Memory-mapped registers
    DCD 0x86500000, 0x00000000,64; XSBASE270: nCS0: Boot Flash (64MB).
    DCD 0x96600000, 0x3C000000,64; BULVERDE: PCMCIA S1 common memory space.
    DCD 0x8A600000, 0x38000000,32; BULVERDE: PCMCIA S1 attribute memory space.
    DCD 0x8C600000, 0x30000000,32; BULVERDE: PCMCIA S1 I/O space.
    DCD 0x8E500000, 0x2C000000,64; BULVERDE: PCMCIA S0 common memory space.
    DCD 0x92500000, 0x28000000,32; BULVERDE: PCMCIA S0 attribute memory space.
    DCD 0x94500000, 0x20000000,32; BULVERDE: PCMCIA S0 I/O space.
    DCD 0x96500000, 0xE0000000,1; XSBASE270: Zero-bank .
    DCD 0x96600000, 0x14000000,1; XSBASE270: nCS5: eXpansion board header.
    DCD 0x96600000, 0x10000000,64; XSBASE270: nCS4: USB2.0/IDE controller.
    DCD 0x9A700000, 0x0C000000,1; XSBASE270: nCS3: SMSC 91C111 Ethernet controller.
    DCD 0x9A800000, 0x0A000000,1; XSBASE270: nCS2 : Board registers (CPLD).
    DCD 0x9A900000, 0x04000000,32; XSBASE270: nCS1: Secondary flash (32MB).
    DCD 0x9F900000, 0x50000000,1; BULVERDE: Camera peripheral interface.
    DCD 0x9FA00000, 0x14700000,1
    DCD 0x00000000, 0x00000000,0;end of table
END


$(_PLATFORMROOT)/xsbase270/src/oal/OalLib/Startup.s
ALIGN
LEAF_ENTRY OALStartUp
    add r0, pc, #g_oalAddressTable - (. + 8)
    mov r11, r0
    b KernelStart
    nop
    nop
    nop
    nop
    nop
    nop
    STALL
    b STALL ;Spin forever.

3、KernelStart函數主要作用:
◆完成OEMAddressTable表中的物理地址到虛擬地址和虛擬地址到物理地址之間的映射;
◆對存儲器頁表和內核參數區存儲空間(RAM或DRAM)進行清零處理。
◆讀出CPU的ID號,內核需要根據該ID決定ARM的MMU處理,因爲ARMV6和ARMV6之前的ARM處理器的MMU處理過程有所區別;
◆設置並開啓MMU和Cache,因爲在Startup函數關閉MMU和Cache;
◆設置ARM處理器工作模式的SP指針,ARM處理器共用7種不同的工作模式(USER、FIQ、IRQ、Supervisor、Abort、 Undefined、System),除用戶模式(USER)和系統模式(System)之外,其他5種工作模式都有具有特定的SP指針寄存器(ARM處理器稱其爲影子寄存器);
◆讀取內核啓動所需要的KDataStruct結構體;
◆調用ARMInit函數重新定位Windows CE內核參數pTOC和初始化OEMInitGlobals全局變量;
◆利用mov pc, r12指令跳轉到kernel.dll的入口位置,即NKStartup函數中。
$(_PRIVATEROOT)WINCEOS/COREOS/NK/LDR/ARM/armstart.s
LEAF_ENTRY KernelStart
    mov r11, r0 ;(r11) = &OEMAddressTable (save pointer)
    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 VaFromPa
    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
    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)
    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
    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
    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
    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
    VirtualStart DCD VStart
ENTRY_END KernelStart

4、ARMInit函數:
    在ARMInit之前,系統仍無法使用全局變量,因爲系統的全局還在ROM區域,對於操作系統而言,出於安全考慮,只有XIP程序纔有讀取ROM區域數據的權利,對於大部分Windows CE 操作系統,只有將數據拷貝到RAM區域後才能進行讀寫,ARMInit函數中通過調用KernelRelocate函數對pTOC全局變量重新定位,定位之後,對內核啓動所需要的KDataStruct結構體進行初始化,其中OEMInitGlobals便是交換oal.exe和kernel.dll之間的全局指針,ARMInit函數返回kernel.dll的入口位置。並在KernelStart函數最後利用mov pc, r12指令跳轉到kernel.dll的入口位置,即NKStartup函數中。
$(_PRIVATEROOT)WINCEOS/COREOS/NK/LDR/ARM/arminit.c
LPVOID ARMInit (struct KDataStruct *pKData)
{
/* Initialize kernel globals */
    KernelRelocate (pTOC);
/* The only argument passed to the entry point of kernel.dll is the address */
/* of KData, we need to put everything we need to pass to in in KData. */
    pKData->dwTOCAddr = (DWORD) pTOC;
    pKData->dwOEMInitGlobalsAddr = (DWORD) OEMInitGlobals;
    SetOsAxsDataBlockPointer(pKData);
    return FindKernelEntry (pTOC);
}

5、NKStartup函數:
    硬件平臺初始化完成後,oal.exe的啓動任務基本完成,餘下的啓動工作由內核相關且獨立於內核的OAL層實現體kernel.dll接管。kernel.dll主要作用:
◆從結構體參數KDataStruct * pKData提取內核啓動時所必須的全局變量,同時初始化內核全局變量;
◆定位對Windows CE 6.0特有的OEMGLOBAL結構體的初始化函數OEMInitGlobals地址,該結構體構建了內核和OAL層之間進行通信的橋樑。在 OEMGLOBAL結構體定義了OAL層所必須的函數,該結構體在oemglobal.c文件中被初始化,並被編譯在OEMMain.lib和 OEMMain_StaticKITL.lib兩個庫中,如果OAL鏈接這兩個庫,則必須要有該結構體中函數實現體;
◆通過調用ARMSetup設置物理地址和非緩衝的虛擬內存地址的映射、ARM中斷向量以及內核模式所需要的堆棧。
◆調用OEMInitDebugSerial函數初始化調試串口;
◆調用OEMInit進行平臺初始化;
    需要注意的時,NKStartup函數調用OEMInitDebugSerial和OEMInit函數的過程與Windows CE 6.0之前的版本完全不同,這是因爲在Windows CE 6.0以前的版本中,由於內核(kernel)、OAL和KITL編譯在一個可執行的文件中,它們之間的共享變量只需簡單利用extern關鍵字申明便可相互之間進行訪問,而在Windows CE 6.0中,由於內核(kernel)、OAL和KITL被編譯成不同的可執行文件,變量之間的相互訪問無法使用extern關鍵字實現共享,即內核無法使用extern DWORD varX方法訪問OAL層的變量varX,當然OAL層的實現體同樣無法通過同樣的方式訪問內核變量。爲實現內核和OAL訪問共享信息,Windows CE 6.0定義了OEMGLOBAL和GLOBAL兩個結構體。
    在 Windows CE 6.0的內核啓動時,OS找到OAL的入口位置,然後調用入口函數與全局塊進行指針交換,這樣內核才能使用OAL層中的信息,同樣OAL層才能訪問內核(kernel)導出的函數。
所以上述兩個函數的調用實際上通過OEMGLOBAL結構體實現的。實際調用位置爲$(_PRIVATEROOT)/winceos/coreos/nk /oemstub/oemstub.c中的OEMInitDebugSerial和OEMInit,這兩個函數中通過OEMGLOBAL結構體指針訪問 OAL層中的OEMInitDebugSerial和OEMInit。
    調用KernelFindMemory()函數分割RAM區域,在Windows CE操作系統中,RAM空間主要分爲存儲內存和程序內存,存儲內存主要爲文件的存儲空間,包括內核文件和複製到系統中所有目標文件,程序內存爲運行程序時所需要的存儲空間。
◆KernelStart ()啓動內核。
$(_PRIVATEROOT)/WINCEOS/COREOS/NK/KERNEL/ARM/mdarm.c
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;
} 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 ();
    // (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);
}

6、KernelSstart函數:
    這裏的KernelStart函數與前面的KernelStart函數的屬於兩個完全不同的函數,NKStartup函數中調用的KernelStart 函數爲$(_PRIVATEROOT)/WINCEOS/COREOS/NK/KERNEL/ARM/armtrap.s文件中的KernelStart 函數,主要完成調用內核初始化函數KernelInit,並跳轉到操作系統的第一個啓動的任務。
LEAF_ENTRY KernelStart
    ldr r4, =KData ; (r4) = ptr to KDataStruct
    ldr r0, =APIRet
    str r0, [r4, #pAPIReturn] ; set API return address
    mov r1, #SVC_MODE
    msr cpsr_c, r1 ; switch to Supervisor Mode w/IRQs enabled
    CALL KernelInit ; initialize scheduler, etc.
    mov r0, #0 ; no current thread
    mov r1, #ID_RESCHEDULE
    b FirstSchedule
ENTRY_END

7、KernelInit函數:
    Windows CE 6.0的內核初始化函數同其他版本的內核初始化函數基本相近,主要完成在啓動第一個線程前對內核進行初始化,主要包括API函數集初始化、堆的初始化、初始化內存池、進程初始化、線程初始化和文件映射初始化等操作。
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 threadsv
    MapfileInit ();
#ifdef DEBUG
    g_pNKGlobal->pfnWriteDebugString (TEXT("Scheduling the first thread./r/n"));
#endif
}

8、FirstSchedule:
    FirstSchedule函數爲Windows CE操作系統啓動過程中最後無條件跳轉的一個函數,windows CE進行第一個調度,實際爲一個空閒線程,因爲windows CE系統還沒有完成啓動,只有當windows CE完全啓動並進入穩定狀態,然後啓動文件系統filesys.dll,設備管理device.dll,窗體圖像子系統gews.dll和shell程序 explore.exe。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章