上篇博客: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