1. 程式人生 > >uboot之位置無關代碼解析

uboot之位置無關代碼解析

nor flash 初始 處理器 特性 直接 全局函數 判斷 編譯 但是

在之前的話

  新年過去了,那麽久沒有好好學習,感覺好頹廢,現在就uboot的一些基礎問題做一些筆記,順便分享給大家,不過由於見識有限,如果有不足之處請多多指教。

位置無關?什麽意思?我們先了解一些基礎知識。。。。。

  我們都知道我們寫的代碼最後是運行在內存SDRAM或者SRAM,通常是SDRAM)中的,但是在運行之前他們是保存在諸如nandflash等非易失存儲設備中的,而這些存儲設備的地址要映射到CPU能夠尋找的地址上(一般映射在0X0地址上,這個後面詳細解釋),這樣才能得到要運行的代碼。而代碼要運行的內存(這裏就假設是SDRAM)也要映射到CPU上(肯定是和nand那些存儲器不一樣的地址,例如三星的

2440的就映射到了0x30000000上),所以說存儲在nand或者flash的代碼最終得復制到內存上運行的。這裏就記住代碼存儲的地方(加載地址)和代碼要運行的地方(運行地址)是不一樣的

  現在說一下地址映射的問題。在學習ARM時,我們一般會遇到兩種啟動方式,一個是nand啟動,一個是nor啟動。這兩種啟動方式都是映射到0x0地址的,但是映射方式稍有不同。所以先了解一下這兩種介質的硬件特性:

     nand:由於nand存儲器的硬件特性,代碼是無法在他上面運行的,他只有存儲代碼的功能。

     nor:他不僅可以存儲代碼,還可以讓代碼運行,但是他只能進行讀,不能進行寫的操作,但畢竟是可以運行代碼啦。

  所以nand啟動的話,如果沒有進行特殊手段,代碼是無法得到的,在ARM中,他是通過硬件自動把nand前面的4K代碼復制到一塊映射到0x0地址上的SRAM中的,記住這是硬件自動完成的。這樣4K代碼就在SRAM上了,我們知道SRAM是能運行代碼的(可讀可寫)。但是這個SRAM只有4K大小,要是存儲在nand的代碼不止4K,只在SRAM上面運行是無法完成代碼設定的所有功能的。為了解決這個問題,於是就在這4K的代碼中完成一個可以將nand的代碼復制到其他更大的內存上(這裏是SDRAM),這樣就可以運行所有代碼了。

  nor啟動的話,由於nor可以運行代碼,所以就不用什麽SRAM了,直接把nor映射到

0x0位置上就可了,如果nor空間夠大,甚至可以不用重定位代碼,只是nor無法進行寫功能,讀取的效率又沒SDRAM,加上為了兼容nand啟動(不用寫兩套代碼),也就跟nand啟動一樣只運行前面4K,剩下復制到SDRAM上再繼續運行。

  問題又來了,我們知道代碼經過編譯,鏈接後才可以得到可以運行的代碼,而這代碼只有在鏈接時指定的位置上才能運行。對於2440,鏈接文件指定的位置一般是在SDRAM上面的,可是nand啟動時,前面的4K代碼卻在SRAM中運行了

  這會有什麽問題?我們知道編寫好的代碼的地址在鏈接時就已經安排好了。例如2440鏈接文件指定鏈接地址是0x30008000,那麽所有代碼地址就是從0x30008000開始的。而在啟動時,代碼卻是在0x0開始運行的,如果不進行特殊處理,肯定會有問題,比如我們調用一個函數,而這個函數地址是在SDRAM上的(鏈接時已經確定),並且這時代碼還沒復制到SDRAM上,也就是說代碼還在nand或者在4KSRAM裏面,在SDRAM是找不到要調用的函數的,那樣就會無法執行這個函數。

  如果在還在nand裏面肯定是沒辦法了,這個代碼是無法運行的必須廢棄,如果在4KSRAM就有辦法了。於是就出現了位置無關指令:

    位置無關/相關指令:

      B BL ADR MOV ADD等,這些指令都是位置無關指令

      LDR STR等指令是位置有關指令

位置無關什麽意思呢?

  既然這個時候SDRAM還沒有代碼,那就不去那裏找了嘛,我們去別的地方找。別的地方是哪裏呢?這個時候代碼是運行在SRAM上的,處理器的PC寄存器也是指向這個地方的,有4K代碼也是在這個地方的。那我就不用鏈接指定的地址來尋找想要的代碼,我設計一些是與PC寄存器指向的位置有聯系的尋址指令來尋址,這樣PC寄存器指向哪裏,我就在哪裏找我想要的代碼,而不是去鏈接指定的地址去找

  位置無關指令就是這樣的指令,他不去鏈接指定位置尋找代碼,而是在PC寄存器指向的位置相對它前後32M的範圍尋找代碼,也就是說只要我想要調用的代碼在PC指向位置前後的32M範圍內,就能找到(這是硬件完成的,我們知道位置無關指令就是這個意思就行)。當然,我們的SRAM只有4K,所以尋找的範圍肯定就只能在這4K裏面才行了,超過了也是找不到的。所以要求我們的重定位代碼必須在這4K裏面,必要的硬件初始化代碼,內存初始化代碼,nand初始化代碼等等,必須在4K以內完成,否則就會出問題。所以位置無關指令的尋址是基於當前PC寄存器位置來尋址的。

下面根據uboot的代碼進行講解

1.內存控制器初始化代碼

_TEXT_BASE:

.word TEXT_BASE /*根據鏈接地址得知這是0x30000000*/

.globl lowlevel_init /*定義lowlevel_init為全局函數*/

lowlevel_init:

ldr r0, =SMRDATA /*鏈接後,SMRDATA是在SDRAM上某個位置的,但是這時候SDRAM還沒任何代碼,代碼現在還在SRAM上,也就說ldr指令是位置相關指令*/

ldr r1, _TEXT_BASE /*0x30000000*/

sub r0, r0, r1 /* SMRDATA _TEXT_BASE就是13個寄存器在SRAM上的地址 */

ldr r1, =BWSCON /* R1指向內存控制器的寄存器地址上 */

add r2, r0, #13*4 /*13個寄存器,每個32位寬度*/

0:

ldr r3, [r0], #4 /*13個寄存器的值逐一賦值給對應的寄存器*/

str r3, [r1], #4

cmp r2, r0

bne 0b

/* everything is fine now */

mov pc, lr

.ltorg

/* the literal pools origin */

SMRDATA: /* 下面是13個寄存器的值 */

.word … …

.word … …

… …

2.nor啟動的重定位代碼

  adr r0, _start /*adr是位置無關指令,因為nor啟動,這時第一條指令被映射到nor上的0x0位置,所以R0=0x0,如果是RAM啟動,那_start就是存在0x30000000 */

ldr r1, _TEXT_BASE /* 位置相關指令,R1=0x30000000 */

/* 判斷U-Boot是否是下載到RAM中運行,若是,則不用 再復制到RAM中了,這種情況通常在調試U-Boot時才發生 */

cmp r0, r1 /*_start等於_TEXT_BASE說明是下載到RAM中運行 */

beq stack_setup

/* 以下直到nand_boot標號前都是NOR Flash啟動的代碼 */

ldr r2, _armboot_start /*這裏其實就是_start的地址*/

ldr r3, _bss_start

sub r2, r3, r2 /* 得到代碼大小 */

add r2, r0, r2 /* 得到代碼結束地址 */

/* 搬運U-Boot自身到RAM*/

copy_loop:

ldmia r0!, {r3-r10} /* 從地址為[r0]NOR Flash中讀入8個字的數據 */

stmia r1!, {r3-r10} /* r3r10寄存器的數據復制給地址為[r1]的內存 */

cmp r0, r2 /* until source end addreee [r2] */

ble copy_loop

b stack_setup /* 跳過NAND Flash啟動的代碼 */

以上就是位置無關代碼的解析了,uboot在運行第二階段之前,絕大部分的指令都是位置無關指令,這樣才能保證代碼能夠運行。

uboot之位置無關代碼解析