zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    ", 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
    ",
    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
    ",
    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
    ",
    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
    ",
    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

  • 相关阅读:
    ZT Android Debuggerd的分析及使用方法
    使用信号进行同步 sem_post
    linux c编程调用系统的动态库时,要使用dlopen等函数吗?
    Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析
    linux的pthread_self与gettid的返回值和开销的区别
    转贴:参禅与悟道》——浅谈人生
    my target
    转贴:如何学好C++语言.docx
    [EffectiveC++]item24:若所有参数皆需类型转换,请为此采用non-member函数
    [EffectiveC++]item23:Prefer non-member non-friend functions to member functions
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/7355248.html
Copyright © 2011-2022 走看看