IMX6ULL-uboot启动内核流程分析(3)

uboot版本2016.03

在上一节中学习到uboot开始执行_main函数,_main函数定义在/arch/arm/lib/crt0.S中,_main函数代码比较长,代码中主要是调用了以下函数:

① board_init_f_alloc_reserve

② board_init_f_init_reserve

③ board_init_f

④ relocate_code

⑤ relocate_vectors

⑦ board_init_r

board_init_f_alloc_reserve函数

函数代码如下:

ulong board_init_f_alloc_reserve(ulong top)
{
    /* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
    top -= CONFIG_SYS_MALLOC_F_LEN;    //top -= 0x400
#endif
    /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
    top = rounddown(top-sizeof(struct global_data), 16);    //留出gd空间大小

    return top;
}

该函数有一个参数,通过寄存器r0(0x0091FF00)传入,该函数的作用是为早期的malloc函数和全局变量gd留出空间,并返回top值,函数执行完毕后内存如下图所示:

board_init_f_init_reserve

在执行完board_init_f_alloc_reserve函数以后,寄存器r0保存着函数返回值为(0x0091FA00),并将r0寄存器的值存放进r9寄存器。因为uboot中规定了r9寄存器存放的是一个指向全局变量gd的地址。

gd是一个gd_t类型的结构体,定义在/include/asm-generic/global_data.h 。

随后调用board_init_f_init_reserve函数,该函数的参数就是r0(0x0091FA00)

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;
    /* zero the area */
#ifdef _USE_MEMCPY
    memset(gd_ptr, '\0', sizeof(*gd));
#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);

    /*
     * 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;
    /* next alloc will be higher by one 'early malloc arena' size */
    base += CONFIG_SYS_MALLOC_F_LEN;
#endif
}

该函数的作用主要是清空gd结构体中的内容,同时设置了gd->malloc_base地址的值为0X0091FB00(采用16字节对齐)。

board_init_f函数

执行完board_init_f_init_reserve函数就会继续执行,board_init_f函数主要

①、初始化一系列外设,比如串口、定时器,打印一些消息等。

②、初始化 gd 的各个成员变量, uboot 会将自己拷贝到 DRAM 最后面的内存区域中。在拷贝之前需要通过gd结构体的内容分配,最终形成一个完整的内存“分配图”,在后面重定位 uboot 的时候就会用到这个内存“分配图”。

void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
    /*
     * For some archtectures, global data is initialized and used before
     * calling this function. The data should be preserved. For others,
     * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
     * here to host global data until relocation.
     */
    gd_t data;

    gd = &data;

    /*
     * Clear global data before it is accessed at debug print
     * in initcall_run_list. Otherwise the debug print probably
     * get the wrong vaule of gd->have_console.
     */
    zero_global_data();
#endif

    gd->flags = boot_flags;
    gd->have_console = 0;

    if (initcall_run_list(init_sequence_f))
        hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
        !defined(CONFIG_EFI_APP)
    /* NOTREACHED - jump_to_copy() does not return */
    hang();
#endif
}

由于未定义宏定义CONFIG_SYS_GENERIC_GLOBAL_DATA,条件编译之间的代码无效。

第25行,通过函数initcall_run_list初始化序列init_sequence_f中的函数。initcall_run_list代码如下:

int initcall_run_list(const init_fnc_t init_sequence[])
{
    const init_fnc_t *init_fnc_ptr;

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        unsigned long reloc_ofs = 0;
        int ret;

        if (gd->flags & GD_FLG_RELOC)
            reloc_ofs = gd->reloc_off;
#ifdef CONFIG_EFI_APP
        reloc_ofs = (unsigned long)image_base;
#endif
        debug("initcall: %p", (char *)*init_fnc_ptr - reloc_ofs);
        if (gd->flags & GD_FLG_RELOC)
            debug(" (relocated to %p)\n", (char *)*init_fnc_ptr);
        else
            debug("\n");
        ret = (*init_fnc_ptr)();
        if (ret) {
            printf("initcall sequence %p failed at call %p (err=%d)\n",
                   init_sequence,
                   (char *)*init_fnc_ptr - reloc_ofs, ret);
            return -1;
        }
    }
    return 0;
}

在initcall_run_list函数的for循环中,会依次执行函数指针数组各表项指向的内容。

init_sequence函数指数组定义在common/board_f.c中。函数指针数组部分代码如下:

static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOX
    setup_ram_buf,
#endif
    setup_mon_len,
#ifdef CONFIG_OF_CONTROL
    fdtdec_setup,
#endif
#ifdef CONFIG_TRACE
    trace_early_init,
#endif
    initf_malloc,
    initf_console_record,
#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
    /* TODO: can this go into arch_cpu_init()? */
    probecpu,
#endif
#if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP)
    x86_fsp_init,
#endif
    arch_cpu_init,        /* basic arch cpu dependent setup */
    initf_dm,
    arch_cpu_init_dm,
    mark_bootstage,        /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
    board_early_init_f,

/******省略大部分********/
}

列举部分重要函数:

initf_malloc函数设置了gd->malloc_limit = 0x400,也就是内存池的大小。

board_early_init_f 初始化串口的IO配置

timer_init初始化定时器,通过此定时器给uboot提供时钟

env_init 设置gd的成员环境变量

init_baud_rate根据gd的环境变量设置波特率

serial_init初始化串口

console_init_f设置控制台,初始化完串口和控制台这也是为什么能在屏幕上看到打印信息的原因

dram_init设置gd->ram_size的值,我的板子为512MB

setup_dest_addr设置目的地址为以下三个值:

gd->ram_size = 0X20000000 //ram 大小为 0X20000000=512MB
gd->ram_top = 0XA0000000 //ram 最高地址为 0X80000000+0X20000000=0XA0000000
gd->relocaddr = 0XA0000000 //重定位后最高地址为 0XA0000000

还有很多内容暂时省略。。。。。。

在所有的配置函数执行完毕以后,会形成以下在ddr中的内存分配图!!

relocate_code函数

在内存分配图形成以后,就会执行relocate_code函数实现对uboot的拷贝工作。

ENTRY(relocate_code)
    ldr    r1, =__image_copy_start    /* r1 <- SRC &__image_copy_start */
    subs    r4, r0, r1        /* r4 <- relocation offset */
    beq    relocate_done        /* skip relocation */
    ldr    r2, =__image_copy_end    /* r2 <- SRC &__image_copy_end */

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

    /*
     * fix .rel.dyn relocations
     */
    ldr    r2, =__rel_dyn_start    /* r2 <- SRC &__rel_dyn_start */
    ldr    r3, =__rel_dyn_end    /* r3 <- SRC &__rel_dyn_end */
fixloop:
    ldmia    r2!, {r0-r1}        /* (r0,r1) <- (SRC location,fixup) */
    and    r1, r1, #0xff
    cmp    r1, #23            /* relative fixup? */
    bne    fixnext

    /* relative fix: increase location by offset */
    add    r0, r0, r4
    ldr    r1, [r0]
    add    r1, r1, r4
    str    r1, [r0]
fixnext:
    cmp    r2, r3
    blo    fixloop

relocate_done:

#ifdef __XSCALE__
    /*
     * On xscale, icache must be invalidated and write buffers drained,
     * even with cache disabled - 4.2.7 of xscale core developer's manual
     */
    mcr    p15, 0, r0, c7, c7, 0    /* invalidate icache */
    mcr    p15, 0, r0, c7, c10, 4    /* drain write buffer */
#endif

    /* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
    mov    pc, lr
#else
    bx    lr
#endif

ENDPROC(relocate_code)

第2行,将uboot拷贝的起始地址保存到r1寄存器

第3行,寄存器r0保存的是uboot拷贝的目标首地址,计算出偏移地址保存到r4寄存器

第4行,如果r0-r1=0,直接跳过拷贝

第5行,寄存器r2保存拷贝之前的uboot代码结束地址。

第7行,通过函数copy_loop完成拷贝。拷贝函数流程:每次将r1寄存器值地址上的内容拷贝到r0和r11,拷贝完后r1会更新下一个要拷贝的数据地址。然后将r10和r11的数据写到r0寄存器值地址上。直到r1寄存器值=r2寄存器值,跳出循环。至此完成了uboot的拷贝。

relocate_vectors函数

此函数的作用是用来重定位异常向量表,主要完成了以下工作:

将gd->relocaddr(uboot重定位后首地址)赋值给r0寄存器,将r0寄存器的值写入到VBAR寄存器,此寄存器用于向量表重定位。

board_init_r函数

void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    int i;
#endif

#ifdef CONFIG_AVR32
    mmu_init_r(dest_addr);
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
    gd = new_gd;
#endif

#ifdef CONFIG_NEEDS_MANUAL_RELOC
    for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
        init_sequence_r[i] += gd->reloc_off;
#endif

    if (initcall_run_list(init_sequence_r))
        hang();

    /* NOTREACHED - run_main_loop() does not return */
    hang();
}

在board_init_f主要是调用initcall_run_list函数完成对函数指针数组init_sequence_r各表项的调用,最终实现了剩余外设的初始化,并且设置gd->flags表示uboot和向量表的重定位已经完成。

至此外设初始化完毕。

uboot有关命令行解析等函数先不分析。。

猜你喜欢

转载自blog.csdn.net/qq_42174306/article/details/128808357