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, '\0', 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函數進行分析。


免責聲明!

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



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