uboot學習之uboot.bin的運行流程


上篇博客:http://www.cnblogs.com/yeqluofwupheng/p/7347925.html

講到uboot-spl的工作流程,接下來簡述一下uboot.bin的工作流程,這對應BL2的流程。

BL2的主要文件和任務流程如下:

arch/arm/cpu/armv7/start.S
           1. 設置CPU為SVC模式
           2. 關閉MMU
           3. 關閉Cache
           4. 跳轉到lowlevel_init.S low_level_init
board/samsung/origen/lowlevel_init.S
           5. 初始化時鍾
           6. 初始化內存
           7. 初始化串口
           8. 關閉看門狗
           9. 跳轉到crt0.S _main
arch/arm/lib/crt0.S
           10. 設置棧
           11. 初始化C運行環境
           12. 調用board_init_f()
arch/arm/lib/board.c
           13. board_init_f對全局信息GD結構體進行填充
arch/arm/lib/crt0.S
           14. 代碼重定位------------BL2的最后的工作, 執行完就進入DRAM執行BL2

 

1.首先從board_init_f函數開始,它是定義在/u-boot/arch/arm/lib/board.c文件中。

它的作用是初始化開發板。需要注意的是,此時程序是在flash中運行的。

 1 void board_init_f(ulong bootflag)
 2 {
 3     bd_t *bd;
 4     init_fnc_t **init_fnc_ptr;
 5     gd_t *id;
 6     ulong addr, addr_sp;
 7 
 8 #if defined(CONFIG_ARCH_SUN8IW6P1) || defined(CONFIG_ARCH_SUN9IW1P1)
 9     memset((void*)0x00000000, 0, 4*1024);
10 #endif
11     /* Pointer is writable since we allocated a register for it */
12     gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
13     /* compiler optimization barrier needed for GCC >= 3.4 */
14     __asm__ __volatile__("": : :"memory");
15 
16     memset((void *)gd, 0, sizeof(gd_t));
17     gd->mon_len = _bss_end_ofs + sizeof(struct spare_boot_head_t);
18         gd->debug_mode = 1;

 

gd是一個保存在ARM的r8寄存器中的gd_t結構體的指針,該結構體包括了u-boot中所有重要的全局變量,它是在arch/arm/include/asm目錄下的global_data.h文件內被定義的。上述代碼的作用是為gd分配地址,並清零,最后得到整個u-boot的長度。gd_t結構體的定義如下:

typedef    struct    global_data {
    bd_t        *bd;
    unsigned long    flags;
    unsigned long    baudrate;
    unsigned long    have_console;    /* serial_init() was called */
    unsigned long    env_addr;    /* Address  of Environment struct */
    unsigned long    env_valid;    /* Checksum of Environment valid? */
    unsigned long    fb_base;    /* base address of frame buffer */
#ifdef CONFIG_FSL_ESDHC
    unsigned long    sdhc_clk;
#endif
#ifdef CONFIG_AT91FAMILY
    /* "static data" needed by at91's clock.c */
    unsigned long    cpu_clk_rate_hz;
    unsigned long    main_clk_rate_hz;
    unsigned long    mck_rate_hz;
    unsigned long    plla_rate_hz;
    unsigned long    pllb_rate_hz;
    unsigned long    at91_pllb_usb_init;
#endif
#ifdef CONFIG_ARM
    /* "static data" needed by most of timer.c on ARM platforms */
    unsigned long    timer_rate_hz;
    unsigned long    tbl;
    unsigned long    tbu;
    unsigned long long    timer_reset_value;
    unsigned long    lastinc;
#endif
#ifdef CONFIG_IXP425
    unsigned long    timestamp;
#endif
    unsigned long    relocaddr;    /* Start address of U-Boot in RAM */
    phys_size_t    ram_size;    /* RAM size */
        unsigned long   ram_size_mb;    /* RAM size MB*/
    unsigned long    mon_len;    /* monitor len */
    unsigned long    irq_sp;        /* irq stack pointer */
    unsigned long    start_addr_sp;    /* start_addr_stackpointer */
    unsigned long    reloc_off;
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
    unsigned long    tlb_addr;
#endif
#if defined(CONFIG_ALLWINNER)
    int             uart_console;
    int             boot_card_num;
    unsigned int    layer_para;
    unsigned int    layer_hd;
    int             key_pressd_value;
    int             axp_power_soft_id;
    int             power_step_level;
    int             pmu_suspend_chgcur;
    int             pmu_runtime_chgcur;
    int             limit_vol;
    int             limit_cur;
    int             limit_pcvol;
    int             limit_pccur;
    int                power_main_id;
    int                power_slave_id;
    char            *script_mod_buf;
    int             script_main_key_count;
    int             force_shell;
    uint            malloc_noncache_start;
    int             lockflag;
    uint            chargemode;
    uint            force_download_uboot;
    int             securemode;
    uint            vbus_status;        //0: 未知;1:存在;2:不存在
        uint            debug_mode;
#endif
    void        **jt;        /* jump table */
    char        env_buf[32];    /* buffer for getenv() before reloc. */
} gd_t;
View Code
19 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
20         if ((*init_fnc_ptr)() != 0) {
21             hang ();
22         }
23     }

上述代碼的作用是循環調用init_sequence函數指針數組中的成員,該數組成員函數主要完成一些初始化的工作。

其中init_sequence的定義如下:

init_fnc_t *init_sequence[] = {
//#if defined(CONFIG_ARCH_CPU_INIT)
    arch_cpu_init,        /* basic arch cpu dependent setup */
//#endif
    sunxi_probe_securemode,
#if defined(CONFIG_USE_NEON_SIMD)
    arm_neon_init,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
    board_early_init_f,
#endif
    timer_init,        /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
    get_clocks,
#endif
    env_init,            /* initialize environment */
    init_baudrate,        /* initialze baudrate settings */
    serial_init,        /* serial communications setup */
    console_init_f,        /* stage 1 init of console */
    display_banner,        /* say that we are here */
    display_inner,      /* show the inner version */
        print_commit_log,
    script_init,
#if defined(SUNXI_OTA_TEST)
    display_ota_test,
#endif
        get_debugmode_flag,
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,        /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,        /* display board info */
#endif
    smc_init,
    init_func_pmubus,
    power_source_init,
        check_update_key,
        check_uart_input,
    dram_init,        /* configure available RAM banks */
        sunxi_set_secure_mode,
        NULL,
};

board_early_init_f函數(在board/samsung/smdk2410目錄下的smdk2410.c文件內)完成ARM的時鍾頻率和IO的設置;

timer_init函數(在arch/arm/cpu/arm920t/s3c24x0目錄下的timer.c文件內)完成定時器4的設置;

env_init函數(在common目錄下的env_flash.c文件內,因為include/configs/smdk2410.h中定義了CONFIG_ENV_IS_IN_FLASH)完成環境變量的設置;

init_baudrate函數(在arch/arm/lib目錄下的board.c文件內)完成波特率的設置;

serial_init函數(在drivers/serial目錄下的serial_s3c24x0.c文件內,因為include/configs/smdk2410.h中定義了CONFIG_S3C24X0_SERIAL)完成串口通訊的設置;

console_init_f函數(在common目錄下的console.c文件內)完成第一階段的控制台初始化;

display_banner函數(在arch/arm/lib目錄下的board.c文件內)用來打印輸出一些信息;

dram_init函數(在board/samsung/smdk2410目錄下的smdk2410.c文件內)用來配置SDRAM的大小。

 24 #if defined(CONFIG_SYS_MEM_TOP_HIDE)
 25     /*
 26     * Subtract specified amount of memory to hide so that it won't
 27     * get "touched" at all by U-Boot. By fixing up gd->ram_size
 28     * the Linux kernel should now get passed the now "corrected"
 29     * memory size and won't touch it either. This should work
 30     * for arch/ppc and arch/powerpc. Only Linux board ports in
 31     * arch/powerpc with bootwrapper support, that recalculate the
 32     * memory size from the SDRAM controller setup will have to
 33     * get fixed.
 34     */
 35    gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
 36 #endif
 37     if(gd->ram_size)
 38         addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
 39     else
 40         addr = CONFIG_SYS_SDRAM_BASE + (1U<<30);

得到SDRAM的末位物理地址,即SDRAM的空間分布。

65 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
66     /* reserve TLB table */
67     addr -= (4096 * 4);
68 
69     /* round down to next 64 kB limit */
70     addr &= ~(0x10000 - 1);
71 
72     gd->tlb_addr = addr;
73     debug("TLB table at: %08lx\n", addr);
74 #endif
75 
76     /* round down to next 4 kB limit */
77     addr &= ~(4096 - 1);
78     debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

分配SDRAM的高64kB區域作為TLB,並且該區域也被用於U-Boot。16KB保存TLB表,

88     /*
89     * reserve memory for U-Boot code, data & bss
90     * round down to next 4 kB limit
91     */
92    addr -= gd->mon_len;
93    addr &= ~(4096 - 1);
94
95    debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

分配SDRAM的下一個單元為U-Boot代碼段、數據段及BSS段。

 96 #ifndef CONFIG_SPL_BUILD
 97     /*
 98      * reserve memory for malloc() arena
 99      */
100     addr_sp = addr - TOTAL_MALLOC_LEN;
101     debug("Reserving %dk for malloc() at: %08lx\n",
102             TOTAL_MALLOC_LEN >> 10, addr_sp);
103 #ifdef CONFIG_NONCACHE_MEMORY
104      addr_sp &= (~(0x00100000 -1));
105     addr_sp -= CONFIG_NONCACHE_MEMORY_SIZE;
107 #endif
108     /*
109      * (permanently) allocate a Board Info struct
110      * and a permanent copy of the "global" data
111      */
112     addr_sp -= sizeof (bd_t);
113     bd = (bd_t *) addr_sp;
114     gd->bd = bd;
115     debug("Reserving %zu Bytes for Board Info at: %08lx\n",
116             sizeof (bd_t), addr_sp);
117 
118 #ifdef CONFIG_MACH_TYPE
119     gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
120 #endif
121
122     addr_sp -= sizeof (gd_t);
123     id = (gd_t *) addr_sp;
124     debug("Reserving %zu Bytes for Global Data at: %08lx\n",
125            sizeof (gd_t), addr_sp);
126 
127     /* setup stackpointer for exeptions */
128     gd->irq_sp = addr_sp;
129 #ifdef CONFIG_USE_IRQ
130     addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
131     debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
132         CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
133 #endif
134     /* leave 3 words for abort-stack    */
135     addr_sp -= 12;
136 
137     /* 8-byte alignment for ABI compliance */
138     addr_sp &= ~0x07;
139 #else
140     addr_sp += 128;    /* leave 32 words for abort-stack   */
141     gd->irq_sp = addr_sp;
142 #endif

第100行的意思為在SDRAM中又開辟了一塊malloc空間,該區域是緊挨着上面定義的U-Boot區域的下面。然后在SDRAM中又分別依次定義了bd結構體空間、gd結構體空間和3個字大小的異常中斷堆空間。其中bd結構體的數據原型為bd_t數據結構,它表示的是“板級信息”結構體,這些信息包括開發板的波特率、IP地址、ID、以及DRAM等信息,它是在arch/arm/include/asm目錄下的u-boot.h文件中定義的。下圖詳細描述了SDRAM的空間分配情況(地址從上到下遞減):

64KB的TLB
4KB的RAM空間
4KB的U-Boot代碼段、數據段及BSS段
malloc空間
bd空間
gd空間
3字異常中斷堆空間
棧空間
143     gd->bd->bi_baudrate = gd->baudrate;
144     /* Ram ist board specific, so move it to board code ... */
145     dram_init_banksize();
146     display_dram_config();    /* and display it */
147 
148     gd->relocaddr = addr + sizeof(struct spare_boot_head_t) + sizeof(uboot_hash_value);
149     gd->start_addr_sp = addr_sp;
150     gd->reloc_off = addr - _TEXT_BASE;

上述代碼主要的作用是為gd結構體賦值,其中display_dram_config函數的作用是計算SDRAM的大小,並把它通過串口顯示在控制台上。

151     memcpy(id, (void *)gd, sizeof(gd_t));
152 
153     relocate_code(addr_sp, id, addr + sizeof(struct spare_boot_head_t)+sizeof(uboot_hash_value));
154 
155     /* NOTREACHED - relocate_code() does not return */
156 }

在board_init_f函數的最后是跳轉到relocate_code函數體內,這個函數是在arch/arm/cpu/arm920t目錄下的start.s文件內,也就是說從最開始的start.s跳到board.c,又從board.c跳回到了start.s中,這是因為此時程序需要重定向,即把代碼從flash中搬運到ram中,這個過程是需要匯編這個低級語言來完成的。傳遞給relocate_code函數的三個參數分別棧頂地址、數據ID(即全局結構gd)在SDRAM中的起始地址和在SDRAM中存儲U-Boot的起始地址。需要注意的是relocate_code函數執行完后,並不會返回到relocate_code (addr_sp, id, addr);的下一條語句繼續執行。而是繼續運行start.S的程序。

下面繼續看start.S的程序:

/*
 * void relocate_code (addr_sp, gd, addr_moni)
 *
 * This "function" does not return, instead it continues in RAM
 * after relocating the monitor code.
 *
 */
    .globl    relocate_code

relocate_code:
    mov    r4, r0    /* save addr_sp */
    mov    r5, r1    /* save addr of gd */
    mov    r6, r2    /* save addr of destination */

取得三個參數,分別放入寄存器r4、r5和r6。

    /* Set up the stack                            */
stack_setup:
    mov    sp, r4

設置堆棧地址。

    /* Set up irq stack */
    add r4, r4, #12
    add r4, r4, #0x2000
    mrs r0, cpsr
    bic r0, r0, #0x1f
    orr r0, r0, #0x12
    msr cpsr_c, r0
    mov sp, r4

設置IRQ 棧。

/* Set up svc stack */
    sub r4, r4, #0x2000
    sub r4, r4, #12
    mrs r0, cpsr
    bic r0, r0, #0x1f
    orr r0, r0, #0x13
    msr cpsr_c, r0

設置SVN 棧。

    adr    r0, _start
    cmp    r0, r6
    moveq    r9, #0        /* no relocation. relocation offset(r9) = 0 */
    beq    clear_bss        /* skip relocation */

    @ mov r9, #0
    @ b   clear_bss

    mov    r1, r6            /* r1 <- scratch for copy_loop */
    ldr    r3, _image_copy_end_ofs
    add    r2, r0, r3        /* r2 <- source end address        */

copy_loop:
    ldmia    r0!, {r9-r10}        /* copy from source address [r0]    */
    stmia    r1!, {r9-r10}        /* copy to   target address [r1]    */
    cmp    r0, r2            /* until source end address [r2]    */
    blo    copy_loop

判斷U-Boot是在什么位置上,如果在SDRAM中,則直接跳到BSS段清零函數處即可;如果在FLASH中,則要把U-Boot復制到SDRAM中指定的位置處。

#ifndef CONFIG_SPL_BUILD
    /*
     * fix .rel.dyn relocations
     */
    @ldr    r0, _TEXT_BASE        /* r0 <- Text base */
    adr r0, _start
    sub    r9, r6, r0        /* r9 <- relocation offset */
    ldr    r10, _dynsym_start_ofs    /* r10 <- sym table ofs */
    add    r10, r10, r0        /* r10 <- sym table in FLASH */
    ldr    r2, _rel_dyn_start_ofs    /* r2 <- rel dyn start ofs */
    add    r2, r2, r0        /* r2 <- rel dyn start in FLASH */
    ldr    r3, _rel_dyn_end_ofs    /* r3 <- rel dyn end ofs */
    add    r3, r3, r0        /* r3 <- rel dyn end in FLASH */
fixloop:
    ldr    r0, [r2]        /* r0 <- location to fix up, IN FLASH! */
    add    r0, r0, r9        /* r0 <- location to fix up in RAM */
    ldr    r1, [r2, #4]
    and    r7, r1, #0xff
    cmp    r7, #23            /* relative fixup? */
    beq    fixrel
    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]
    add    r1, r1, r9
fixnext:
    str    r1, [r0]
    add    r2, r2, #8        /* each rel.dyn entry is 8 bytes */
    cmp    r2, r3
    blo    fixloop
    b    clear_bss
_rel_dyn_start_ofs:
    .word __rel_dyn_start - _start
_rel_dyn_end_ofs:
    .word __rel_dyn_end - _start
_dynsym_start_ofs:
    .word __dynsym_start - _start

#endif    /* #ifndef CONFIG_SPL_BUILD */
View Code

上述代碼的含義是對rel.dyn進行重定向。

...
ldr    r0, _board_init_r_ofs
    adr    r1, _start
    add    lr, r0, r1
    add    lr, lr, r9
    /* setup parameters for board_init_r */
    mov    r0, r5        /* gd_t */
    mov    r1, r6        /* dest_addr */
    /* jump to it ... */
    mov    pc, lr
...
    _board_init_r_ofs:
        .word board_init_r - _start

該段代碼的作用是跳轉到board_init_r函數,並且給該函數傳遞了兩個參數:全局結構gd在SDRAM中的起始地址和在SDRAM中存儲U-Boot的起始地址。board_init_r函數是在arch/arm/lib目錄下的board.c文件中,也就是又回到了上面執行過的board_init_f函數所在的board.c文件中。以后,程序就開始在SDRAM中運行了。

以上是uboot.bin的流程。

下一篇:http://www.cnblogs.com/yeqluofwupheng/p/7372849.html


免責聲明!

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



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