雖然已經寫了幾篇關於uboot移植的、但是覺得整體對uboot的理解還是只停留在copy的層面。
狠下心來,從代碼進行uboot的分析,並從新移植一次uboot。
這次更側重於記錄代碼分析心得。
使用uboot的版本仍為2010.3版本。
這里有一個很是詳盡的start.S分析網站,分析過程借鑒了網站的方法,但也有一些不同的地方
http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/html/uboot_starts_analysis.html
總的不同有如下
在網站的分析中,基於代碼為arm920t,即ARM9系列芯片,ARM9系列芯片的初始化過程為:
1、設置CPU模式 2、關閉看門狗 3、關閉中斷 4、設置堆棧sp指針 5、清除bss段 6、異常中斷處理
然后我現在選擇的修改模板是arm1176,即ARM11系列芯片,ARM11系列芯片的初始化過程為:
1、設置CPU模式 // -- 關閉看門狗 // -- 關閉中斷 2、初始化MMU // ++ 3、設置堆棧sp指針 4、清除bss段 5、異常中斷處理
兩種芯片的start.S的區別一目了然。
接着開始分析代碼了。
一切的開始:start.S (文件路徑為/cpu/arm1176/start.S)
為了方便跳轉閱讀,啟用行號。
為避免誤導,在此說明:
此時我使用的是未經修改的uboot2010版本的start.S,在這篇分析中不會對它進行任何修改。
在隨后的blog中才對start.S進行修改並闡述理由,修改處必有類似 git diff 的說明
1、頭文件
1 /* 2 * …… 3 */ 4 5 #include <config.h> 6 #include <version.h> 7 #ifdef CONFIG_ENABLE_MMU 8 #include <asm/proc/domain.h> 9 #endif 10 #include <asm/arch/s3c6410.h> 11 12 #if !defined(CONFIG_ENABLE_MMU) && !defined(CONFIG_SYS_PHY_UBOOT_BASE) 13 #define CONFIG_SYS_PHY_UBOOT_BASE CONFIG_SYS_UBOOT_BASE 14 #endif
頭文件部分不必贅述,但是這里有一個不得不說的地方
13 #define CONFIG_SYS_PHY_UBOOT_BASE CONFIG_SYS_UBOOT_BASE
為了解析這個定義,找到了 /include/configs/smdk6400.h
/* NAND U-Boot load and start address */ #define CONFIG_SYS_UBOOT_BASE (CONFIG_SYS_MAPPED_RAM_BASE + 0x07e00000)
繼續跳轉搜索 CONFIG_SYS_MAPPED_RAM_BASE:
#ifdef CONFIG_ENABLE_MMU #define CONFIG_SYS_MAPPED_RAM_BASE 0xc0000000 #define CONFIG_BOOTCOMMAND "nand read 0xc0018000 0x60000 0x1c0000;" \ "bootm 0xc0018000" #else #define CONFIG_SYS_MAPPED_RAM_BASE CONFIG_SYS_SDRAM_BASE #define CONFIG_BOOTCOMMAND "nand read 0x50018000 0x60000 0x1c0000;" \ "bootm 0x50018000" #endif
很顯然了,在MMU工作的狀態下:
#define CONFIG_SYS_MAPPED_RAM_BASE 0xc0000000
MMU工作就是開發板播到從NAND flash啟動的狀態。
而在MMU未工作的狀態下:
#define CONFIG_SYS_MAPPED_RAM_BASE CONFIG_SYS_SDRAM_BASE .... #define CONFIG_SYS_SDRAM_BASE 0x50000000
從這里,可以理解在使用dnw下載的時候,Download Address為
#define CONFIG_SYS_UBOOT_BASE (CONFIG_SYS_MAPPED_RAM_BASE + 0x07e00000) // 0x5000000 + 0x07e00000 = 0x57e00000
uboot是bootloader的一種,要更為准確地描述這兩種狀態可以說為
1、啟動加載(Boot loading)模式,即自主"(Autonomous)模式;
2、下載(Downloading)模式。
更為詳細的描述可以參詳
http://blog.csdn.net/r91987/article/details/6695007
接下來進入正文分析了
1 /* 2 ************************************************************************* 3 * 4 * Jump vector table as in table 3.1 in [1] 5 * 6 ************************************************************************* 7 */ 8 9 .globl _start 10 _start: b reset //跳轉到reset處執行,即下一段代碼的44行 //但此時不必急着看reset的執行代碼,可以繼續順序往下 11 #ifndef CONFIG_NAND_SPL //定義NAND_SPL時 12 ldr pc, _undefined_instruction //ldr = Load Register 13 ldr pc, _software_interrupt //軟件中斷 14 ldr pc, _prefetch_abort //預取指中止 15 ldr pc, _data_abort //數據中止 16 ldr pc, _not_used //保留 17 ldr pc, _irq //IRQ中斷 18 ldr pc, _fiq //FIQ中斷 19 //這里的七種異常就是ARM的七種異常處理類型,對應的聲明在 20 - 33行 20 _undefined_instruction: //.word = 32bit , 這里可以理解為 _undef.. = &undef.. 21 .word undefined_instruction //這里將地址放入 _undef.. ,而_undef的地址又放入pc 22 _software_interrupt: //雙層取址,所以最終送入pc的數據為 undefined_instruction 23 .word software_interrupt //以下含義相同 24 _prefetch_abort: 25 .word prefetch_abort //pc 是 ARM 的指令寄存器 26 _data_abort: //將這些異常送入 pc ,意為讓 ARM 運行這些指令的初始化代碼(后文可見) 27 .word data_abort 28 _not_used: 29 .word not_used 30 _irq: 31 .word irq 32 _fiq: 33 .word fiq 34 _pad: 35 .word 0x12345678 /* now 16*4=64 */ 36 #else 37 . = _start + 64 38 #endif 39 40 .global _end_vect 41 _end_vect: 42 .balignl 16,0xdeadbeef // .balignl 為對其指令,意為讓以下的代碼按16位對其 //不足為則補上0xdeadbeef //很有意思的dead beef,為數不多的能用16進制表示的單詞
接下來為正式的啟動指令了
1 /* 2 ************************************************************************* 3 * 4 * Startup Code (reset vector) 5 * 6 * do important init only if we don't start from memory! 7 * setup Memory and board specific bits prior to relocation. 8 * relocate armboot to ram 9 * setup stack 10 * 11 ************************************************************************* 12 */ 13 14 _TEXT_BASE: 15 .word TEXT_BASE //這里是 .text 的 base ,即代碼段的基址 16 17 /* 18 * Below variable is very important because we use MMU in U-Boot. 19 * Without it, we cannot run code correctly before MMU is ON. 20 * by scsuh. 21 */ 22 _TEXT_PHY_BASE: //上面的注釋很清晰了 23 .word CONFIG_SYS_PHY_UBOOT_BASE 24 25 .globl _armboot_start //此處聲明了 _armboot_start 指向 _start 26 _armboot_start: 27 .word _start 28 29 /* 30 * These are defined in the board-specific linker script. 31 */ 32 .globl _bss_start //此處的標號其實最終指向的是鏈接腳本文件(.lds)中的定義 33 _bss_start: 34 .word __bss_start 35 36 .globl _bss_end //同上 37 _bss_end: 38 .word _end 39 40 /* 41 * the actual reset code 42 */ 43 44 reset: //這里就是start之后跳轉的地方了 45 /* 46 * set the cpu to SVC32 mode 47 */ //SVC模式也成為管理模式,是操作系統的一種保護模式 48 mrs r0, cpsr //mrs為讀寄存器指令 49 bic r0, r0, #0x3f //清除r0的低7位 50 orr r0, r0, #0xd3 //將r0置為0b1101_0011,我們只看低5位 51 msr cpsr, r0 //查CPSR處理器模式位知SVC模式位就是0b1_0011
至此,CPU的SVC模式設置成功了。
最后補充一個內容,.lds文件是編譯腳本文件,展開可以看成ld script。
這里的 ld 對應於編譯器的 arm-linux-ld。
有關於.lds文件的說明,可以參閱
http://blog.csdn.net/pottichu/article/details/4261289
在后面的篇幅中,也會涉及.lds文件的修改。