• Uboot启动流程分析(二)


    1、前言

    在前面的文章《Uboot启动流程分析(一)》中,链接如下:

    https://www.cnblogs.com/Cqlismy/p/12000889.html

    已经简单地分析了low_level_init函数,其调用流程如下:

    save_boot_params_ret
        |
        cpu_init_crit
        |          |
        |          lowlevel_init
        |          |
        |          s_init
        |
        _main

    接下来,则继续往下分析_main函数。

    2、_main函数

    在save_boot_params_ret的最后,会运行bl _main这句代码,Uboot则将会跳转到_main函数中去运行,该函数的定义在arch/arm/lib/crt0.S文件中,_main函数的功能已经在文件中注释得很清楚了,先来看看_main函数实现的功能是什么,注释如下:

    /*
     * This file handles the target-independent stages of the U-Boot
     * start-up where a C runtime environment is needed. Its entry point
     * is _main and is branched into from the target's start.S file.
     *
     * _main execution sequence is:
     *
     * 1. Set up initial environment for calling board_init_f().
     *    This environment only provides a stack and a place to store
     *    the GD ('global data') structure, both located in some readily
     *    available RAM (SRAM, locked cache...). In this context, VARIABLE
     *    global data, initialized or not (BSS), are UNAVAILABLE; only
     *    CONSTANT initialized data are available. GD should be zeroed
     *    before board_init_f() is called.
     *
     * 2. Call board_init_f(). This function prepares the hardware for
     *    execution from system RAM (DRAM, DDR...) As system RAM may not
     *    be available yet, , board_init_f() must use the current GD to
     *    store any data which must be passed on to later stages. These
     *    data include the relocation destination, the future stack, and
     *    the future GD location.
     *
     * 3. Set up intermediate environment where the stack and GD are the
     *    ones allocated by board_init_f() in system RAM, but BSS and
     *    initialized non-const data are still not available.
     *
     * 4a.For U-Boot proper (not SPL), call relocate_code(). This function
     *    relocates U-Boot from its current location into the relocation
     *    destination computed by board_init_f().
     *
     * 4b.For SPL, board_init_f() just returns (to crt0). There is no
     *    code relocation in SPL.
     *
     * 5. Set up final environment for calling board_init_r(). This
     *    environment has BSS (initialized to 0), initialized non-const
     *    data (initialized to their intended value), and stack in system
     *    RAM (for SPL moving the stack and GD into RAM is optional - see
     *    CONFIG_SPL_STACK_R). GD has retained values set by board_init_f().
     *
     * 6. For U-Boot proper (not SPL), some CPUs have some work left to do
     *    at this point regarding memory, so call c_runtime_cpu_setup.
     *
     * 7. Branch to board_init_r().
     *
     * For more information see 'Board Initialisation Flow in README.
     */

    从注释中,我们可以大概知道_main函数的执行顺序为:

    • 先设置用于调用board_init_f()函数的初始环境,该环境仅仅是提供了堆栈和存储位置GD('global data')结构,两者都是位于可以使用的RAM(SRAM,locked cache...)中,在调用board_init_f()函数前,GD应该被清0;
    • 调用board_init_f()函数,该函数的功能为从system RAM(DRAM,DDR...)中执行准备硬件,当system RAM还不能够使用的时,必须要使用目前的GD存储传递到后续阶段的所有数据,这些数据包括重定位的目标,将来的堆栈和GD的位置;
    • 设置中间环境,其中堆栈和GD是由board_init_f()函数在system RAM中进行分配的,但此时的bss和初始化的非常量仍然不能使用;
    • 对于正常的uboot引导(非SPL),调用relocate_code()函数,该函数的功能将uboot从当前的位置重新转移到由board_init_f()函数计算的目标位置;
    • 对于SPL,board_init_f()函数仅仅是返回(crt0),没有代码的重定位;
    • 设置用于调用board_init_r()函数的最终环境,该环境将有bss段(初始化为0),初始化非常量数据(初始化为预期值),并入栈到system RAM中,GD保留了board_init_f()函数设置的值;
    • 为了使uboot正常运行(非SPL),某些CPU还有一些关于内存的工作要做,调用c_runtime_cpu_setup()函数;
    • 调用board_init_r()函数。

     _main函数的定义如下所示:

    ENTRY(_main)
    
    /*
     * Set up initial C runtime environment and call board_init_f(0).
     */
    
    #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
        ldr    sp, =(CONFIG_SPL_STACK)
    #else
        ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)    //sp指针指向CONFIG_SYS_INIT_SP_ADDR(0x0091FF00)
    #endif
    #if defined(CONFIG_CPU_V7M)    /* v7M forbids using SP as BIC destination */
        mov    r3, sp
        bic    r3, r3, #7
        mov    sp, r3
    #else
        bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
    #endif
        mov    r0, sp    //将sp指针保存到r0寄存器,此时r0的值为0x0091FF00
        bl    board_init_f_alloc_reserve    //调用函数board_init_f_alloc_reserve,参数为r0的值
        mov    sp, r0    //将函数返回值写到sp指针,r0的值为0x0091FA00
        /* set up gd here, outside any C code */
        mov    r9, r0    //将r0寄存器的值保存到r9寄存器,也就是gd结构体的地址,r9 = 0x0091FA00
        bl    board_init_f_init_reserve    //调用board_init_f_init_reserve,参数为r0的值0x0091FA00
    
        mov    r0, #0    //将r0寄存器的值设置为0
        bl    board_init_f    //调用board_init_f函数
    
    #if ! defined(CONFIG_SPL_BUILD)
    
    /*
     * Set up intermediate environment (new sp and gd) and call
     * relocate_code(addr_moni). Trick here is that we'll return
     * 'here' but relocated.
     */
    
        ldr    sp, [r9, #GD_START_ADDR_SP]    /* sp = gd->start_addr_sp */ //获取新的sp指针值
    #if defined(CONFIG_CPU_V7M)    /* v7M forbids using SP as BIC destination */
        mov    r3, sp
        bic    r3, r3, #7
        mov    sp, r3
    #else
        bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */ //sp指针8字节对齐
    #endif
        ldr    r9, [r9, #GD_BD]  /* r9 = gd->bd */ //获取gd->bd的值
        sub    r9, r9, #GD_SIZE  /* new GD is below bd */  //r9 = r9 - GD_SIZE,r9 = gd->new_gd
    
        adr    lr, here   /* 获取here的链接地址 */
        ldr    r0, [r9, #GD_RELOC_OFF]   /* r0 = gd->reloc_off */ //r0保存重定位的偏移量
        add    lr, lr, r0   //lr = lr + r0,here的链接地址需要加上偏移,因为uboot将被重定位
    #if defined(CONFIG_CPU_V7M)
        orr    lr, #1                /* As required by Thumb-only */
    #endif
        ldr    r0, [r9, #GD_RELOCADDR]   /* r0 = gd->relocaddr */ //r0保存重定位的地址
        b    relocate_code    //跳转到relocate_code函数
    here:
    /*
     * now relocate vectors
     */
    
        bl    relocate_vectors
    
    /* Set up final (full) environment */
    
        bl    c_runtime_cpu_setup    /* we still call old routine here */
    #endif
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
    # ifdef CONFIG_SPL_BUILD
        /* Use a DRAM stack for the rest of SPL, if requested */
        bl    spl_relocate_stack_gd
        cmp    r0, #0
        movne    sp, r0
        movne    r9, r0
    # endif
        ldr    r0, =__bss_start    /* this is auto-relocated! */
    
    #ifdef CONFIG_USE_ARCH_MEMSET
        ldr    r3, =__bss_end        /* this is auto-relocated! */
        mov    r1, #0x00000000        /* prepare zero to clear BSS */
    
        subs    r2, r3, r0        /* r2 = memset len */
        bl    memset
    #else
        ldr    r1, =__bss_end        /* this is auto-relocated! */
        mov    r2, #0x00000000        /* prepare zero to clear BSS */
    
    clbss_l:cmp    r0, r1            /* while not at end of BSS */
    #if defined(CONFIG_CPU_V7M)
        itt    lo
    #endif
        strlo    r2, [r0]        /* clear 32-bit BSS word */
        addlo    r0, r0, #4        /* move to next */
        blo    clbss_l
    #endif
    
    #if ! defined(CONFIG_SPL_BUILD)
        bl coloured_LED_init
        bl red_led_on
    #endif
        /* call board_init_r(gd_t *id, ulong dest_addr) */
        mov     r0, r9                  /* gd_t */    //设置第一个参数值gd指针
        ldr    r1, [r9, #GD_RELOCADDR]    /* dest_addr */    //设置第二个参数值gd->relocaddr
        /* call board_init_r */
    #if defined(CONFIG_SYS_THUMB_BUILD)
        ldr    lr, =board_init_r    /* this is auto-relocated! */
        bx    lr
    #else
        ldr    pc, =board_init_r    /* this is auto-relocated! */ //调用board_init_r函数
    #endif
        /* we should not return here. */
    #endif
    
    ENDPROC(_main)

    _main函数的实现比较复杂,需要对其分部分进行分析。

    第一部分,首先是设置初始化C运行环境,然后调用board_init_f(0)函数,功能实现的代码如下:

    /*
     * Set up initial C runtime environment and call board_init_f(0).
     */
    
    #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
        ldr    sp, =(CONFIG_SPL_STACK)
    #else
        ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)    //sp指针指向CONFIG_SYS_INIT_SP_ADDR(0x0091FF00)
    #endif
    #if defined(CONFIG_CPU_V7M)    /* v7M forbids using SP as BIC destination */
        mov    r3, sp
        bic    r3, r3, #7
        mov    sp, r3
    #else
        bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
    #endif
        mov    r0, sp    //将sp指针保存到r0寄存器,此时r0的值为0x0091FF00
        bl    board_init_f_alloc_reserve    //调用函数board_init_f_alloc_reserve,参数为r0的值
        mov    sp, r0    //将函数返回值写到sp指针,r0的值为0x0091FA00
        /* set up gd here, outside any C code */
        mov    r9, r0    //将r0寄存器的值保存到r9寄存器,也就是gd结构体的地址,r9 = 0x0091FA00
        bl    board_init_f_init_reserve    //调用board_init_f_init_reserve,参数为r0的值0x0091FA00
    
        mov    r0, #0    //将r0寄存器的值设置为0
        bl    board_init_f    //调用board_init_f函数

    从上面的代码中可以知道,_main函数进来后,设置sp指针的值为CONFIG_SYS_INIT_SP_ADDR,并对其进行8字节对齐,然后将sp指针的值保存到r0寄存器中,接下来开始调用board_init_f_alloc_reserve函数,传入的参数为r0寄存器的值,也就是sp指针,board_init_f_alloc_reserve函数的定义在common/init/board_init.c文件中,定义如下所示:

    ulong board_init_f_alloc_reserve(ulong top)    //top = 0x0091FF00
    {
        /* Reserve early malloc arena */
    #if defined(CONFIG_SYS_MALLOC_F)
        top -= CONFIG_SYS_MALLOC_F_LEN; //top = top - 0x400,此时top = 0x0091FF00 - 0x400 = 0x0091FB00
    #endif
        /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */ //sizeof(struct global_data) = 248
        top = rounddown(top-sizeof(struct global_data), 16);
    //top = top - sizeof(struct global_data),16字节对齐 //top = 0x0091FB00 - 248 - 8 = 0x0091FA00 return top; }

    在上面的代码中,可以知道,board_init_f_alloc_reserve()函数主要是留出早期的malloc内存区域和global_data结构体内存区域,对于CONFIG_SYS_MALLOC_F_LEN宏定义在文件include/generated/autoconf.h文件中,为0x400,而sizeof(struct global_data)为GD_SIZE宏的值,为248,并且top的值需要16字节对齐,该函数返回top指针的值,函数调用后,OCRAM的内存分配如下所示:

    board_init_f_alloc_reserve()函数是具有返回值,将top=0x0091FA00返回,继续往下分析,因此r0寄存器的值为0x0091FA00,并将r0寄存器的值回写sp指针,此时,sp=0x0091FA00,另外,r0寄存器的值也写到了r9寄存器,因此r9=0x0091FA00,为啥要将分配好的global_data结构体首地址写到r9寄存器呢?

    在uboot源码文件arch/arm/include/asm/global_data.h中,具有下面的两个宏定义:

    #ifdef CONFIG_ARM64
    #define DECLARE_GLOBAL_DATA_PTR        register volatile gd_t *gd asm ("x18")
    #else
    #define DECLARE_GLOBAL_DATA_PTR        register volatile gd_t *gd asm ("r9")
    #endif
    #endif

    对于ARM32的CPU,uboot定义了一个指向gd_t类型的gd指针,gd是一个全局变量,存放在r9寄存器,gd_t的类型定义在文件include/asm-generic/global_data.h中,其实就是struct global_data,该结构体的成员比较多,定义如下:

    typedef struct global_data {
        bd_t *bd;  //指向bd_t结构体内存空间
        unsigned long flags;
        unsigned int baudrate;
        unsigned long cpu_clk;    /* CPU clock in Hz!        */
        unsigned long bus_clk;
        /* We cannot bracket this with CONFIG_PCI due to mpc5xxx */
        unsigned long pci_clk;
        unsigned long mem_clk;
    #if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
        unsigned long fb_base;    /* Base address of framebuffer mem */
    #endif
    #if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER)
        unsigned long post_log_word;  /* Record POST activities */
        unsigned long post_log_res; /* success of POST test */
        unsigned long post_init_f_time;  /* When post_init_f started */
    #endif
    #ifdef CONFIG_BOARD_TYPES
        unsigned long board_type;
    #endif
        unsigned long have_console;    /* serial_init() was called */ //标志位:是否具有console
    #ifdef CONFIG_PRE_CONSOLE_BUFFER
        unsigned long precon_buf_idx;    /* Pre-Console buffer index */
    #endif
        unsigned long env_addr;    /* Address  of Environment struct */
        unsigned long env_valid;    /* Checksum of Environment valid? */
    
        unsigned long ram_top;    /* Top address of RAM used by U-Boot */ //DRAM的最终地址
    
        unsigned long relocaddr;    /* Start address of U-Boot in RAM */ //uboot重定位的地址
        phys_size_t ram_size;    /* RAM size */  //DRAM的大小
    #ifdef CONFIG_SYS_MEM_RESERVE_SECURE
    #define MEM_RESERVE_SECURE_SECURED    0x1
    #define MEM_RESERVE_SECURE_MAINTAINED    0x2
    #define MEM_RESERVE_SECURE_ADDR_MASK    (~0x3)
        /*
         * Secure memory addr
         * This variable needs maintenance if the RAM base is not zero,
         * or if RAM splits into non-consecutive banks. It also has a
         * flag indicating the secure memory is marked as secure by MMU.
         * Flags used: 0x1 secured
         *             0x2 maintained
         */
        phys_addr_t secure_ram;
    #endif
        unsigned long mon_len;    /* monitor len */  //整个uboot的长度
        unsigned long irq_sp;        /* irq stack pointer */
        unsigned long start_addr_sp;    /* start_addr_stackpointer */ //栈指针
        unsigned long reloc_off;   //uboot重定位的偏移
        struct global_data *new_gd;    /* relocated global data */  //uboot重定位后新的gd_t结构体首地址
    
    #ifdef CONFIG_DM
        struct udevice    *dm_root;    /* Root instance for Driver Model */
        struct udevice    *dm_root_f;    /* Pre-relocation root instance */
        struct list_head uclass_root;    /* Head of core tree */
    #endif
    #ifdef CONFIG_TIMER
        struct udevice    *timer;    /* Timer instance for Driver Model */
    #endif
    
        const void *fdt_blob;    /* Our device tree, NULL if none */
        void *new_fdt;        /* Relocated FDT */
        unsigned long fdt_size;    /* Space reserved for relocated FDT */
        struct jt_funcs *jt;        /* jump table */
        char env_buf[32];    /* buffer for getenv() before reloc. */
    #ifdef CONFIG_TRACE
        void        *trace_buff;    /* The trace buffer */
    #endif
    #if defined(CONFIG_SYS_I2C)
        int        cur_i2c_bus;    /* current used i2c bus */
    #endif
    #ifdef CONFIG_SYS_I2C_MXC
        void *srdata[10];
    #endif
        unsigned long timebase_h;
        unsigned long timebase_l;
    #ifdef CONFIG_SYS_MALLOC_F_LEN
        unsigned long malloc_base;    /* base address of early malloc() */ //早期malloc相关成员
        unsigned long malloc_limit;    /* limit address */
        unsigned long malloc_ptr;    /* current address */
    #endif
    #ifdef CONFIG_PCI
        struct pci_controller *hose;    /* PCI hose for early use */
        phys_addr_t pci_ram_top;    /* top of region accessible to PCI */
    #endif
    #ifdef CONFIG_PCI_BOOTDELAY
        int pcidelay_done;
    #endif
        struct udevice *cur_serial_dev;    /* current serial device */
        struct arch_global_data arch;    /* architecture-specific data */
    #ifdef CONFIG_CONSOLE_RECORD
        struct membuff console_out;    /* console output */
        struct membuff console_in;    /* console input */
    #endif
    #ifdef CONFIG_DM_VIDEO
        ulong video_top;        /* Top of video frame buffer area */
        ulong video_bottom;        /* Bottom of video frame buffer area */
    #endif
    } gd_t;

    所以,将r0寄存器的值写到r9寄存器,其实就是设置gd指针的值为0x0x0091FA00,也就是指向在OCRAM中分配的struct global_data结构体的首地址。

    接下来,带参调用board_init_f_init_reserve()函数,参数为r0寄存器的值,也就是struct global_data结构体首地址0x0091FA00,board_init_f_init_reserve()函数的定义如下:

    void board_init_f_init_reserve(ulong base)
    {
        struct global_data *gd_ptr;
    #ifndef _USE_MEMCPY
        int *ptr;
    #endif
    
        /*
         * clear GD entirely and set it up.
         * Use gd_ptr, as gd may not be properly set yet.
         */
    
        gd_ptr = (struct global_data *)base;    //获取global_data结构体首地址
        /* zero the area */
    #ifdef _USE_MEMCPY
        memset(gd_ptr, '', sizeof(*gd));    //将global_data结构体清0操作
    #else
        for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
            *ptr++ = 0;
    #endif
        /* set GD unless architecture did it already */
    #if !defined(CONFIG_ARM)
        arch_setup_gd(gd_ptr);
    #endif
        /* next alloc will be higher by one GD plus 16-byte alignment */
        base += roundup(sizeof(struct global_data), 16); //指向early_alloc的起始地址0x0091FB00
    
        /*
         * record early malloc arena start.
         * Use gd as it is now properly set for all architectures.
         */
    
    #if defined(CONFIG_SYS_MALLOC_F)
        /* go down one 'early malloc arena' */
        gd->malloc_base = base;    //设置gd->malloc_base指向early malloc首地址
        /* next alloc will be higher by one 'early malloc arena' size */
        base += CONFIG_SYS_MALLOC_F_LEN;
    #endif
    }

    在上面的代码中可以看出,该函数主要是将OCRAM中分配的struct global_data结构体区域进行清0操作,另外设置gd->malloc_base成员的值为early malloc区域的首地址。

    接下来就是设置r0寄存器的值为0,然后调用board_init_f()函数,传入的参数为r0寄存器的值0,该函数的定义在文件common/board_f.c文件中,该函数实现比较复杂,主要实现的功能是完成DDR、定时器、uboot代码拷贝等,另起篇章进行分析,本篇主要内容是分析_main函数的实现流程。

    将board_init_f()函数跳过后,接下来就是到了_main函数的第二部分,设置中间环境(新的sp指针值和gd指针值),并且调用relocate_code函数,代码如下所示:

    /*
     * Set up intermediate environment (new sp and gd) and call
     * relocate_code(addr_moni). Trick here is that we'll return
     * 'here' but relocated.
     */
    
        ldr    sp, [r9, #GD_START_ADDR_SP]    /* sp = gd->start_addr_sp */ //获取新的sp指针值
    #if defined(CONFIG_CPU_V7M)    /* v7M forbids using SP as BIC destination */
        mov    r3, sp
        bic    r3, r3, #7
        mov    sp, r3
    #else
        bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */ //sp指针8字节对齐
    #endif
        ldr    r9, [r9, #GD_BD]        /* r9 = gd->bd */    //设置新的gd指针值
        sub    r9, r9, #GD_SIZE        /* new GD is below bd */
    
        adr    lr, here
        ldr    r0, [r9, #GD_RELOC_OFF]        /* r0 = gd->reloc_off */
        add    lr, lr, r0
    #if defined(CONFIG_CPU_V7M)
        orr    lr, #1                /* As required by Thumb-only */
    #endif
        ldr    r0, [r9, #GD_RELOCADDR]        /* r0 = gd->relocaddr */
        b    relocate_code    //跳转到relocate_code函数
    here:
    /*
     * now relocate vectors
     */
    
        bl    relocate_vectors

    代码执行后,首先是重新对sp指针的值进行赋值,sp = gd->start_addr_sp,在board_init_f()函数中会初始化struct global_data结构体的成员变量,代码中的宏GD_START_ADDR_SP为64,为struct global_data结构体中start_addr_sp指针的偏移量,该成员变量的值为DDR内存中的地址,新的sp指针值和gd指针值将会存放DDR的内存地址,而不再是OCRAM的地址,获取到新的sp指针值后,对其进行8字节对齐,同样,对r9寄存器重新赋值,r9 = gd->bd,在上面说过,r9寄存器里面保存着gd指针的值,此时gd指针的值已经为DDR的内存地址,然后r9 = r9 - GD_SIZE,新的gd指针值在bd下面,在这,就完成了新的sp和gd指针值赋值。

    继续往下分析,设置lr寄存器为here,该技巧用于后期执行完其它函数后,进行返回,则直接返回到here处继续执行,宏GD_RELOC_OFF的值为68,设置r0寄存器的值,r0 = gd->reloc_off,lr寄存器的值加上r0寄存器的值,将结果重新赋值给lr寄存器,因为接下来,要开始重新定位uboot代码了,把代码拷贝到DDR中新的内存地址里面去,在uboot.map分析中,可以知道,当前的uboot起始地址为0x87800000,重新定位的代码包括here,因此,需要将lr寄存器的值加上r0寄存器的值,并将新的结果赋给lr寄存器,lr寄存器需要使用的是重新定位后的here。

    宏GD_RELOCADDR为48,重新将r0寄存器赋值,r0 = gd->relocaddr,struct global_data结构体中的relocaddr成员变量保存着uboot要拷贝的RAM目标地址,所以,此时r0寄存器中保存着uboot重新定位的目标地址,接下来,带参调用函数relocate_code,传入的参数为r0寄存器的值,该函数的定义在arch/arm/lib/relocate.S文件中。

    函数relocate_code调用完成后,返回到here执行,继续调用函数relocate_vectors重新定位中断向量表vectors,该函数的定义也在arch/arm/lib/relocate.S文件中,到这里,第二部分的代码就执行完成了。

    继续分析_main函数的第三部分,主要是完成最终的环境设置,并且调用board_init_r()函数,第三部分的代码如下所示:

    /* Set up final (full) environment */
    
        bl    c_runtime_cpu_setup    /* we still call old routine here */
    #endif
    #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
    # ifdef CONFIG_SPL_BUILD
        /* Use a DRAM stack for the rest of SPL, if requested */
        bl    spl_relocate_stack_gd
        cmp    r0, #0
        movne    sp, r0
        movne    r9, r0
    # endif
        ldr    r0, =__bss_start    /* this is auto-relocated! */
    
    #ifdef CONFIG_USE_ARCH_MEMSET
        ldr    r3, =__bss_end        /* this is auto-relocated! */
        mov    r1, #0x00000000        /* prepare zero to clear BSS */
    
        subs    r2, r3, r0        /* r2 = memset len */
        bl    memset
    #else
        ldr    r1, =__bss_end        /* this is auto-relocated! */
        mov    r2, #0x00000000        /* prepare zero to clear BSS */
    
    clbss_l:cmp    r0, r1            /* while not at end of BSS */
    #if defined(CONFIG_CPU_V7M)
        itt    lo
    #endif
        strlo    r2, [r0]        /* clear 32-bit BSS word */
        addlo    r0, r0, #4        /* move to next */
        blo    clbss_l
    #endif
    
    #if ! defined(CONFIG_SPL_BUILD)
        bl coloured_LED_init
        bl red_led_on
    #endif
        /* call board_init_r(gd_t *id, ulong dest_addr) */
        mov     r0, r9                  /* gd_t */
        ldr    r1, [r9, #GD_RELOCADDR]    /* dest_addr */
        /* call board_init_r */
    #if defined(CONFIG_SYS_THUMB_BUILD)
        ldr    lr, =board_init_r    /* this is auto-relocated! */
        bx    lr
    #else
        ldr    pc, =board_init_r    /* this is auto-relocated! */
    #endif
        /* we should not return here. */
    #endif

    代码执行后,首先是调用了函数c_runtime_cpu_setup,该函数的定义在文件arch/arm/cpu/armv7/start.S文件中,主要是完成与ARM处理器的配置,定义如下:

    ENTRY(c_runtime_cpu_setup)
    /*
     * If I-cache is enabled invalidate it
     */
    #ifndef CONFIG_SYS_ICACHE_OFF
        mcr    p15, 0, r0, c7, c5, 0    @ invalidate icache
        mcr     p15, 0, r0, c7, c10, 4    @ DSB
        mcr     p15, 0, r0, c7, c5, 4    @ ISB
    #endif
    
        bx    lr
    
    ENDPROC(c_runtime_cpu_setup)

    接下来,则是清除bss段,board_init_r(gd_t *id, ulong dest_addr)函数具有两个参数,第一个参数为gd指针的值,第二个参数为gd->relocaddr,设置完成后,则是调用board_init_r()函数,该函数的定义在文件common/board_r.c文件中。

    到这里,基本分析完了_main函数调用的大概流程了,该函数实现的功能非常多,比较复杂,比较重要的包括board_init_f函数、relocate_code函数、relocate_vectors函数和board_init_r函数,这些函数都将分篇章进行分析。

    3、小结

    大概对_main函数的总体流程以及实现的功能有初步了解后,最后,对_main函数的调用流程思路整理一下,如下所示:

    _main
        |
        board_init_f_alloc_reserve-->reserve gd and early malloc area
        |
        board_init_f_init_reserve-->initialize global data
        |
        board_init_f-->initialize ddr,timer...,and fill gd_t
        |
        relocate_code-->relocate uboot code
        |
        relocate_vectors-->relocate vectors
        |
        board_init_r-->calling board_init_r

    接下来,将在下一篇文章中对board_init_f函数进行分析。

  • 相关阅读:
    C#执行cmd命令
    mongodb 高级查询详解(2)
    mongodb-管道操作:常规查询
    python-pymongo高级查询
    traceback异常打印
    Sanic基础和测试
    Python网络爬虫实战:根据天猫胸罩销售数据分析中国女性胸部大小分布
    POST提交数据的四种方式
    pymongo基础:入门
    python中__name__的意义
  • 原文地址:https://www.cnblogs.com/Cqlismy/p/12002764.html
Copyright © 2020-2023  润新知