U-Boot第一階段關鍵代碼理解(綠色加粗爲自己添加)

U-Boot第一階段關鍵代碼理解

轉自 http://blog.csdn.net/liaozc/article/details/6051274

綠色加粗部分是我添加的,以更加方便理解

1.u-boot程序的入口地址

要理解程序的入口地址,自然想到的是連接文件,首先看連接文件"/board/smdk2410/u-boot.lds"

ENTRY(_start)

SECTIONS

{

. = 0x00000000;

. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}

. = ALIGN(4);
.rodata : { *(.rodata) }

. = ALIGN(4);
.data : { *(.data) }

. = ALIGN(4);
.got : { *(.got) }

__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}

(1) 從ENTRY(_start)可以看出u-boot的入口函數是_start

(2) 從. = 0x00000000也許可以看出_start的地址是0x00000000,事實並不是這樣的,這裏的0x00000000無效,在連接的時候最終會被TETX_BASE所代替的,具體請參考u-boot根目錄下的config.mk.

(3) 網上很多說法是 _start=TEXT_BASE,我想這種說法也是正確的,實際上,不是TEXT_BASE這段代碼映射到0x0的地方,其實編譯器進行編譯,是按照鏈接文件進行的。也就是說,編譯的時候所有的地址都是相對於TEXT_BASE計算出來的。而這個地址是在連接文件裏面指定的爲0x338f0000,你可以看鏈接文件,可以看反彙編裏面的地址,全是0x33f8XXXX的地址。

1.1 cpu/arm920t/start.S部分代碼釋疑

(1)

_TEXT_BASE:
	.word TEXT_BASE

此處定義一彙編語言標籤_TEXT_BASE,它的值就是 “.word TEXT_BASE” 這句代碼所在的鏈接地址值; TEXT_BASE定義在board\smdk2410\config.mk文件中

(即0x33F80000)。以後通過引用_TEXT_BASE即可得到TEXT_BASE的值。

(2)

.globl _armboot_start
_armboot_start:
    .word _start

定義外部可以引用的變量_armboot_start,其值爲代碼“.word   _start"所在的鏈接地址值。

圖1 u-boot.map


編譯完成後,會生成u-boot.map 文件,如圖1所示。圖1顯示_armboot_start 所指的地址值是0x33f80044。用UltraEdit打開u-boot.bin,定位到偏移0x44處(0x44由_armboot_start 所在地址0x33f80044-TEXT_BASE得到),如圖2所示,此值就是_start 所指的鏈接地址的值 33 F8 00 00(小端模式)。

圖2 u-boot.bin


(3)

.globl _bss_start
_bss_start:
   .word __bss_start
__bss_start 的值定義在board/smdk2410/u-boot.lds文件中:

. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
__bss_start = .;  表示__bss_start 值就是當前位置的值。當前位置是多少呢?從下面一句 .bss : { *(.ss) } 知道。緊接該位置後面馬上就是放 bss 段數據了。所以,__bss_start 當然就是bss段的起始地址。_end 就是bss段的結束地址。

bss是這個鏈接腳本的最後一個段。start.S 就是以這個段的起始地址來計算要搬運的 u-boot 代碼的大小,即這個段前面的所有數據都將被搬到TEXT_BASE處,然後跳到start_armboot 處,即C語言的入口代碼。__bss_start這個值是多少? 我編譯後的值是0x33f979d4。可以u-boot.map文件中查到,如圖3所示,bss段是從0x33f979d4開始分配的。

圖3 u-boot.map


由圖1可知,_bss_start 所指的鏈接地址爲0x33f80048, 該處即存儲了bss段的起始地址。打開u-boot.bin,定位到偏移0x48處(0x48由_bss_start所在地址0x33f80048-TEXT_BASE得到)如圖2所示,此值確爲 33 F9 79 D4(小端模式)。

2.SDRAM初始化,lowleverl_init.S

_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4

不理解SMRDATA與_TEXT_BASE這兩個地址相減之後,到底是一個什麼值。爲什麼要相減呢?

我們的程序是存放在Flash中的,這裏面的地址稱爲加載地址,當然是從0x0這個地址開始,而程序中所用的標號編譯時都是基於_TEXT_BASE地址,我們稱爲鏈接或運行地址。這時加載地址和運行地址不相同,所以要求我們的代碼在還沒有搬移到TEXT_BASE(0x33f80000)這個位置以前是不能使用這些標號。如果直接調用標號,程序就飛了,只有運行在SDRAM中纔可以調用標號,因爲0x33f80000在SDRAM中。所以只能找到一個相對於0x0的偏移地址出來,才能得到真正的SMRDATA定義數據,也就是說此部分代碼與地址無關,是基於PC的偏移來進行的。下面來分析代碼:
ldr r0, =SMRDATA取得標號SMRDATA的絕對地址,它是大於_TEXT_BASE;ldr r1, _TEXT_BASE 取基地址(0x33f80000)。sub r0, r0, r1相減後得到SMRDATA相對於_TEXT_BASE偏移。

3.UBOOT啓動代碼重定位

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */

一直迷惑r0,r1什麼時候才相等,什麼時候纔不等,看看adr與ldr僞指令區別

adr是小範圍的地址讀取僞指令,指令將基於PC相對偏移的地址值讀取到寄存器中;ldr是大範圍的讀取地址僞指令,用於加載32爲立即數或一個地址到指定的寄存器中。

_start的鏈接地址在鏈接的時候與各指令之間的偏移地址已經確定,與位置無關,該偏移地址是負值,因爲_start始終是代碼段的起始地址。假設PC所指指令的鏈接地址與_start之間的偏移量爲x,該值爲負,所以在執行adr r0,_start時,相當於ldr r0,=PC+x(x<0),這個時候r0中的值即爲代碼段的起始地址。因爲PC中的地址是當前代碼的加載地址,和它所指的指令的鏈接地址基於_start(代碼的鏈接起始地址)的偏移相加後,就是代碼所存儲的起始地址,即加載起始地址。

在flash中運行時:r0 = PC+x = 0,這個時候r0 != r1,就需要搬運。
在RAM中運行時:r0 = PC+x =TEXT_BASE=0x33f80000,這個時候r0 = r1,就不需要搬運.

4.U-BOOT怎樣從4Ksteppingstone跳到RAM中執行

ldr pc, _start_armboot
_start_armboot: .word start_armboot

S3C2440的Nand Flash控制器會自動的把Nand Flash上的前4K數據搬移到4K內部RAM中,並把0x00000000設置內部RAM的起始地址,CPU從內部RAM的0x00000000位置開始運行。這個過程不需要程序干涉。程序員需要完成的工作,是把最核心的啓動程序放在Nand Flash的前4K中。由於Nand Flash控制器從Nand Flash中搬移到內部RAM的代碼是有限的,所以在啓動代碼的前4K裏,我們必須完成S3C2410的核心配置以及把啓動代碼(U-BOOT)剩餘部分搬到RAM中運行。

在這之前的程序完成了核心配置及代碼的拷貝,這兩條語句,ldr pc, _start_armboot中start_armboot是一個函數地址,在編譯的時候分配了一個絕對地址(大於0x33f80000),所以上面語句實際上是完成了一個絕對地址的跳轉。跳轉後進入c語言初始化。而爲什麼在start.S裏面有很多BL,B跳轉語句都沒有跳出4Ksteppingstone,原因是他們都是相對於PC的偏移跳轉,而不是絕對地址的跳轉。

跳轉指令,ARM有兩種跳轉方式。
(1) mov pc <跳轉地址〉
這種向程序計數器PC直接寫跳轉地址,能在4GB連續空間內任意跳轉。

(2) 通過B BL BLX BX可以完成在當前指令向前或者向後32MB的地址空間的跳轉(爲什麼是32MB呢?寄存器是32位的,此時的值是24位有符號數,所以32MB)。

B是最簡單的跳轉指令。要注意的是,跳轉指令的實際值不是絕對地址,而是相對地址——是相對當前PC值的一個偏移量,它的值由彙編器計算得出。BL非常常用。它在跳轉之前會在寄存器LR(R14)中保存PC的當前內容。BL的經典用法如下:

bl NEXT ; 跳轉到NEXT

……
NEXT:
……

mov pc, lr ; 從子程序返回

有了上面的基礎在分析U-Boot源代碼就簡單多了!!!!!!!


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