WindowsCE是微軟公司嵌入式、移動計(jì)算平臺的基礎(chǔ),,它是一個(gè)開放的,、可升級的32位嵌入式操作系統(tǒng),,是基于掌上型電腦類的電子設(shè)備操作系統(tǒng),它是精簡的Windows 95,Windows CE的圖形用戶界面相當(dāng)出色,。
Windows CE作業(yè)系統(tǒng)是Windows家族中最新的成員,,專門設(shè)計(jì)給掌上型電腦(HPCs)所使用的電腦環(huán)境。這樣的作業(yè)系統(tǒng)可使完整的可攜式技術(shù)與現(xiàn)有的Windows桌面技術(shù)整合工作,。Windows CE 被設(shè)計(jì)成針對小型設(shè)備(它是典型的擁有有限內(nèi)存的無磁盤系統(tǒng))的通用操作系統(tǒng),,Windows CE 可以通過設(shè)計(jì)一層位于內(nèi)核和硬件之間代碼來用設(shè)定硬件平臺,這即是眾所周知的硬件抽象層(HAL),。不象其它的微軟 Windows 操作系統(tǒng),,Windows CE 并不是代表一個(gè)標(biāo)準(zhǔn)的相同的對所有平臺適用的軟件。為了足夠靈活以達(dá)到適應(yīng)廣泛產(chǎn)品需求,, Windows CE 采用標(biāo)準(zhǔn)模式,,這就意味著,它能夠由一系列軟件模式做出選擇,,從而使產(chǎn)品定制,。另外,一些可利用模式也可作為其組成部分,,這意味著這些模式能夠通過從一套可利用的組份做出選擇,,從而成為標(biāo)準(zhǔn)模式,通過選擇,,能夠達(dá)到系統(tǒng)要求的最小模式,, OEM 能夠減少存儲腳本和操作系統(tǒng)的運(yùn)行。
Windows CE中的C代表袖珍(Compact),、消費(fèi)(CONsumer),、通信能力(Connectivity)和伴侶(Companion);E代表電子產(chǎn)品(Electronics),。與Windows 95/98,、Windows NT不同的是,Windows CE是所有源代碼全部由微軟自行開發(fā)的嵌入式新型操作系統(tǒng),,其操作界面雖來源于Windows 95/98,但Windows CE是基于WIN32 API重新開發(fā),、新型的信息設(shè)備的平臺。Windows CE具有模塊化,、結(jié)構(gòu)化和基于Win32應(yīng)用程序接口和與處理器無關(guān)等特點(diǎn),。Windows CE不僅繼承了傳統(tǒng)的Windows圖形界面,并且在Windows CE平臺上可以使用Windows 95/98上的編程工具(如Visual Basic,、Visual C++等),、使用同樣的函數(shù)、使用同樣的界面風(fēng)格,使絕大多數(shù)的應(yīng)用軟件只需簡單的修改和移植就可以在Windows CE平臺上繼續(xù)使用,。Windows CE并非是專為單一裝置設(shè)計(jì)的,,所以微軟為旗下采用Windows CE作業(yè)系統(tǒng)的產(chǎn)品大致分為三條產(chǎn)品線,Pocket PC(掌上電腦),、Handheld PC(手持設(shè)備)及Auto PC.
我們的嵌入式系統(tǒng)用的是ARM處理器+WinCE平臺,,目的是要把WinCE平臺從舊版本移植到WinCE6.0平臺上。但結(jié)果是這個(gè)WinCE系統(tǒng)在啟動的時(shí)候經(jīng)常會出現(xiàn)失敗,,而且每次失敗的原因都莫明其妙和不盡相同,。
為什么會出現(xiàn)這種情況呢?經(jīng)過分析和研究,,原來主因是系統(tǒng)的引導(dǎo)過程,、內(nèi)核加載過程、OAL啟動過程和硬件驅(qū)動加載過程時(shí)都存在可能導(dǎo)致的失敗的隱憂,。本文通過對以上因素進(jìn)行分析,,并提出相應(yīng)的解決辦法。但由于WinCE啟動失敗會非常取決于硬件平臺,,因此在具體應(yīng)用時(shí)需要綜合考慮和分析,。
一 什么是WinCE啟動過程?
WinCE系統(tǒng)在啟動時(shí)一般需要三個(gè)基本元素:引導(dǎo)初始化,、內(nèi)核加載和OAL初始化等,。它們的作用是要完成引導(dǎo)過程的初始化和操作系統(tǒng)執(zhí)行環(huán)境的初始化。其中引導(dǎo)初始化是由引導(dǎo)工具BootLoader完成,,主要是完成板級,、片級的初始化。例如,,通過設(shè)置寄存器來完成硬件的初始化,如設(shè)置時(shí)鐘,、設(shè)置中斷控制寄存器,、完成內(nèi)存映射和初始化MMU的工作方式等。內(nèi)核加載是指將操作系統(tǒng)內(nèi)核映像從只讀存儲器加載或者拷貝到系統(tǒng)的RAM中并執(zhí)行,。OAL是位于操作系統(tǒng)的內(nèi)核與硬件之間的適配層,,也是連接系統(tǒng)內(nèi)核與硬件的樞紐,它具有屏蔽硬件設(shè)備細(xì)節(jié)以及抽象硬件功能的作用,。而OAL初始化則是指通過一組函數(shù)來體現(xiàn)出0AL屏蔽和抽象硬件設(shè)備的作用,。
此外,如果要WinCE系統(tǒng)成為完整的操作系統(tǒng),,還得加上硬件驅(qū)動程序,、硬件接口程序和應(yīng)用程序組。因此,即使在一個(gè)簡單的嵌入式系統(tǒng)里,,WinCE系統(tǒng)啟動時(shí)是需要加載內(nèi)核和加載許多組件或驅(qū)動程序,。
WinCE系統(tǒng)在啟動時(shí)調(diào)用函數(shù)的順序:①CPU執(zhí)行引導(dǎo)向量,跳轉(zhuǎn)到硬件初始化代碼,,即STartup函數(shù),。②在start up函數(shù)完成最小硬件環(huán)境初始化后跳轉(zhuǎn)到KernelStart函數(shù),來對內(nèi)核進(jìn)行初始化,。③Kernelstart函數(shù)調(diào)用OEMInitDebugSerial完成對調(diào)試串口的初始化,;同時(shí)調(diào)用0EMInit函數(shù)來完成硬件初始化工作以及設(shè)置時(shí)鐘、中斷,;最后,,調(diào)用OEMGetextensionDRAM函數(shù)來判斷是否還有另外一塊DRAM.至此,內(nèi)核加載完畢,。由此可見,,WinCE系統(tǒng)啟動的重中之重是Startup函數(shù)的正確加載,如果這個(gè)Startup函數(shù)調(diào)用失敗,,則會使到系統(tǒng)在啟動頻繁出錯(cuò),。
WinCE啟動失敗可能會存在于引導(dǎo)初始化失敗、內(nèi)核加載失敗,、0AL函數(shù)初始化失敗,、驅(qū)動程序加載失敗、組件加載失敗和應(yīng)用程序加載失敗,。也就是說,,WinCE啟動失敗一方面可能是在Startup函數(shù)的處理上,例如引導(dǎo)初始化和OAL初始化,。另一方面還存在于驅(qū)動程序和組件自啟動的失敗上,,例如基本的驅(qū)動程序、注冊表配置或自運(yùn)行的程序等,。就不能被使用,。所以,當(dāng)注冊表在啟動時(shí)加載錯(cuò)誤或者注冊表配置有錯(cuò)誤時(shí),,也是會導(dǎo)致WinCE系統(tǒng)啟動失敗的,。
二 導(dǎo)致WinCE啟動失敗的主因分析
Windows CE在啟動時(shí)為什么會失敗呢?這個(gè)問題也一直讓我頭痛,。因?yàn)閃indows CE啟動失敗既有軟件因素,,也有硬件因素。例如,,可能是WinCE的啟動引導(dǎo)過程有問題,、也許是內(nèi)核加載時(shí)有問題、也許是OAL函數(shù)調(diào)用的隱性問題或者硬件設(shè)備本身的問題造成的。所以,,解決起來比較麻煩和比較耗時(shí)間,,也是最讓我們頭疼的事情。
一般來說,,解決和分析WinCE啟動失敗有一個(gè)原則,,就是"先軟后硬"的原則,也就是說要先分析軟件因素再到硬件因素,。本文主要是在arm微處理器和Windows CE 6.0平臺上進(jìn)行分析軟件因素造成的失敗,。
(1)引導(dǎo)程序BootLoader導(dǎo)致的失敗
在Windows CE系統(tǒng)中,整個(gè)系統(tǒng)的加載啟動任務(wù)由BootLoader來完成,,BootLoader是在WinCE內(nèi)核運(yùn)行之前運(yùn)行的一段小程序,。通過這段小程序,可以初始化硬件設(shè)備,、建立內(nèi)存空間的映射圖和初始化MMU等,。從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適的狀態(tài),為調(diào)用操作系統(tǒng)內(nèi)核準(zhǔn)備好環(huán)境,。因此,,只有在引導(dǎo)程序正確的完成自己的任務(wù)后,才會將控制權(quán)移交給內(nèi)核,。
在WinCE平臺上,,引導(dǎo)裝載程序是在硬件上執(zhí)行的第一段代碼,通常將引導(dǎo)程序放置在不易丟失的存儲器的開始地址或者是系統(tǒng)冷啟動時(shí)PC寄存器的初始值,。如果這段小程序代碼編寫錯(cuò)誤,,則系統(tǒng)無法完成第一步的引導(dǎo)操作,這是導(dǎo)致啟動系統(tǒng)失敗的第一個(gè)因素,。
①BootLoader初始化硬件失敗
BootLoader第一個(gè)功能是要實(shí)現(xiàn)板級和片級初始化硬件,,主要是把CPU初始化到一已知狀態(tài)。在BootLoader目錄下,,會發(fā)現(xiàn)一些,。s文件,可能會是init.s或者是reset.s等,,這樣的文件是CPU加電后最先執(zhí)行的代碼。StartUp 函數(shù)是BootLoader的入口函數(shù),。該函數(shù)一般是使用匯編語言編寫,,與CPU關(guān)系非常緊密,能完成初始化CPU,、內(nèi)存等核心硬件,。然后,BootLoader在平臺初始化完畢后就可以在不用人工干預(yù)的情況下自動加載WinCE內(nèi)核了。但如果BootLoader在初始化硬件時(shí)失敗,,就會直接導(dǎo)致系統(tǒng)的啟動失敗了,。
②BootLoader加載內(nèi)核時(shí)失敗
一般在平臺調(diào)試完畢后,BootLoader就會加載WinCE內(nèi)核映像,,這也是BootLoader的功能之一,。WinCE內(nèi)核映像文件通常叫做nk.bin,它是Windows CE二進(jìn)制數(shù)據(jù)格式文件,不僅包含了有效的程序代碼,,還有按照一定規(guī)則加入的控制信息,。
在系統(tǒng)啟動時(shí)BootLoader可以通過兩種不同的方式來加載WinCE內(nèi)核文件nk.bin.一種是下載模式,另一種是本地啟動模式,。本地啟動模式也稱為自主模式,,即 BootLoader 從目標(biāo)機(jī)上的某個(gè)固態(tài)存儲設(shè)備上將操作系統(tǒng)加載到 RAM 中運(yùn)行,整個(gè)過程并沒有用戶的介入,。而下載模式則是目標(biāo)機(jī)上的 BootLoader 將通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(jī)(Host)下載文件,。當(dāng)BootLoader正確的把nk.bin解壓到RAM后,就會把CPU控制權(quán)交給CE內(nèi)核,。因此,,如果Boot Loader處理不當(dāng),就可能會造成加載和解壓nk.bin文件的失敗,,這樣自然也就會造成系統(tǒng)啟動的失敗了,。
(2)OAL導(dǎo)致的啟動失敗
OAL(OEM Adaptation Layer)是指OEM 適配層,它是位于Windows CE內(nèi)核和硬件之間的一層適配層,,是OAL各個(gè)模塊代碼被編譯后(,。lib)和其它內(nèi)核庫鏈接到一起形成Windows CE的內(nèi)核可執(zhí)行文檔NK.EXE.OAL包括了和系統(tǒng)硬件通訊的最底層代碼,內(nèi)核是通過OAL跟硬件進(jìn)行交互,。邏輯上,,OAL是介于CE內(nèi)核和設(shè)備硬件之間的一個(gè)代碼層,是一個(gè)抽象的概念,。物理上,,OAL和其它一些庫一起鏈接成可執(zhí)行文件。
與以前的Win CE舊版本不同的是,,在Win CE 6.0中內(nèi)核(Kenerl)和OEM代碼被分成oal.exe,、kernel.dll和kitl.dll三個(gè)部分,其中啟動代碼(startup)和 OAL層的實(shí)現(xiàn)部分不再與內(nèi)核鏈接生成NK.exe,取而代之的是啟動代碼(startup)和硬件相關(guān)且獨(dú)立于內(nèi)核的OAL層的實(shí)現(xiàn)部分編譯成 oal.exe;而與內(nèi)核相關(guān)且獨(dú)立于硬件的OAL層代碼包含在kernel.dll中,,內(nèi)核無關(guān)傳輸層(KITL)的支持代碼從OAL層分離出來編譯成 kitl.dll.因此,,WinCE6.0的啟動只與oal.exe和kernel.dll有關(guān)。至于kitl.dll,只有將操作系統(tǒng)編譯成具有 KITL功能時(shí)才用到,。這樣做的好處是可以單獨(dú)升級OAL,但整體的OAL結(jié)構(gòu)并沒有改變,。
①OAL初始化硬件時(shí)失敗
oal.exe是通過Startup函數(shù)來完成硬件的初始化,。一般來說,OAL的啟動代碼(Startup.s)與該硬件平臺的Bootloader的啟動代碼(Startup.s)是可以共享的,。例如,,其中PreInit 函數(shù)主要完成將arm處理器工作模式切換到管理員模式,同時(shí)關(guān)閉MMU,并檢測系統(tǒng)啟動原因,。如果是熱啟動,,即在該函數(shù)調(diào)用之前已經(jīng)啟動過 Bootloader的啟動代碼(Startup.s),相當(dāng)基本硬件初始化已經(jīng)完成,,則可直接跳轉(zhuǎn)到OALStartUp函數(shù)中,;否則需要進(jìn)行硬件中斷屏蔽、內(nèi)存,、系統(tǒng)時(shí)鐘頻率,、電源管理等硬件的基本初始化過程。
在StartUp 函數(shù)初始化CPU等核心硬件并跳轉(zhuǎn)到Main函數(shù)后,,系統(tǒng)就會轉(zhuǎn)入C語言代碼執(zhí)行環(huán)境,。這時(shí)Main函數(shù)分為3個(gè)模塊:BLCOMMON、Download Function,、FLASH Function.其中BLCOMMON模塊是由微軟提供的,,執(zhí)行一些邏輯上的功能。而Download Function,、FLASH Function中的函數(shù)與硬件平臺息息相關(guān),。因此,對于每種硬件平臺都要將函數(shù)的實(shí)現(xiàn)進(jìn)行適當(dāng)修改,,這種修改是需要對硬件非常熟悉的,。當(dāng)修改出現(xiàn)錯(cuò)誤時(shí),就會導(dǎo)致系統(tǒng)啟動失敗了,。
在硬件平臺初始化完成后,,oal.exe的啟動任務(wù)基本完成,余下的啟動工作由內(nèi)核相關(guān)且獨(dú)立于內(nèi)核的OAL層實(shí)現(xiàn)體kernel.dll接管,。也就是說,,這時(shí)Startup會調(diào)用OALStartUp函數(shù),OALStartUp函數(shù)主要完成將OEMAddressTable表傳遞給內(nèi)核,,然后調(diào)用KernelStart函數(shù)跳轉(zhuǎn)到內(nèi)核,。因此,如果此時(shí)OAL的啟動Startup函數(shù)調(diào)用失敗的話,,就也會導(dǎo)致系統(tǒng)的啟動失敗了,。
這里需要特別注意的是,Bootloader和OAL中均包含啟動Startup函數(shù),。它的功能大致相同,,都是要初始化最小硬件環(huán)境。Bootloader的啟動Startup函數(shù)是在為自己的執(zhí)行準(zhǔn)備硬件環(huán)境,,OAL的啟動Startup函數(shù)則是為kernel的執(zhí)行準(zhǔn)備硬件環(huán)境,。由于這兩種硬件環(huán)境要求基本相同,所以它們的代碼也有很大部分可以相互借鑒,。但應(yīng)該明白Bootloader與OAL在物理上是獨(dú)立的,,它們并不是同一段代碼。當(dāng)然,,如果可以確定這一部分在Bootloader已經(jīng)初始化過如熱啟動,,則在OAL中不必重復(fù)執(zhí)行。
②OAL入口位置定位失誤導(dǎo)致的失敗
從上述WinCE啟動流程可知,,在OAL初始化硬件后而在內(nèi)核啟動前,,系統(tǒng)是需要調(diào)用KernelStart函數(shù)來跳轉(zhuǎn)到內(nèi)核。因此,,這里有一個(gè)要點(diǎn),,就是WinCE需要找到OAL的入口位置,然后才能調(diào)用入口函數(shù)與全局塊進(jìn)行指針交換,,這樣內(nèi)核才能使用OAL層中的信息,,同樣OAL層也才能訪問內(nèi)核(kernel)導(dǎo)出的函數(shù)。
OAL入口位置函數(shù)的調(diào)用實(shí)際上是通過OEMGLOBAL結(jié)構(gòu)體實(shí)現(xiàn)的,,實(shí)際調(diào)用位置為OEMInitDebugSerial和OEMInit.也就是說,,OEMGLOBAL結(jié)構(gòu)體構(gòu)建了內(nèi)核和OAL層之間進(jìn)行通信的橋梁。OEMGLOBAL結(jié)構(gòu)體定義了OAL層所有必須的函數(shù),,該結(jié)構(gòu)體在oemglobal.c文件中被初始化,,并會被編譯在OEMMain.lib和 OEMMain_StaticKITL.lib兩個(gè)庫中。如果OAL鏈接這兩個(gè)庫,,則必須要有正確的該結(jié)構(gòu)體的函數(shù)實(shí)現(xiàn)體,,同時(shí)還需要調(diào)用ARMSetup來設(shè)置物理地址和非緩沖的虛擬內(nèi)存地址的映像、arm中斷向量以及內(nèi)核模式所需要的堆棧,、調(diào)用OEMInitDebugSerial函數(shù)初始化調(diào)試串口,、調(diào)用OEMInit進(jìn)行平臺初始化等。否則,,如果OAL入口位置函數(shù)有誤,,則內(nèi)核和OAL層之間的訪問就會失敗,也就會導(dǎo)致系統(tǒng)在啟動時(shí)出錯(cuò)和失敗,。
三 導(dǎo)致的WinCE啟動失敗的其它相關(guān)因素
(1)驅(qū)動程序加載錯(cuò)誤導(dǎo)致的失敗
在調(diào)試中,,我們還發(fā)現(xiàn)系統(tǒng)在啟動時(shí)執(zhí)行到OEMInit時(shí)也經(jīng)常會出現(xiàn)錯(cuò)誤。一般來說,,系統(tǒng)調(diào)用OEMInit運(yùn)行完成之后,,就會跳回Private或Public下的代碼繼續(xù)運(yùn)行,,然后再啟動device.exe加載各個(gè)驅(qū)動程序。由于這一段代碼是微軟提供的default代碼,,基本上不會有問題,。所以,我們就有理由懷疑如果加載的驅(qū)動程序出了問題,,是也會造成系統(tǒng)啟動失敗的,。一般來說,這些加載的驅(qū)動程序主要是 BSP中的Audio,、Display,、SDMMC、Serial,、USB等,。
(2)啟動時(shí)加載配置有誤的注冊表導(dǎo)致的失敗
與桌面Windows一樣,WinCE注冊表(Registry)也是一個(gè)系統(tǒng)數(shù)據(jù)庫,,用來保存應(yīng)用程序,、驅(qū)動程序、用戶的設(shè)定以及其它一些系統(tǒng)的配置信息,,通常還存儲著操作系統(tǒng)運(yùn)作和調(diào)用程序的狀態(tài)信息,。例如,每個(gè)用戶的配置文件,、安裝的應(yīng)用程序以及每個(gè)應(yīng)用程序可以創(chuàng)建的文檔類型,、文件夾和應(yīng)用程序圖標(biāo)的屬性表設(shè)置、系統(tǒng)上存在哪些硬件以及正在使用哪些端口等,。
對于硬件外設(shè)來說注冊表是一個(gè)記錄驅(qū)動程序設(shè)置和位置的數(shù)據(jù)庫,。當(dāng)WinCE系統(tǒng)在啟動時(shí)需要啟動某些必要的硬件設(shè)備時(shí),就會需要使用外設(shè)驅(qū)動程序,。但如果在WinCE中這個(gè)外設(shè)驅(qū)動是獨(dú)立于操作系統(tǒng)的,,WinCE系統(tǒng)就需要知道從哪里找到它們,例如文件名,、版本號,、其它設(shè)置和信息。因此,,注冊表上沒有此設(shè)備的記錄時(shí),,它們就不能被使用。所以,,當(dāng)注冊表在啟動時(shí)加載錯(cuò)誤或者注冊表配置有錯(cuò)誤時(shí),,也是會導(dǎo)致WinCE系統(tǒng)啟動失敗的。