基于ARM-Linux嵌入式系統引導程序的設計
(a)建立中斷向量表,當程序出現異常后可跳轉到相應子程序執行。如
bResetAddr;
bUndefAddr;handlerforUndefinedmode0x4
bSWI_Addr;handlerforSWIinterrupt0x8
bPreAbortAddr;handlerforPreAbort0xC
bDataAbortAddr;handlerforDataAbort0x10
b.;reserved0x14
bIRQ_Addr;handlerforIRQinterrupt0x18
bFIQ_Addr;handlerforFIQinterrupt0x1C
(b)屏蔽所有的中斷。為中斷提供服務通常是操作系統設備驅動程序的責任,因此在Bootloader的執行全過程中可以不必響應任何中斷。可以通過寫S3C2410的寄存器INTMSK、INTSUBMSK來完成;
(c)設置CPU的速度和時鐘頻率。可以通過寫寄存器LOCKTIME、MPLLCON、UPLLCON來實現;
(d)RAM初始化。包括正確地設置系統的內存控制器的功能寄存器BWSCON以及各內存控制寄存器等。
(e)初始化LED。典型地,通過GPIO來驅動LED,其目的是表明系統的狀態是正常還是出現錯誤。
3.1.2為加載Stage2準備RAM空間,拷貝Stage2到RAM中。
為了獲得更快的執行速度,通常把Stage2加載到RAM空間中來執行,因此必須為加載Bootloader的Stage2準備好一段可用的RAM空間范圍。具體的地址范圍可以任意安排,比如我們習慣將Stage2可執行映像安排到RAM地址最頂部1MB開始的空間內執行。拷貝時要確定兩點:Stage2的可執行映像存放在Flash中的起始地址和終止地址;以及RAM空間的起始地址。
3.1.3設置堆棧指針sp。
堆棧指針的設置是為了執行C語言代碼作好準備,通常我們可以把sp(Userstack)設置在上面所安排的那個1MBRAM空間的最頂端(堆棧向下生長)。此外,在設置堆棧指針sp之前,也可以關閉LED燈,以提示我們準備跳轉到Stage2。
3.1.4跳轉到Stage2的C入口點。
在上述一切都就緒后,就可以跳轉到Bootloader的Stage2去執行了。比如通過修改PC寄存器為合適的地址來實現。
3.2Bootloader的Stage2通常依次執行以下主要步驟:
3.2.1進入Stage2的入口程序
Stage2的代碼通常用C語言來實現,但是與普通C語言應用程序不同的是,在編譯和鏈接bootloader這樣的程序時,我們無法使用glibc庫中的任何支持函數。這就需要我們利用匯編語言寫一段trampoline(彈簧床)小程序,并將這段trampoline小程序來作為Stage2可執行映象的執行入口點。然后我們可以在trampoline小程序中用CPU跳轉指令跳入main()函數中去執行;而當main()函數返回時,CPU執行路徑顯然再次回到了trampoline程序。具體程序如下:
.text
.globl_trampoline
_trampoline:
blmain
b_trampoline
3.2.2初始化本階段要使用到的硬件設備
這通常包括:初始化至少一個串口,以便和終端用戶進行I/O輸出信息;初始化計時器等。在初始化這些設備之前,也可以重新把LED燈點亮,以表明我們已經進入main()函數執行。設備初始化完成后,可以輸出一些打印信息,程序名字字符串、版本號等。
3.2.3檢測系統的內存映射(Memorymap)
所謂內存映射就是指在整個4GB物理地址空間中有哪些地址范圍被分配用來尋址系統的RAM單元。CPU通常預留出一段足夠大的地址空間給系統RAM,但是在搭建具體的嵌入式系統時不一定會實現CPU預留的全部RAM地址空間。而是往往只把CPU預留的全部RAM地址空間中的一部分映射到RAM單元上,而讓剩下的那部分預留RAM地址空間處于未使用狀態。如S3C2410使用的RAM空間僅為0x30000000-0x33ffffff。
3.2.4加載內核映像和根文件系統映像并從Flash上拷貝
規劃內存占用的布局。這里包括兩個方面:(1)內核映像所占用的內存范圍;(2)根文件系統所占用的內存范圍。對于內核映像,一般將其拷貝到從(MEM_START+0x8000)這個基地址開始的大約1MB大小的內存范圍內(嵌入式Linux的內核一般都不超過1MB)。從MEM_START到MEM_START+0x8000這段32KB大小的內存之所以被空出,是因為Linux內核要在這段內存中放置一些全局數據結構,如:啟動參數和內核頁表等信息。而對于根文件系統映像,則一般將其拷貝到MEM_START+0x0010,0000開始的地方。若采用Ramdisk作為根文件系統映像,則其解壓后的大小一般為1MB。
由于像ARM這樣的嵌入式CPU通常都是在統一的內存地址空間中尋址Flash等固態存儲設備,因此從Flash上讀取數據與從RAM單元中讀取數據并沒有什么不同。用一個簡單的循環就可以完成從Flash設備上拷貝映像的工作。如:
count=kernelsize
while(count>0){
*dest++=*src++;/*theyareallalignedwithwordboundary*/
count-=4;/*bytenumber*/
};
3.2.5設置內核的啟動參數
將內核映像和根文件系統映像拷貝到RAM空間后,設置Linux內核的啟動參數,如ATAG_CORE、ATAG_MEM、ATAG_CMDLINE、ATAG_RAMDISK、ATAG_INITRD等,然后就可以準備啟動Linux內核了。
3.2.6調用內核。
Bootloader調用Linux內核的方法是直接跳轉到內核的第一條指令處,也即直接跳轉到MEM_START+0x8000地址處。此時,還需要設置CPU寄存器、CPU模式、Cache和MMU。
3.結束語
Bootloader是依賴于硬件而實現的,每個目標板的硬件配置都不完全一樣,因此Bootloader程序也都不會完全一樣。本文以Samsung公司的的S3C2410開發板為平臺,闡述了Bootloader運行的主要步驟和關鍵技術,為實現引導Linux操作系統內核運行提供合適的環境。文章后面關于Linux內核啟動參數的具體設置及如何調用內核是和具體操作系統密切關系的,未做詳細論述。此外,設計與實現一個優秀的Bootloader程序是一個龐大復雜的過程,在程序中如能多利用LED和串口輸出信息會是幫助我們調試的好方法。
參考文獻:
[1].ARM920Tdatasheethttp://www.dzsc.com/datasheet/ARM920T_139814.html.
[2].ROMdatasheethttp://www.dzsc.com/datasheet/ROM_1188413.html.
評論