WINCE6.0+S3C2443的启动过程---内核启动
********************************LoongEmbedded********************************
作者:LoongEmbedded(kandi)
時間:2010.11.07
類別:WINCE操作系統
********************************LoongEmbedded********************************
?
WINCE6.0+S3C2443系統啟動過程
?
我們知道是NBOOT用來引導EBOOT,繼而EBOOT加載并引導WinCE操作系統(NK)。那么,WinCE6.0是如何啟動的呢?在前面文章我們知道eboot的OEMLaunch函數通過調用下面函數來跳轉到dwPhysLaunchAddr處執行
Launch(dwPhysLaunchAddr)
我們先來看看NK.bin的image start等一些信息吧
圖1
上圖的start address=0x80245c50,這個地址經過OALVAtoPA()函數轉換之后獲得的對應的物理地址0x30245C50就是dwPhysLaunchAddr地址,那么這個地址對應的是NK.bin中哪個文件的哪個函數呢?通過命令viewbin –o nk.bin >output.txt輸出output.txt到release目錄下,下面看這個文件的下面內容:
?
?
?
圖2
也可以通過PB6.0中瀏覽NK.bin的功能來查看這個地址(file->open->file,選擇Windows Embedded CE Run-Time Image類型,然后選擇NK.bin)
?
圖3
從圖2或圖3 ?start address=0x80245c50對應的是NK.exe入口地址,而NK.exe就是OAL.exe,為什么呢?我們看看release目錄下ce.bib的如下內容
?
圖4
由圖4可知,NK.exe就是OAL.exe,那么OAL.exe的入口地址是哪個函數呢?看/Src/Oal/Oalexe下面的source文件
?
圖5
這樣我們就知道eboot跳轉到OAL.exe的startup函數中執行了。
?
1.?????? Statup函數的主要部分
Startup入口
?
圖6
因為EBOOT中已經初始化了相關硬件,所以OAL的啟動代碼就可以省去這部分工作,接著執行下面部分
?
圖7
圖7 tst指令先用r0來和0x8進行與運算,結果為0,所以設置標志位Zero=1,后面的beq指定在zero=1的時候,跳轉到下面標號4的地方,下面的鏈接是ARM匯編beq和bne的介紹http://blog.csdn.net/LoongEmbedded/archive/2010/11/04/5987565.aspx
?
?
圖8
圖8的是將g_oalAddressTable保存到r0,作為參數傳遞給kernelStart函數,以便KernelStart函數中初始化MMU地址映射(g_oalAddressTable就是地址映射表的地址)。我們怎么來理解
add??????????? r0, pc, #g_oalAddressTable - (. + 8)
這條指令呢?我們知道PC總是指向取指的指令,而不是指向正在執行的指令或是正在譯碼的指令,一般情況下,人們總是習慣把正在執行的指令作為參考點,稱為當前第一條指令,因此,PC總是指向第3條指令。對于ARM指令,有:PC的值=當前指令所在的存儲地址+8;對于Thumb指令,則有:PC的值=當前指令所在的存儲地址+4,下圖是ARM系列的流水線圖
?
圖9
(.+8)這個表達式的. (+之前的點) 是在MS的ARM編譯器在預處理時決定的,其代表它所在的指令的地址(也即這條指令的存儲地址)。這里還要知道一點,就是ARM架構的特點,一條指令從加載到執行主要分為預取、譯碼、執行三個階段。結合上面提到的“PC的值=當前指令所在的存儲地址+8”,因此有恒等式.+8=pc,上面語句可以理解為
r0 = pc+g_oalAddressTable-(.+8) = g_oalAddressTable+ (pc-(.+8)) = g_oalAddressTable
也可以這樣理解
r0 = pc+g_oalAddressTable-(.+8)=pc-(.+8)+ g_oalAddressTable=當前指令所在的存儲地址+8-(.+8)+ g_oalAddressTable=.+8--(.+8)+ g_oalAddressTable= g_oalAddressTable
為什么要采用這樣的方式給r0賦值,而不采用下面的語句呢
?
Ldr r0,g_oalAddressTable (我試過用這條語句,就不能成功跳轉,系統無法正常啟動)
?
為什么startup函數中不能直接把g_oalAddressTable符號的值賦值給r0來作為KernelStart函數的參數呢?因為在調用KernelStart函數的時候MMU單元還處于關閉的狀態,CPU訪問外部物理存儲器仍然使用的是系統物理地址,而g_oalAddressTable符號記錄的是g_oalAddressTable數組的起始存儲位置在0x80000000~0x9FFFFFFF范圍內的虛擬內存地址。之所以g_oalAddressTable符號記錄的是虛擬內存地址而不是物理地址,并且落在了0x80000000~0x9FFFFFFF虛擬內存地址范圍內,因為在匯編源代碼中使用的用來表達程序地址的字符串在程序鏈接階段要接受地址重定位的處理,而地址重定位的依據是config.bib,其中對系統物理存儲的使用分配作出安排所使用的全部是0x80000000~0x9FFFFFFF范圍內的虛擬內存地址
我們接下來看看從startup函數跳轉到KernelStart函數的語句
bl?????????????? KernelStart?????? ; Call the WinCE kernel.
首先我們要知道arm指令集與X86不同,不是通過棧空間來傳遞參數,而是通過寄存R0,R1,R2,R3來傳遞,而前面的語句就是把g_oalAddressTable的物理存儲地址賦值給r0后傳遞給KernelStart函數的,到此,硬件平臺初始化完成后,oal.exe的啟動任務基本完成,余下的啟動工作由內核相關且獨立于內核的OAL層實現體kernel.dll接管
?
2.?????? KernelStart函數
這個函數在/WINCE600/PRIVATE/WINCEOS/COREOS/NK/LDR/ARM/armstart.s中定義,這個函數的主要任務就是為WINCE操作系統設置頁表并且啟用MMU,先看KernelStart入口處
?
圖10
設置頁表的部分就不介紹了,下面看啟動MMU和caches部分
?
圖11
圖11啟用了MMU和caches部分之后,WINCE操作系統后面就使用虛擬內存地址而不是物理地址來訪問了。接著KernelStart函數為ABORT、IRQ、FIQ、UNDEF和supervisor這5個CPU的特權級狀態設置堆棧
?
圖12
這些工作完成之后KernelStart函數就會調用ARMInit函數,下面看是如何調用ARMIint函數的
?
圖13
這樣在調用ARMIint之前,r0就保存了KDataSrtuct這塊數據區域的指針,也即r0指向了KDataSrtuct這塊數據區域,這就是傳遞給ARMIint函數的參數,這個函數這個函數在/WINCE600/PRIVATE/WINCEOS/COREOS/NK/LDR/ARM/armint.c下面定義
?
圖14
?
ARMIint函數有三個主要的任務:
⑴是調用KernelRelocate()函數進行內核全局變量重定位。
?
圖15
在ARMInit函數執行之前,系統仍無法使用全局變量,因為系統的全局還在ROM區域,對于操作系統而言,出于安全的考慮,只有XIP程序才有讀取ROM區域數據的權利,對于大部分WINCE操作系統,只有將數據拷貝到RAM區域才能進行讀寫,ARMInit函數通過調用KernelRelocate函數對pTOC全局變量重新定位之后,才能讀寫。對內核啟動所需要的
?
⑵全局變量重新定位之后,對內核啟動所需要的KDataStruct結構體(它是內核數據結構, 簡單理解成共享內存好了, oal 和 kernel都可訪問)進行初始化,是將位于OAL的OEMinitGlobals()函數指針賦值到pKdata的成員變量dwOEMInitGlobalsAddr,這個在后面的內核NKStartup函數將會用到,OEMInitGlobals便是交換oal.exe和kernel.dll之間的全局指針,下圖是OEMInitGlobals函數體
?
圖16
⑶是調用函數FindKernelEntry通過TOC找到kernel.dll的入口點函數地址,是如何找到的呢?先看FindKernelEntry函數體
?
圖17
圖17具體是如何找到kernel.dll的入口函數的呢?我們通過viewbin –o nk.bin >output.txt輸出NK.bin的相關信息中的kernel.dll部分信息如下
?
圖18
我們看kernel.map文件
?
圖19
再來看下圖
?
圖20
結合圖18,圖19和圖20我們可以知道kernel.dll的入口函數是NKStartup,這樣通過調用函數ARMInit函數就返回NKStartup,那么我們回到KernelStart函數來看看是如何跳轉到NKStartup函數執行的
?
圖21
就這樣跳轉到NKStartup函數執行了。
?
3.?????? NKStartup函數
這個函數在/WINCE600/PRIVATE/WINCEOS/COREOS/NK/KERNEL/ARM/mdram.c中定義,我們先看看NKStartup函數頭的注釋吧
?
圖22
NKstartup函數的主要工作如下:
⑴從NK Loader傳遞過來的KDataStruct結構體數據中獲取到供自身啟動所需要的數據信息,如下圖所示,其中http://blog.csdn.net/LoongEmbedded/archive/2010/11/06/5991971.aspx介紹了KDataStruct結構體結構體
?
圖23
⑵定位對WINCE6.0特有的OEMGLOBAL結構體的初始化函數OEMInitGlobals地址,該結構體構建了內核和OAL層之間進行通信的橋梁。在 OEMGLOBAL結構體定義了OAL層所必須的函數,該結構體在oemglobal.c文件中被初始化,并被編譯在OEMMain.lib和 OEMMain_StaticKITL.lib兩個庫中,如果OAL鏈接這兩個庫,則必須要有該結構體中函數實現體;
?
圖24
這里涉及到一個很重要的全局變量g_pOemGlobal,它指向結構體OEMGLOBAL全局變量OemGlobal,而OemGlobal在oemglobal.c定義并對其成員函數賦值,下圖是其中一部分
?
圖25
在這里,我們看到了一個很重要的函數OEMInit,這些OEM函數就是被內核用來和OAL部分通信的。
?
⑶調用函數ARMSetup為操作系統內核進程填充虛擬內存0XA0000000~0XBFFFFFFF范圍的uncachable靜態映射區域,在前面的KernelStartup函數中,依據g_oalAddressTable數組的數據內容將所有的系統物理地址單元(包括所有類型的CPU可直接尋址訪問的物理存儲設備和各類外設的控制與狀態寄存器、數據緩沖區而后I/O端口)映射到虛擬內存的0x80000000~0x9FFFFFFF地址范圍。這是WINCE操作系統的一個約束,正是由于這個緣故,在基于WINCE操作系統中所有的系統物理地址單元總容量不得超過512MB,在ARMSetup函數中再一次將全部的物理存儲映射到虛擬地址的0xA0000000~0xBFFFFFFF地址范圍。
?
圖26
另外ARMSetup函數還設置中斷向量表,如下
?
圖27
?
⑷如果系統啟用了KITL調試功能,則加載KITL模塊
?
圖28
至于kitl.dll的入口函數值如何確定的呢?這個辦法可以參考上面介紹到如何定位kernel.dll的入口函數一樣,我們可以找到kitl.dll的入口函數是KitlDllMain()。當在編譯選項(Build Options)中選中Enable KITL時,編譯器就會將kitl.dll動態鏈接庫鏈接到到內核中,在OEMGLOBAL結構體定義了利用KITLOEM宏將kitl.dll的入口函數KitlDllMain編譯到系統中條件,這是系統鏈接的庫為oemmain_statickitl.lib,下面為OEMGLOBAL結構體中KITL的定義部分,我們看OemGlobal全局數組中的pfnKITLGlobalInit成員的初始化
?
圖29
?
⑸調用OAL函數執行對目標平臺板級硬件的初始化動作。
?
圖30
圖30注釋中的OEMInit是OAL中用來實現對板級硬件進行初始化的一個很重要的OEM函數,其主要的任務是初始化cache全局變量,初始化LCD控制器,初始化中斷,初始化系統時鐘和初始化KITL等,其他OAL函數也是通過這樣的方式被內核調用來和OAL層通信的。
?
⑸清楚TLB,分隔RAM區域和跳轉到KernelStartup函數執行。
?
圖31
KernelFindMemory()是查找系統可用內存并且分隔,在WINCE操作系統中,RAM空間主要分為存儲內存和程序內存,存儲內存主要為文件的存儲空間,包括內核文件和復制到系統中所有目標文件,程序內存為運行程序時所需要的存儲空間,見相關的博文http://blog.csdn.net/LoongEmbedded/archive/2010/10/16/5944846.aspx
?
NKStartup函數完成自己的工作之后跳轉到第二個KernelStart函數,這里的KernelStart函數與前面的KernelStart函數的屬于兩個完全不同的函數,NKStartup函數中調用的KernelStart 函數為/WINCE600/PRIVATE/WINCEOS/COREOS/NK/KERNEL/ARM/armtrap.s文件中的KernelStart 函數,主要完成調用內核初始化函數KernelInit,并跳轉到操作系統的第一個啟動的任務,這個KernelStartup函數與NKstartup函數同屬一個靜態庫及同一個kernel.dll模塊,而上一個KernelStartup函數所屬的nkldr.lib庫被鏈接進了WINCE系統模塊oal.exe中。
?
4.?????? 第二個KernelStart函數
?
圖32
這個函數的主要工作有兩部分:
⑴調用KernelInit函數來初始化內核
?
圖33
在目標設備上的硬件初始化結束后,下一步就是調用KernelInit內核初始化函數來初始化WINCE操作系統本身,WINCE6.0的內核初始化函數同其他版本的內核初始化函數基本相近,主要完成在啟動第一個線程前對內核進行初始化,主要包括API函數集初始化、堆的初始化、初始化內存池、進程初始化、線程初始化和文件映射初始化等操作。下面來看THRDIint函數體最重要的一部分,THRDInit()初始化了第一個線程是SystemStartupFunc。
?
圖34
⑵FirstSchedule
FirstSchedule函數為Windows CE操作系統啟動過程中最后無條件跳轉的一個函數,FirstSchedule調用HandleException函數來讓系統進行重新調度,這樣就選擇了第一個就緒的線程(也就是SystemStartupFunc)來執行,下面接著看線程處理函數SystemStartupFunc
?
圖35
接著看RunApps線程
?
圖36
RunApps線程處理函數主要是啟動filesys.dll,啟動后等待其執行的情況,在完成了文件系統的相應的初始化之后,這里繼續初始化MUI和系統設置,完成后再通知filesys這邊的工作已經完成,filesys繼續啟動,filesys會完成WinCE的最后啟動過程,包括設備管理device.dll,窗體圖像子系統gews.dll和shell程序 explore.exe,這樣,WINCE6.0的啟動就完成了。
?
備注:對于fiesys文件系統的啟動過程,可以看MSDN的描述http://msdn.microsoft.com/en-us/library/aa912276.aspx
?
參考文檔
http://blog.csdn.net/LoongEmbedded/archive/2010/11/06/5991297.aspx
http://www.cnblogs.com/we-hjb/archive/2008/10/12/1309596.html
http://apps.hi.baidu.com/share/detail/17484975
http://blog.chinaunix.net/u3/105630/showart_2181227.html
http://www.armce.com/bbs/thread-403-1-1.html
?
?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的WINCE6.0+S3C2443的启动过程---内核启动的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NKStartup的参数KData
- 下一篇: volatile 使用说明