S3C2440移植uboot之啓動過程概述

  上節燒寫了uboot到開發板,不能運行。這節咱們分析uboot從新編譯uboot,由最後一條連接命令開始分析uboot
@[TOC]html

  下圖爲編譯uboot後顯示的最後一條連接命令。
在這裏插入圖片描述linux

1.分析start.S

  打開uboot.lds,發現連接地址爲0,因此新的uboot只能在nor flash運行。運行開始文件爲start.o。
在這裏插入圖片描述
  下面咱們分析arch/arm/cpu/arm920t/start.S
  從start_code開始運行數組

.globl _start                                //聲明_start全局符號,這個符號會被lds連接腳本用到
_start:    
b     start_code                            //跳轉到start_code符號處,0x00
       ldr   pc, _undefined_instruction                    //0x04
       ldr   pc, _software_interrupt                       //0x08
       ldr   pc, _prefetch_abort                           //0x0c
       ldr   pc, _data_abort                               //0x10
       ldr   pc, _not_used                                 //0x14
       ldr   pc, _irq                                      //0x18
       ldr   pc, _fiq                                      //0x20

_undefined_instruction:  .word undefined_instruction
           //定義_undefined_instruction指向undefined_instruction(32位地址)

_software_interrupt:      .word software_interrupt
_prefetch_abort:    .word prefetch_abort
_data_abort:          .word data_abort
_not_used:             .word not_used
_irq:               .word irq
_fiq:               .word fiq

   .balignl 16,0xdeadbeef        //balignl使用,參考http://www.cnblogs.com/lifexy/p/7171507.html

2._start會跳轉到start_code處

start_code:

    /*設置CPSR寄存器,讓CPU進入管理模式*/
       mrs  r0, cpsr                 //讀出cpsr的值
       bic   r0, r0, #0x1f           //清位
       orr   r0, r0, #0xd3          //位或
       msr  cpsr, r0                 //寫入cpsr

#if   defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
       /*
        * relocate exception table
        */
       ldr   r0, =_start            
       ldr   r1, =0x0                //r1等於異常向量基地址
       mov r2, #16
copyex:
       subs       r2, r2, #1           //減16次,s表示每次減都要更新條件標誌位
       ldr   r3, [r0], #4       
       str   r3, [r1], #4      //將_start標號後的16個符號存到異常向量基地址0x0~0x3c處
       bne  copyex             //直到r2減爲0
#endif

#ifdef CONFIG_S3C24X0

       /* 關看門狗*/
#  define pWTCON       0x53000000
#  define INTMSK 0x4A000008    /* Interrupt-Controller base addresses */
#  define INTSUBMSK  0x4A00001C
#  define CLKDIVN       0x4C000014    /* clock divisor register */

       ldr   r0, =pWTCON       
       mov r1, #0x0        
       str   r1, [r0]           //關看門狗,使WTCON寄存器=0

       /*關中斷*/
       mov r1, #0xffffffff
       ldr   r0, =INTMSK
       str   r1, [r0]                  //關閉全部中斷
# if defined(CONFIG_S3C2410)
       ldr   r1, =0x3ff
       ldr   r0, =INTSUBMSK
       str   r1, [r0]                  //關閉次級全部中斷
# endif

    /* 設置時鐘頻率, FCLK:HCLK:PCLK = 1:2:4 ,而FCLK默認爲120Mhz*/
       ldr   r0, =CLKDIVN
       mov r1, #3
       str   r1, [r0]

 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
       bl    cpu_init_crit                         //關閉mmu,並初始化各個bank

#endif

call_board_init_f:
       ldr   sp, =(CONFIG_SYS_INIT_SP_ADDR) //CONFIG_SYS_INIT_SP_ADDR=0x30000f80
       bic   sp, sp, #7         //sp=0x30000f80
       ldr   r0,=0x00000000
       bl    board_init_f

  上面的CONFIG_SYS_INIT_SP_ADDR =0x30000f80,是經過arm-linux-objdump -D u-boot>u-boot.dis生成反彙編,而後從u-boot.dis獲得的。函數

3.而後進入第一個C數:board_init_f()

  該函數主要工做是:
  清空gd指向的結構體、經過init_sequence函數數組,來初  始化各個函數以及逐步填充gd結構體,最後劃份內存區域,  將數據保存在gd裏,而後調用relocate_code()對uboot重定位。
  (gd是用來傳遞給內核的參數)
1)具體代碼以下所示:oop

void board_init_f(ulong bootflag) // bootflag=0x00000000
{
       bd_t *bd;
       init_fnc_t **init_fnc_ptr;         //函數指針
       gd_t *id;
       ulong addr, addr_sp;
#ifdef CONFIG_PRAM
       ulong reg;
#endif

       bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");
       /* Pointer is writable since we allocated a register for it */
       gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);

  其中gd是一個全局變量,用來傳遞給內核的參數用的,以下圖所示,在arch/arn/include/asm/global_data.h中定義,*gd指向r8寄存器,因此r8專門提供給gd使用
在這裏插入圖片描述
  而CONFIG_SYS_INIT_SP_ADDR,在以前獲得=0x30000f80,因此gd=0x30000f80
2)繼續來看board_init_f():fetch

__asm__ __volatile__("": : :"memory");           //memory:讓cpu從新讀取內存的數據

      memset((void *)gd, 0, sizeof(gd_t));        //將0x30000f80地址上的gd_t結構體清0

      gd->mon_len = _bss_end_ofs;  
         // _bss_end_ofs =__bss_end__ - _start,在反彙編找到等於0xae4e0,因此mon_len等於uboot的數據長度
      gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, (uintptr_t)gd->fdt_blob);

       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
            //調用init_sequence[]數組裏的各個函數
      {
              if ((*init_fnc_ptr)() != 0)     //執行函數,若函數執行出錯,則進入hang()
             {    
           hang ();   //打印錯誤信息,而後一直while
             }

       }

  上面的init_sequence[]數組裏存了各個函數,好比有:
  board_early_init_f():設置系統時鐘,設置各個GPIO引腳
  timer_init():初始化定時器
  env_init():設置gd的成員變量
  init_baudrate():設置波特率
  dram_init():設置gd->ram_size= 0x04000000(64MB)
3)繼續來看board_init_f():ui

addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;  // addr=0x34000000 
// CONFIG_SYS_SDRAM_BASE:  SDRAM基地址,爲0X30000000
// gd->ram_size:          等於0x04000000 


#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
       /* reserve TLB table */
       addr -= (4096 * 4);        //addr=33FFC000

       addr &= ~(0x10000 - 1);  // addr=33FF0000,   

       gd->tlb_addr = addr;   //將64kB分配給TLB,因此TLB地址爲33FF0000~33FFFFFF
#endif

       /* round down to next 4 kB limit */
       addr &= ~(4096 - 1);                    //4kb對齊, addr=33FF0000
       debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

       /*
        * reserve memory for U-Boot code, data & bss
        * round down to next 4 kB limit
        */
       addr -= gd->mon_len; // 在前面分析過gd->mon_len=0xae4e0,
                           //因此addr=33FF0000 -0xae4e0=33F41B20,

       addr &= ~(4096 - 1);  //4095=0xfff,4kb對齊, addr=33F41000
                             //因此分配給uboot各個段的重定位地址爲33F41000~33FFFFFF
       debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

#ifndef CONFIG_SPL_BUILD
       addr_sp = addr - TOTAL_MALLOC_LEN; //分配一段malloc空間給addr_sp
                       //TOTAL_MALLOC_LEN=1024*1024*4,因此malloc空間爲33BF1000~33F40FFF

       addr_sp -= sizeof (bd_t);            //分配一段bd_t結構體大的空間
    bd = (bd_t *) addr_sp;               //bd指向剛剛分配出來的bd_t結構體
    gd->bd = bd;                         // 0x30000f80處的gd變量的成員bd等於bd_t基地址

    addr_sp -= sizeof (gd_t);              //分配一個gd_t結構體大的空間
    id = (gd_t *) addr_sp;                 //id指向剛剛分配的gd_t結構體
    gd->irq_sp = addr_sp;                 //0x30000f80處的gd變量的成員irq_sp等於gd_t基地址

    addr_sp -= 12;
    addr_sp &= ~0x07;
    ... ...

    relocate_code(addr_sp, id, addr);  //進入relocate_code()函數,重定位代碼,以及各個符號
    // addr_sp: 棧頂,該棧頂向上的位置用來存放gd->irq_sp、id 、gd->bd、malloc、uboot、TLB(64kb),
    //id:       存放 gd_t結構體的首地址
    // addr:    等於存放uboot重定位地址33F41000
}

  執行完board_init_f()後,最終內存會劃分以下圖所示:spa

在這裏插入圖片描述
  其實此時uboot還在flash中運行,而後會進入start.S的relocate_code()裏進行uboot重定位debug

4.接下來進入重定位

1)start.S的relocate_code()代碼以下所示3d

relocate_code:
       mov r4, r0      /* save addr_sp */              // addr_sp棧頂值
       mov r5, r1      /* save addr of gd */           // id值
       mov r6, r2      /* save addr of destination */  // addr值:uboot重定位地址

       /* Set up the stack        */
stack_setup:
       mov sp, r4                //設置棧addr_sp
       adr  r0, _start           //在頂層目錄下system.map符號文件中找到_start =0,因此r0=0
       cmp r0, r6                //判斷_start(uboot重定位以前的地址)和addr(重定位地址)是否同樣
       beq clear_bss             /* skip relocation */ 

       mov r1, r6             /* r1 <- scratch for copy_loop */ //r1= addr(重定位地址)
       ldr   r3, _bss_start_ofs               //_bss_start_ofs=__bss_start - _start(uboot代碼大小)
       add r2, r0, r3         /* r2 <- source end address*/   //r2= uboot重定位以前的結束地址

copy_loop:
       ldmia      r0!, {r9-r10}  /* copy from source address [r0] */
                              //將r0處的兩個32位數據拷到r9-r10中,而後r0+=8

       stmia      r1!, {r9-r10}  /* copy to   target address [r1]*/
                             //將拷出來的兩個數據放入r1(重定位地址)處,而後r1+=8

       cmp r0, r2  /* until source end address [r2]*/   //判斷拷貝的數據是否到結束地址
       blo  copy_loop

  上面只是把代碼複製到SDRAM上,而連接地址內容卻沒有改變,好比異常向量0x04的代碼內容仍是0x1e0,
  咱們以異常向量0x04爲例,來看它的反彙編:
在這裏插入圖片描述
   如上圖所示,即便uboot在SDRAM運行,因爲代碼沒修改,PC也會跳到0x1e0(flash地址)上,和以前老的uboot有很大區別,之前老的uboot直接是使用的SDRAM連接地址,以下圖所示:
在這裏插入圖片描述
  因此,新的uboot採用了動態連接地址的方法,在連接腳本uboot.lds中,能夠看到這兩個段(.rel.dyn、.dynsym):
在這裏插入圖片描述
  該兩個段裏,即是保存了各個文件的相對動態信息(.rel.dyn)、動態連接地址的符號(.dynsym)
  以上圖的.rel.dyn段爲例來分析,找到__rel_dyn_start符號處:
在這裏插入圖片描述
  如上圖所示,其中0x17表示的是符號的結束標誌位,咱們以0x20爲例來說解:
  在以前,咱們講過0x20裏面保存的是異常向量0x04跳轉的地址(0x1e0),以下圖所示:
在這裏插入圖片描述
  因此,接下來的代碼,便會根據0x20裏的值0x1e0(flash地址),將SDRAM的33F41000+0x20的內容改成33F41000+0x1e0(SDRAM地址),來改變uboot的連接地址
2)重定位的剩餘代碼,以下所示:

#ifndef CONFIG_SPL_BUILD
       /*
        * fix .rel.dyn relocations
        */
       ldr   r0, _TEXT_BASE             /* r0 <- Text base */  //r0=text段基地址=0
       sub  r9, r6, r0         /* r9 <- relocation offset */   //r9= 重定位後的偏移值=33F41000
       ldr   r10, _dynsym_start_ofs  /* r10 <- sym table ofs */ 
                                          //_dynsym_start_ofs =__dynsym_start - _start=0x73608
                                          //因此r10=動態符號表的起始偏移值=0x73608

       add r10, r10, r0            /* r10 <- sym table in FLASH */
                                       //r10=flash上的動態符號表基地址=0x73608

       ldr   r2, _rel_dyn_start_ofs     /* r2 <- rel dyn start ofs */
                                          //r2=__rel_dyn_start - _start=0x6b568
                                          //因此r2=相對動態信息的起始偏移值=0x6b568

       add r2, r2, r0         /* r2 <- rel dyn start in FLASH */
                                      //r2=flash上的相對動態信息基地址=0x6b568

       ldr   r3, _rel_dyn_end_ofs      /* r3 <- rel dyn end ofs */
                                          // _rel_dyn_end_ofs=__rel_dyn_end - _start=00073608
                                          //因此r3=相對動態信息的結束偏移值=00073608

       add r3, r3, r0         /* r3 <- rel dyn end in FLASH */
                                    //r3=flash上的相對動態信息結束地址=0x6b568

fixloop:
       ldr   r0, [r2]           /* r0 <- location to fix up, IN FLASH! */
                               //以0x20爲例,r0=0x6b568地址處的內容= 0x20

       add r0, r0, r9         /* r0 <- location to fix up in RAM */
                                     //r0=33F41000+0x20=33F41020

       ldr   r1, [r2, #4]             //r1= 33F41024地址處的內容=0x17
       and  r7, r1, #0xff       
       cmp r7, #23                  /* relative fixup? */  //0x17=23,因此相等
       beq fixrel                                       //跳到:fixerl

       cmp r7, #2                    /* absolute fixup? */
       beq fixabs
       /* ignore unknown type of fixup */
       b     fixnext
fixabs:
       /* absolute fix: set location to (offset) symbol value */
       mov r1, r1, LSR #4         /* r1 <- symbol index in .dynsym */
       add r1, r10, r1              /* r1 <- address of symbol in table */
       ldr   r1, [r1, #4]             /* r1 <- symbol value */
       add r1, r1, r9         /* r1 <- relocated sym addr */
       b     fixnext

fixrel:
       /* relative fix: increase location by offset */
       ldr   r1, [r0]                  //r1=33F41020地址處的內容=0x1e0
       add r1, r1, r9                //r1=0x1e0+33F41000= 33F411e0

fixnext:
       str   r1, [r0]             //改變連接地址裏的內容, 33F41020=33F411e0  (以前爲0x1e0)   
       add r2, r2, #8             //r2等於下一個相對動態信息(0x24)的地址
       cmp r2, r3                //若沒到尾部__rel_dyn_end,便繼續執行: fixloop
       blo  fixloop                 
#endif

5.清除bss段

/*重定位完成後,清除bss段*/
clear_bss:
 #ifndef CONFIG_SPL_BUILD
       ldr   r0, _bss_start_ofs                        //獲取flash上的bss段起始位置
       ldr   r1, _bss_end_ofs                          //獲取flash上的bss段結束位置
       mov r4, r6                    /* reloc addr */     //獲取r6(SDRAM上的uboot基地址)
       add r0, r0, r4                                  //加上重定位偏移值,獲得SDRAM上的bss段起始位置
       add r1, r1, r4                                     //獲得SDRAM上的bss段結束位置
       mov r2, #0x00000000           /* clear*/

clbss_l:
    str    r2, [r0]           /* clear loop...       */                 //開始清除SDRAM上的bss段
       add r0, r0, #4
       cmp r0, r1
       bne  clbss_l
       bl coloured_LED_init
       bl red_led_on
#endif

5.1繼續往下分析

#ifdef CONFIG_NAND_SPL                   //未定義,因此不執行
  ... ...                          
#else                                   //執行else

       ldr   r0, _board_init_r_ofs         //r0=flash上的board_init_r()函數地址偏移值
       adr  r1, _start                    //0
       add lr, r0, r1                     //返回地址lr=flash上的board_init_r()函數
       add lr, lr, r9                     //加上重定位偏移值(r9)後,lr=SDRAM上的board_init_r()函數

       /* setup parameters for board_init_r */
       mov r0, r5             /* gd_t */              //r0=id值
       mov r1, r6             /* dest_addr */         //r1=uboot重定位地址
       /* jump to it ... */
       mov pc, lr              //跳轉:  board_init_r()函數

_board_init_r_ofs:
       .word board_init_r - _start        //獲取在flash上的board_init_r()函數地址偏移值

#endif

  從上面代碼看出, 接下來便會進入uboot的board_init_r()函數,該函數會對各個外設初始化、環境變量初始化等.
uboot的啓動過程到此便結束了。
  下一節咱們將新建一塊單板支持S3C2440。
如遇到排版錯亂的問題,能夠經過如下連接訪問個人CSDN。

**CSDN:[CSDN搜索「嵌入式與Linux那些事」]

相關文章
相關標籤/搜索