1. 程式人生 > >(三) u-boot 啟動分析_第一階段

(三) u-boot 啟動分析_第一階段

本文重點在於分析 uboot 啟動流程以及 uboot 自身的細節,比如棧空間的劃分、如何設定 tag 、如何新增一個自定義命令等。但是不涉及基本的硬體驅動的分析,比如記憶體初始化、時鐘初始化、mmu 、nandflash 等等這些詳細細節不是我們的重點。

u-boot 版本:  uboot 1.1.6

使用的開發板: JZ2440V3   

一、連結指令碼

    uboot1.1.6 的連結指令碼 u-boot.lds 位於 u-boot-1.1.6\board\smdk2410 目錄下:

  1. ENTRY(_start)  
  2. SECTIONS  
  3. {  
  4.     . = 0x00000000;  //起始地址
  5.     . = ALIGN(4);  //4位元組對齊
  6.     .text      :  //test指程式碼段,上面3行標識是不佔用任何空間的
  7.     {  
  8.       cpu/arm920t/start.o   (.text)  //這裡把start.o放在第一位就表示把start.s編譯時放到最開始,這就是為什麼把uboot燒到起始地址上它肯定執行的是start.s
  9.       *(.text)  
  10.     }  
  11.     . = ALIGN(4);  //前面的 “.” 代表當前值,是計算一個當前的值,是計算上面佔用的整個空間,再加一個單元就表示它現在的位置
  12.     .rodata : { *(.rodata) }  
  13.     . = ALIGN(4);  
  14.     .data : { *(.data) }  
  15.     . = ALIGN(4);  
  16.     .got : { *(.got) }  
  17.     . = .;  
  18.     __u_boot_cmd_start = .;  
  19.     .u_boot_cmd : { *(.u_boot_cmd) }  
  20.     __u_boot_cmd_end = .;  
  21.     . = ALIGN(4);  
  22.     __bss_start = .;  
  23.     .bss : { *(.bss) }  
  24.     _end = .;  
  25. }  
    連結地址為 0 ?顯然不應該,實際編譯的時候執行的大概是這樣一條語句:

    arm-Linux-ld -Bstatic -T u-boot.lds -Ttext 0x33F80000 start.o ...

    0x33F80000 在 board/smdk2410/config.mk 中定義,為 TEXT_BASE = 0x33F80000 (連結地址)

    整個 uboot 的入口 _start 包含在 cpu/arm920t/start.S 中

二、第一階段

uboot 的第一階段主要工作是作基本的初始化工作,例如關看門狗、初始化時鐘、初始化 sdram 以及程式碼重定位,為第二階段做準備。這裡的程式碼都是沒有經過移植的原始碼~!

  1、設定異常向量

  1. .globl _start  /*宣告一個符號可被其它檔案引用,相當於聲明瞭一個全域性變數,.globl與.global相同*/
  2. _start: b       reset  /* 復位,b是不帶返回的跳轉(bl是帶返回的跳轉),意思是無條件直接跳轉到reset標號出執行程式*/
  3.     ldr pc, _undefined_instruction  /* 未定義指令向量 ldr相當於mov操作*/
  4.     ldr pc, _software_interrupt  /* 軟體中斷向量 */
  5.     ldr pc, _prefetch_abort  /* 預取指令異常向量 */
  6.     ldr pc, _data_abort  /* 資料操作異常向量 */
  7.     ldr pc, _not_used  /* 未使用 */
  8.     ldr pc, _irq  /* irq中斷向量 */
  9.     ldr pc, _fiq  /* fiq中斷向量 */
  10.  /* 中斷向量表入口地址 */ 
  11. _undefined_instruction: .word undefined_instruction  /*就是在當前地址,即_undefined_instruction 處存放 undefined_instruction*/
  12. _software_interrupt:    .word software_interrupt  
  13. _prefetch_abort:    .word prefetch_abort  
  14. _data_abort:        .word data_abort  
  15. _not_used:      .word not_used  
  16. _irq:           .word irq  //word偽操作用於分配一段字記憶體單元(分配的單元都是字對齊的),並用偽操作中的expr初始化
  17. _fiq:           .word fiq  /* now 16*4=64 */
  18.     .balignl 16,0xdeadbeef  
    第一條 b reset ,決定了U-Boot啟動後將自動跳轉到標號“reset”處執行。因為剛開始執行時程式碼都是在片內 sram 裡,我們在 sram 裡調來調去的話就需要用位置無關碼,那麼 b 就是最佳選擇,因為它是相對跳轉。
    ldr    pc, _undefined_instruction

    _undefined_instruction:.word undefined_instruction

    感覺真是在賣弄,兩條指令連起來的結果就是,CPU 會跳轉到 undefined_instruction 連結地址處去執行(sdram裡)。

    那麼其實,一條 ldr pc,=undefined_instruction 就夠了,它是位置有關碼,絕對跳轉。

    或許,uboot 的作者別有用意我沒看透,不知道這是不是個伏筆。在u-boot2015裡,就只有一個 reset 一個異常入口了。

  2、進入管理SVC模式

  1. reset:  
  2.     /* 
  3.      * set the cpu to SVC32 mode 
  4.      */
  5.     mrs r0,cpsr  
  6.     bic r0,r0,#0x1f  
  7.     orr r0,r0,#0xd3  
  8.     msr cpsr,r0  
    ARM每種工作模式除R0~R15共16個暫存器外,還有第17個暫存器CPSR,叫做 當前程式狀態暫存器,CPSR中一些位被用於標識各種狀態,一些位被用於標識當前出於什麼工作模式。

    

    有時候我們會碰到 CPSR_C ,它其實就是 CPSR 的低 8 位而已。


    I:1-禁止irq中斷 0-允許irq中斷

    F:1-禁止fiq中斷 1-允許fiq中斷

    T:1-Thumb 0-arm 指令集

    M0-M4 : 工作模式


    說了這麼多,前邊兩條指令,先將 cpsr 低 5位 清零,然後或上 1101 0011B

    禁止了 irq 和 fiq 中斷,工作在 arm 指令集,管理模式。

  3、關看門狗

  1. #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
  2.     ldr     r0, =pWTCON  
  3.     mov     r1, #0x0  
  4.     str     r1, [r0]  
  4、遮蔽中斷
  1. /* 
  2.      * mask all IRQs by setting all bits in the INTMR - default 
  3.      */
  4.     mov r1, #0xffffffff  
  5.     ldr r0, =INTMSK  
  6.     str r1, [r0]  
  7. # if defined(CONFIG_S3C2410)
  8.     ldr r1, =0x3ff  
  9.     ldr r0, =INTSUBMSK  
  10.     str r1, [r0]  
  11. # endif
    前邊通過 cpsr 禁止 irq 和 fiq 使 cpu 不接受來自中斷控制器的中斷請求,而這裡通過中斷遮蔽使中斷髮生時,中斷控制暫存器自身就不會上報給 cpu雙保險
  5、設定時鐘
  1. /* FCLK:HCLK:PCLK = 1:2:4 */
  2. /* default FCLK is 120 MHz ! */
  3. ldr r0, =CLKDIVN  
  4. mov r1, #3  
  5. str r1, [r0]  
 6、關 I/D cache 關 TLB
  1. /* 
  2.  * flush v4 I/D caches 
  3.  */
  4. mov r0, #0  
  5. mcr p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache */
  6. mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB */

    協處理器 p15 在2410的資料手冊附錄有介紹

    或者參考:http://blog.sina.com.cn/s/blog_858820890102v1gc.html

  7、關 mmu 

  1. /* 
  2.  * disable MMU stuff and caches 
  3.  */
  4. mrc p15, 0, r0, c1, c0, 0  
  5. bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)  
  6. bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)  
  7. orr r0, r0, #0x00000002 @ set bit 2 (A) Align  
  8. orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache  
  9. mcr p15, 0, r0, c1, c0, 0  
    這裡主要涉及 P15 的 C1暫存器,用到的各位:


  8、初始化 sdram 控制器

  1. .globl lowlevel_init  
  2. lowlevel_init:  
  3.     /* memory control configuration */
  4.     /* make r0 relative the current location so that it */
  5.     /* reads SMRDATA out of FLASH rather than memory ! */
  6.     ldr     r0, =SMRDATA  
  7.     ldr r1, _TEXT_BASE  
  8.     sub r0, r0, r1  
  9.     ldr r1, =BWSCON /* Bus Width Status Controller */
  10.     add     r2, r0, #13*4  
  11. 0:  
  12.     ldr     r3, [r0], #4  
  13.     str     r3, [r1], #4  
  14.     cmp     r2, r0  
  15.     bne     0b  
  16.     /* everything is fine now */
  17.     mov pc, lr  
  18.     .ltorg  
  19. /* the literal pools origin */
  20. SMRDATA:  
  21.     .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))  
  22.     .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))  
  23.     .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))  
  24.     .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))  
  25.     .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))  
  26.     .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))  
  27.     .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))  
  28.     .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))  
  29.     .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))  
  30.     .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)  
  31.     .word 0x32  
  32.     .word 0x30  
  33.     .word 0x30  
    寫裸機程式碼的入門操作,初始化 sdram 暫存器。

  9、程式碼重定位

  1. relocate:               /* relocate U-Boot to RAM       */
  2.     adr r0, _start      /* r0 <- current position of code   */
  3.     ldr r1, _TEXT_BASE      /* test if we run from flash or RAM */
  4.     cmp     r0, r1                  /* don't reloc during debug         */
  5.     beq     stack_setup  
  6.     ldr r2, _armboot_start  
  7.     ldr r3, _bss_start  
  8.     sub r2, r3, r2      /* r2 <- size of armboot            */
  9.     add r2, r0, r2      /* r2 <- source end address         */
  10. copy_loop:  
  11.     ldmia   r0!, {r3-r10}       /* copy from source address [r0]    */
  12.     stmia   r1!, {r3-r10}       /* copy to   target address [r1]    */
  13.     cmp r0, r2          /* until source end addreee [r2]    */
  14.     ble copy_loop  
   adr 位置無關碼,獲取_start實際當前位於的地方,_TEXT_BASE 為 0x33f80000 ,這裡判斷的是程式碼是否直接執行在 sdram 裡了,如果是就不需要重定位了。

    拷貝範圍:_start 至 _bss_start 前,拷貝到 0x33f80000 處。

      33f80048 <_bss_start>:
      33f80048:    33fb064c

0x33fb064c - 0x33f80000 = 193K ,什麼意思呢?(整個 uboot 除了 bss 段) > 4k,如果是 nandflash 啟動的話,SRAM只會複製NAND Flash儲存器的前4K位元組過去執行,我們需要複製的程式碼為193K,顯然需要初始化 nandflash 並從裡面讀取 uboot 到核心,而我們到這裡還沒有初始化NandFlash,,從而得到預設 uboot是從norflash啟動,不支援 nandflash 啟動。

  10、設定棧

  1. stack_setup:  
  2.     ldr r0, _TEXT_BASE      /* upper 128 KiB: relocated uboot   */
  3.     sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
  4.     sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
  5. #ifdef CONFIG_USE_IRQ
  6.     sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)   
  7. #endif
  8.     sub sp, r0, #12     /* leave 3 words for abort-stack    */
  9. clear_bss:  
  10.     ldr r0, _bss_start      /* find start of bss segment        */
  11.     ldr r1, _bss_end        /* stop here                        */
  12.     mov     r2, #0x00000000     /* clear                            */
  13. clbss_l:str r2, [r0]        /* clear loop...                    */
  14.     add r0, r0, #4  
  15.     cmp r0, r1  
  16.     ble clbss_l  
    TEXT_BASE = 0x33F80000 其它的巨集在 smdk2410.h (include\configs):
  1. #define CFG_MALLOC_LEN      (CFG_ENV_SIZE + 128*1024)
  2. #define CFG_ENV_SIZE        0x10000 /* Total Size of Environment Sector */
  3. #define CFG_GBL_DATA_SIZE   128
  4. #define CONFIG_STACKSIZE_IRQ    (4*1024)    /* IRQ stack */
  5. #define CONFIG_STACKSIZE_FIQ    (4*1024)    /* FIQ stack */
  1. 0x34000000:  
  2. (512K)               存放 uboot  
  3. 0x33F80000:         TEXT_BASE  
  4. (64K+128K == 192K)   malloc區  
  5. 0x33F50000:  
  6. (128bytes)           global data區,後邊會提到主要放的gd、bd全域性結構體  
  7. 0x33F4FF80:           
  8. (8K)                 IRQ+FIQ的棧  
  9. 0x33F4DF80:  
  10. (12byte)             abort-stack,棧溢位  
  11. 0x33F4DF74:          sp  
  11、清 BSS 段
  1. clear_bss:  
  2.     ldr r0, _bss_start      /* find start of bss segment        */
  3.     ldr r1, _bss_end        /* stop here                        */
  4.     mov     r2, #0x00000000     /* clear                            */
  5. clbss_l:str r2, [r0]        /* clear loop...                    */
  6.     add r0, r0, #4  
  7.     cmp r0, r1  
  8.     ble clbss_l  
三、第二階段
  1. ldr pc, _start_armboot  
  2. start_armboot:  .word start_armboot  

    跳轉到 sdram 裡的 start_armboot 函式執行。

總結: