ARM上電啟動及Uboot代碼分析


版權聲明:本文為博主原創文章,未經博主同意不得轉載。 https://blog.csdn.net/qianlong4526888/article/details/27698707

注意:由於文檔是去年寫的,內有多個圖片。上傳圖片非常麻煩(須要截圖另存插入等等)。我把文章的PDF版本號上傳到了CSDN下載資源中。為了給自己賺點積分。所以標價2分,沒有積分的同學能夠直接留言跟我要,記得留下郵箱。

下面是文章內容,由於我懶得編輯圖片了,所以文章看來會非常不爽,強烈推薦點擊以上紅色鏈接下載pdf版。

文件編號:DCC01

版本號號:1.0

 

 

 

 

 

ARM上電啟動及Uboot代碼分析

部    門:

                        

作    者:

                    

聯系方式:

                   

日    期:

   2013.03.08                        

 


文件修訂記錄

時間

作者

主要修訂內容

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

文件夾

文件修訂記錄... 1

文件夾... 2

摘要... 4

1ARM上電取第一條指令流程... 5

1.1上電后的第一條指令在哪里?... 5

1.1.1norflash和nandflash的異同... 5

2Uboot.lds鏈接腳本分析... 7

2.1為什么要分析uboot鏈接腳本?... 7

2.2連接代碼詳細分析... 7

3Uboot中start.S文件分析... 9

3.1start.S詳細解釋... 9

3.1.1_start9

3.1.2reset9

3.1.3cpu_init_cp15. 11

3.1.4cpu_init_crit11

3.1.5lowlevel_init13

3.1.6s_init14

3.1.7call_board_init_f15

3.1.8board_init_f15

3.1.9relocate_code. 16

3.1.10clear_bss. 17

3.1.11jump_2_ram.. 17

3.2本章小結... 18

4板級初始化及跳入Linux內核執行... 19

4.1board_init_r19

4.1.1三級標題... 19

4.2本章小結... 19

5Uboot異常處理... 20

5.1Uboot異常向量表... 20

5.1.1異常處理入口函數... 20

5.1.2異常處理函數跳轉... 21

5.1.3異常真正處理函數... 22

5.2本章總結... 24

結論... 25

參考文獻... 26

問題總結及解答... 27

附錄... 29

 

摘要

網上關於ARM的bootloader(以Uboot為例)的啟動順序的資料有好多,可是對於Uboot的地址映射、體系結構級操作介紹非常少,都是直接開始Start.s代碼的閱讀。

本文擬詳細分析Uboot從上電。到第一條指令的執行。同一時候分析代碼對於cache、TLB等部件的操作過程。

下面內容以u-boot-2012.04.01源代碼為例,從網上非常easy下載該版本號。

1 ARM上電取第一條指令流程

1.1 上電后的第一條指令在哪里?

首先明白:對於ARM芯片。啟動時pc值由CPU設計者規定,不同的ARM CPU有不同的值。比如S3C2440芯片上電后PC值被硬件設計者規定為0x0;其它ARM芯片不一定是0x0。

第一章講述的上電取第一條指令過程以S3C2440為例,該芯片是ARMv4T架構,其它芯片在原理上相似。

S3C2440的啟動時讀取的第一條指令是在內存0x00地址處,無論是從nand flash還是nor flash啟動。

可是上電后內存中是沒有數據的,那么0x00地址處的指令是怎樣放進去的?針對不同的flash(nandflash、norflash),操作方式是不同的。下面講述從nandflash和norflash啟動的不同流程。

 

1.1.1  norflash和nandflash的異同

nandflash:價格低,容量大,適合大容量數據存儲,地址線和數據線共用I/O線。全部信息都通過一條線傳送,類比於PC的硬盤。

norflash:價格貴。容量小。適合小容量的程序或數據存儲,相似硬盤,可是能在當中執行程序;有獨立地址線、數據線

sdram:主要用於程序執行時的程序存儲、執行或計算。類比於PC的內存;

 

綜上:norflash比較適合頻繁隨即讀寫的場合。通經常使用於存儲代碼並直接在當中執行。nandflash用於存儲資料。

僅僅要知道以上大概差別即可。下面說明ARM從兩種flash啟動方式的異同。

 

1.1.1.1  ARM從nandFlash啟動

若從nandflash啟動,上電后nandflash控制器自己主動把nandflash存儲器中的0——4K內容載入到芯片內的起步石(Steppingstone。起步石這個機制是處理器中集成的功能,對程序猿透明),即內部SRAM緩沖器中,同一時候把內部SRAM的起始地址設置為0x0(不同的CPU上電后的PC值不盡同樣。對不同的CPU該值也不盡同樣)。然后把這段片內SRAM映射到nGCS0片選的空間。進而CPU開始從內部SRAM的0x0處開始取得第一條指令,該過程全部是硬件自己主動完畢,不須要程序代碼控制。

也許你有個疑問,為什么不能直接把nandflash映射到0x0地址處?非要經過內部SRAM緩沖?

答案是。nandflash根本沒有地址線,沒法直接映射,必須使用SRAM做一個載體。通過SRAM把剩余的nandflash代碼(即剩余的uboot啟動代碼)復制到SDRAM中執行。

若想從nandflash啟動,那么uboot最核心的代碼必須放在前4k完畢。這4k代碼要完畢ARM CPU的核心配置以及將剩余的代碼復制到SDRAM中(若從norflash啟動則沒有4k這個大小的限制,可是還會在完畢最基本的設置后進入SDRAM中執行)。

 

1.1.1.2 ARM從norflash啟動

若從norflash啟動,則norflash直接被映射到內存的0x0地址處(就是nGCS0。這里就不須要片內SRAM來輔助了。所以片內SRAM的起始地址不變,還是0x40000000)。然后cpu從0x00000000開始執行(也就是在Norfalsh中執行)。

      須要說明的是。uboot代碼段(.text段)起始位置必須是與上電后PC值一致,即編譯uboot時,TEXT_BASE宏必須設置成0x0 。反匯編uboot文件后,文本段第一條指令的地址也是0.

總結:

1、從norflash還是從nandflash啟動。是由ARM的OM1和OM0引腳組合決定

2、無論從norflash還是nandflash啟動,S3C2440上電后的pc值為0x0

3、假設某芯片上電后PC值不是0x0。假如是0x38ff0000,那么從norflash啟動時,硬件就要自己主動將其映射到0x38ff0000地址處;假設從nandflash啟動。那么硬件就要自己主動將nandflash中的前4K內容載入到0x38ff0000地址處。

2 Uboot.lds鏈接腳本分析

2.1 為什么要分析uboot鏈接腳本?

由於u-boot.lds決定了u-boot可執行映像的鏈接方式,以及各個段的裝載地址(裝載域)和執行地址(執行域),也就是說。Uboot.lds文件指定uboot.bin可執行文件放到ROM中的哪個地址、在執行時在RAM中執行的起始地址。詳細內容涉及裝載域和執行域的概念。這里不詳述。

2.2 連接代碼詳細分析

以u-boot-2012.04.01版本號為例。

 

 

總結:

1、SECTION后面的段都是依照順序放到內存中的。比如text段后面跟着rodata段

2、該文件里沒有指定段的載入地址(用AT命令),沒指定的情況下載入地址和執行地址是同樣的,也就是說在uboot.bin在rom和ram中的地址同樣。

 

3 Uboot中start.S文件分析

3.1  start.S詳細解釋

上面分析的鏈接腳本中已經規定,首先啟動的文件是arch/arm/cpu/armv7/start.S。

對於uboot的start.S,主要做的事情就是系統的各個方面的初始化。然后復制剩余代碼到RAM中繼續執行。

(1)    設置CPU模式

(2)    關閉cache, MMU, TLBs

(3)    設置棧,pll, mux, memory

(4)    設置watchdog, muxing,  and clocks

(5)    板級初始化

(6)    自我復制到RAM中。並跳轉到RAM中繼續執行。

下面內容依照程序執行流程進行解說。以ARMv7架構為例。

3.1.1 _start

文本段第一條指令就是一條跳轉指令:

依據1.1章的分析。假設uboot是燒寫到norflash中的,那么一上電的_start標號肯定是在0x0處。假設uboot已經啟動。程序執行過了relocate(見后面),那么該標號就會被移動到TEXT_BASE標號處,該標號是編譯uboot時程序猿指定的,詳細數值見開發板的board/~/config.mk文件。

3.1.2 reset

 

CPSR的位域見ARM手冊。下圖截圖方便參考:

 

 

 

 

 

 

 

 

 

3.1.3 cpu_init_cp15

 

 

 

3.1.4 cpu_init_crit

 

 

 

 

 

 

 

 

 

 

3.1.5 lowlevel_init

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3.1.6 s_init

 

 

 

 

 

 

 

3.1.7 call_board_init_f

3.1.8 board_init_f

 

 

 

3.1.9 relocate_code

為什么uboot代碼須要relocate?見問題總結及解答。

3.1.10 clear_bss

 

3.1.11 jump_2_ram

 

 

 

 

 

 

 

 

 

 

 

 

 

3.2 本章小結

主要分析了uboot啟動的第一階段代碼。

 

 

4 板級初始化及跳入Linux內核執行

4.1 board_init_r

該函數在u-boot-2012.04.01\arch\arm\cpu\armv7\omap-common\spl.c中。

其功能是:

(1)    初始化內存分配函數

(2)    假設系統有mmc設備。則初始化mmc設備

(3)    假設系統有nand設備。則初始化nand設備

(4)    進入uboot命令循環或者直接開始執行linux內核。

眼下臨時不須要詳細分析該部分代碼。后期若須要會加上。

 

 

 

 

 

 

 

千萬不要刪除行尾的分節符,此行不會被打印。

“結論”曾經的全部正文內容都要編寫在此行之前。


5 Uboot異常處理

5.1 Uboot異常向量表

緊跟b reset后面的就是異常向量表,發生異常后pc會被自己主動置為對應的值,進入對應異常處理程序。

5.1.1 異常處理入口函數

 

 

 

5.1.2 異常處理函數跳轉

/*

 * exception handlers

 */

      .align     5

undefined_instruction:

      get_bad_stack

      bad_save_user_regs

      bl    do_undefined_instruction

 

      .align     5

software_interrupt:

      get_bad_stack_swi

      bad_save_user_regs

      bl    do_software_interrupt

 

      .align     5

prefetch_abort:

      get_bad_stack

      bad_save_user_regs

      bl    do_prefetch_abort

 

      .align     5

data_abort:

      get_bad_stack

      bad_save_user_regs

      bl    do_data_abort

 

      .align     5

not_used:

      get_bad_stack

      bad_save_user_regs

      bl    do_not_used

 

#ifdef CONFIG_USE_IRQ        //假設在uboot中啟用了用戶中斷。則跳入對應處理函

                         //數執行

      .align     5

irq:

      get_irq_stack

      irq_save_user_regs

      bl    do_irq

      irq_restore_user_regs

 

      .align     5

fiq:

      get_fiq_stack

      /* someone ought to write a more effective fiq_save_user_regs*/

      irq_save_user_regs

      bl    do_fiq

      irq_restore_user_regs

 

#else            //假設沒有配置。則走還有一條路徑。

 

      .align     5

irq:

      get_bad_stack

      bad_save_user_regs

      bl    do_irq

 

      .align     5

fiq:

      get_bad_stack

      bad_save_user_regs

      bl    do_fiq

 

#endif /* CONFIG_USE_IRQ */

#endif /* CONFIG_SPL_BUILD*/

 

 

5.1.3 異常真正處理函數

// u-boot-2012.04.01\arch\arm\lib\Interrupts.c

void do_undefined_instruction (struct pt_regs *pt_regs)

{

      printf ("undefinedinstruction\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_software_interrupt (struct pt_regs *pt_regs)

{

      printf ("software interrupt\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_prefetch_abort (struct pt_regs *pt_regs)

{

      printf ("prefetchabort\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_data_abort (struct pt_regs *pt_regs)

{

      printf ("dataabort\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_not_used (struct pt_regs *pt_regs)

{

      printf ("notused\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

void do_fiq (struct pt_regs *pt_regs)

{

      printf ("fastinterrupt request\n");

      show_regs (pt_regs);

      bad_mode ();

}

 

#ifndef CONFIG_USE_IRQ

void do_irq (struct pt_regs *pt_regs)

{

      printf ("interruptrequest\n");

      show_regs (pt_regs);

      bad_mode ();

}

#endif

void bad_mode (void)         //以上異常處理函數都跳轉到bad_mode。該函數僅僅是

                         //掛起CPU,木有詳細處理。

{

      panic ("Resetting CPU ...\n");

      reset_cpu (0);

}

 

5.2 本章總結

總結uboot下異常處理流程。發現ARMv7下的uboot沒有實現異常處理,ARM的其它架構有。有可能是由於uboot代碼不夠新的原因。Uboot中的ARM異常處理流程都同樣。

結論

參考文獻

[1]   

[2]   

[3]   

 

 

 

 

千萬不要刪除行尾的分節符。此行不會被打印。

問題總結及解答

1、對於載入時地址和執行時地址不同的段。執行時它是怎么跳轉到執行時地址的?

答: 連接地址<==>執行地址

      存儲地址<==>載入地址

(1)對於有操作系統時,執行地址與載入地址不同。在載入過程中裝載器就把段載入到它應該去的連接地址處(也就是生成該段時的執行地址)

(2)對於uboot,執行地址與載入地址不同一時候。須要它自己(比如前4k代碼)將自己載入到執行地址處執行。

Uboot.lds文件里起始地址是0x00,可是config.mk中的TEXT_BASE是0x57e00000,可是生成的uboot反匯編文件里。為什么start.s的第一條指令地址也是0x57e00000?不應該是0x00么?由於start.s的載入地址和執行地址都是0x00啊。?

答:Uboot.lds的0x00:

跟在SECTION后面的第一條

location counter,總是默認初始化為0。config.mk中的TEXT_BASE就是ROM在CPU上的地址。也就是說。不同的CPU已經規定了不同的ROM地址

2、關於為何不能直接用mov指令,而非要用adr偽指令?

把全部uboot代碼復制到內存新地址處。

在分析uboot的start.S中,看到一些指令,比方:

adr r0, _start

認為好像能夠直接用mov指令實現即可。為啥還要這么麻煩地,去用ldr去實現?

關於此處的代碼。為何要用adr指令:

adr r0, _start

其被編譯器編譯后。會被翻譯成:sub r0, pc, #172

而不直接用mov指令直接將_start的值賦值給r0,相似於這樣:

mov r0, _start

呢?

其原因主要是,

sub r0, pc, #172

這種代碼。所處理的值。都是相對於PC的偏移量來說的。這種代碼中,沒有絕對的物理地址值。都是相對的值,利用產生位置無關代碼。由於假設用mov指令:

mov r0, _start

那么就會被編譯成這種代碼:

mov r0, 0x33d00000

假設用了上面這種代碼:

mov r0, 0x33d00000

那么,假設整個代碼,即要執行的程序的指令。被移動到其它位置,那么

mov r0, 0x33d00000

這行指令,執行的功能,就是跳轉到絕對的物理地址,而不是跳轉到相對的_start的位置了,就不能實現我們想要的功能了,這樣包括了絕對物理地址的代碼,也就不是位置無關的代碼了。

與此相對,這行指令:

sub r0, pc, #172

即使程序被移動到其它位置,那么該行指令還是能夠跳轉到相對PC往前172字節的地方。也還是我們想要的_start的位置。這樣包括的都是相對的偏移位置的代碼,就叫做位置無關代碼。其長處就是不用操心你的代碼被移動,即使程序的基地址變了,全部的代碼的相對位置還是固定的。程序還是能夠正常執行的。

關於,之所以不用上面的:

mov r0, 0x33d00000

相似的代碼。除了上面說的,不是位置無關的代碼之外,其還有個潛在的問題,那就是。關於mov指令的源操作數。此處即為0x33d00000,不一定是合法的mov 指令所同意的值。

【總結】

之所以用adr而不用mov。主要是為了生成地址無關代碼。以及由於不方便推斷一個數,是否是有效的mov的操作數。

 

3、為什么uboot代碼須要relocate?

由於uboot啟動時不在片外RAM中,為了加快執行,須要將uboot又一次復制到RAM中執行。

附錄

 

千萬不要刪除行尾的分節符,此行不會被打印!



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM