結合PXA270 Bootloader實例詳細分析ARM映象文件
最近花了一個月的時間終於把PXA270的板子Bootloader 部分整出來了,頗費周折;其中的很長一段時間,一直被ARM可執行程序的執行機理迷惑:搞不懂ARM的映象文件組成、代碼重定位、地址重映射remap等,網上查了N多資料看了N多技術帖子總算整清楚。如今寫出來整理一下思路,也爲其它初始進入ARM系統設計的兄弟們提供點資料借鑑,少走些彎路是好。
ARM學習過程中,初學者最難突破的應當是ARM映象文件形態和ARM啓動代碼分析了,本文將以我的Bootloader源碼爲例,力求作個透徹分析。
如下給出我的開發環境:
² Cpu:INTEL PXA270 (ARM V5TE)
² Board:INTEL MAINSTONE 2
² Memory:64MByte SDRAM,32MByte NorFlash(Inte E28F128),32MByte NandFlash
² Cross-complier:cross_3.3.2
一、關於映象文件和load region和execute region:
1. ARM映象文件組成
我們在ADS中編譯源代碼文件後的生成文件有兩個:.axf和.bin文件。.bin文件是二進制格式執行文件,它纔是真正可下載到FLASH中運行的;.axf文件是ADS的調試文件,利用UltraEdit查看其二進制代碼,可以發現.axf其實是在.bin文件基礎上增加文件頭標識、文件尾表示(U-boot中的mkimage工具在映象文件中加入64kbyte的文件頭,以供U-Boot識別)、再插入ADS的調試符號。打開ADS的調試器,可對.axf文件調試。
2. ARM映象文件加載域(load region)、運行域(execute region)
看了很多ARM教材這方面都沒怎麼涉及,有的書本提到感覺始終太過術語話,不太好明白。這裏我首先對些關鍵概念做個簡單名詞解釋,相信大家一看就懂:
² RO:Read-only
RW:Read-write.
ZI:Zero-initialized
² 段(Section):描述映像文件的代碼或數據塊。
² 輸入段(input section):它包含着代碼,初始化數據或標記了在應用程序運行之前必須要初始化爲0的一段內存。
² 輸出段(output section):它包含了一系列具有相同的RO,RW或ZI屬性的輸入段。
² 域(Regions):在一個映像文件中,一個域包含了1至3個輸出段。1個或多個域(一般是一個)組織在一起,就構成了最終的映像文件。
² 加載時地址:是指映像文件位於存儲器(在該映像文件沒有運行時,如FLASH)中的地址。
² 運行時地址:是指映像文件在運行時的地址(映象文件內存中運行)。
其實舉個實例來分析:我的源代碼編譯後生成的二進制zybootloader.bin文件就是一個域,打開ADS的”Realese Settings”標籤頁“listing”,選中“Image map””Symbols”,編譯生成目標文件時,ADS就會生成映象、符號的地址表。觀察發現它由三個輸出段組成:RO、RW、ZI,各輸出段大小如圖:
二、 源代碼詳細分析
說了這麼多,拿我自己的Bootloader啓動代碼來給大家分析一下,就再清楚不過的了。
Bootloader在進入C代碼運行之前,先經歷一些代碼位置無關的順序初始化例程:
² 獲取外部目標頭文件,聲明外部變量
² 主程序入口,定義中斷向量表,跳轉到復位異常
² 設置處理器工作模式爲SVC,並禁止中斷(bootloader運行過程中一直都要禁止)
² 初始化GPIO、內存控制寄存器
² 初始化電源管理寄存器,設置CPU工作頻率時鐘
² 爲六大中斷分配堆棧空間
以上步驟大家結合datasheet可以對照看懂。
最後一步,最最關鍵的啦,Bootloader將要做個“乾坤大挪移”動作,然後對ZI區清零:
Step 1:複製RO到SDRAM,程序將無縫切換到SDRAM空間運行;
Step 2:複製RW到SDRAM,這裏包括已初始化的全局變量;並且對ZI段未初始化的全局變量全部清零!OK,現在程序可以安全跳入C主函數運行了!下圖爲兩步的示意圖,其中:
|Image$$RO$$Base| ; RO段起始地址
|Image$$RO$$Limit| ; RO段結束地址加1
|Image$$RW$$Base| ; RW段起始地址
|Image$$RW$$Limit| ; RW段結束地址加1
|Image$$ZI$$Base| ; ZI段起始地址
|Image$$ZI$$Limit| ; ZI段結束地址加1
編譯器從R0_base 和RW_base來得到這些地址。
源代碼詳細分析:
;/************************************************************************ /
;Step 1 將FLASH的CODE拷貝到SDRAM中(RO_BASE指定的地址)
;===============================================
;bl xlli_icache_enable ; Enable I-Cache, D-Cache, BTB
;don't enable cache, make it's safe for download and boot!
;bl LedFlash ;led flash test after xlli_icache_enable, hzh
IF RELOCATE_ROM=1
adr r0, MAIN
ldr r2, BaseOfROM
cmp r0, r2
ldreq r0, TopOfROM
beq InitRam
ldr r3, TopOfROM
0
ldmia r0!, {r4-r7}
stmia r2!, {r4-r7}
cmp r2, r3
bcc %B0;無符號數小於
sub r2, r2, r3 ; r2 超出r3
sub r0, r0, r2 ;確保取得RW區精確起始地址
;Step 2:初始化SDRAM,將RW段拷貝到SDRAM
InitRam
ldr r2, BaseOfBSS
ldr r3, BaseOfZero
0
cmp r2, r3
ldrcc r1, [r0], #4
strcc r1, [r2], #4
bcc %B0
;對SDRAM ZI段清零
mov r0, #0
ldr r3, EndOfBSS
1
cmp r2, r3
strcc r0, [r2], #4
bcc %B1
;跳轉到C的主函數Main中
ldr pc, =GotoMain
GotoMain
;mov r0, #3
;bl LedSet
;IF :DEF: BOOTABLE
ldr r0, EndOfBSS
;ELSE
;ldr r0,=|Image$$ZI$$Limit|
;
bl PlatformMain
b GotoMain
ENDIF
BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|
二、 後續
已經今天暫且寫到這裏,PXA270的Bootloader已經做成功了,後面將會結合自己的開發筆記陸續對U-Boot 1.1.6在我的開發板PXA270上的移植流程和LINUX內核啓動代碼作分析。