uboot的启动过程-FDT

uboot的启动过程,省略了汇编部分之后,第一个执行函数是board_init_f(),在uboot/common目录的board_f.c中
 
board_init_f函数,首先初始化了全局数据
#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
 
接下来,就是通过函数initcall_run_list,依次调用init_sequence_f列表里面的模块初始化函数。
    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
 
我手头的uboot源码中,init_sequence_f初始化列表如下,不同的版本会有些差别。注意这里的uboot-spl,就是第一阶段uboot。
  由uboot编译生成,对应于BL1阶段,也就是BL1的镜像,uboot-spl.bin。
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,
#endif
    /* TODO: can any of this go into arch_cpu_init()? */
#if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT)
    get_clocks,        /* get CPU and bus clocks (etc.) */
#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \
        && !defined(CONFIG_TQM885D)
    adjust_sdram_tbs_8xx,
#endif
    /* TODO: can we rename this to timer_init()? */
    init_timebase,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
        defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
        defined(CONFIG_SPARC)
    timer_init,        /* initialize timer */
#endif
#ifdef CONFIG_SYS_ALLOC_DPRAM
#if !defined(CONFIG_CPM2)
    dpram_init,
#endif
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
    board_postclk_init,
#endif
#if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
    get_clocks,
#endif
    env_init,        /* initialize environment */
#if defined(CONFIG_8xx_CPUCLK_DEFAULT)
    /* get CPU and bus clocks according to the environment variable */
    get_clocks_866,
    /* adjust sdram refresh rate according to the new clock */
    sdram_adjust_866,
    init_timebase,
#endif
    init_baud_rate,        /* initialze baudrate settings */
    serial_init,        /* serial communications setup */
    console_init_f,        /* stage 1 init of console */
#ifdef CONFIG_SANDBOX
    sandbox_early_getopt_check,
#endif
#ifdef CONFIG_OF_CONTROL
    fdtdec_prepare_fdt,
#endif
    display_options,    /* say that we are here */
    display_text_info,    /* show debugging info if required */
#if defined(CONFIG_MPC8260)
    prt_8260_rsr,
    prt_8260_clks,
#endif /* CONFIG_MPC8260 */
#if defined(CONFIG_MPC83xx)
    prt_83xx_rsr,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
    checkcpu,
#endif
    print_cpuinfo,        /* display cpu info (and speed) */
#if defined(CONFIG_MPC5xxx)
    prt_mpc5xxx_clks,
#endif /* CONFIG_MPC5xxx */
#if defined(CONFIG_DISPLAY_BOARDINFO)
    show_board_info,
#endif
    INIT_FUNC_WATCHDOG_INIT
#if defined(CONFIG_MISC_INIT_F)
    misc_init_f,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
    init_func_i2c,
#endif
#if defined(CONFIG_HARD_SPI)
    init_func_spi,
#endif
    announce_dram_init,
    /* TODO: unify all these dram functions? */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
        defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
    dram_init,        /* configure available RAM banks */
#endif
#if defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_M68K)
    init_func_ram,
#endif
#ifdef CONFIG_POST
    post_init_f,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_DRAM_TEST)
    testdram,
#endif /* CONFIG_SYS_DRAM_TEST */
    INIT_FUNC_WATCHDOG_RESET
 
#ifdef CONFIG_POST
    init_post,
#endif
    INIT_FUNC_WATCHDOG_RESET
    /*
     * Now that we have DRAM mapped and working, we can
     * relocate the code and continue running from DRAM.
     *
     * Reserve memory at end of RAM for (top down in that order):
     *  - area that won't get touched by U-Boot and Linux (optional)
     *  - kernel log buffer
     *  - protected RAM
     *  - LCD framebuffer
     *  - monitor code
     *  - board info struct
     */
    setup_dest_addr,
#if defined(CONFIG_BLACKFIN)
    /* Blackfin u-boot monitor should be on top of the ram */
    reserve_uboot,
#endif
#if defined(CONFIG_SPARC)
    reserve_prom,
#endif
#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR)
    reserve_logbuffer,
#endif
#ifdef CONFIG_PRAM
    reserve_pram,
#endif
    reserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
        defined(CONFIG_ARM)
    reserve_mmu,
#endif
#ifdef CONFIG_DM_VIDEO
    reserve_video,
#else
# ifdef CONFIG_LCD
    reserve_lcd,
# endif
    /* TODO: Why the dependency on CONFIG_8xx? */
# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
        !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
        !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
    reserve_legacy_video,
# endif
#endif /* CONFIG_DM_VIDEO */
    reserve_trace,
#if !defined(CONFIG_BLACKFIN)
    reserve_uboot,
#endif
#ifndef CONFIG_SPL_BUILD
    reserve_malloc,
    reserve_board,
#endif
    setup_machine,
    reserve_global_data,
    reserve_fdt,
    reserve_arch,
    reserve_stacks,
    setup_dram_config,
    show_dram_config,
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS)
    setup_board_part1,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
    INIT_FUNC_WATCHDOG_RESET
    setup_board_part2,
#endif
    display_new_sp,
#ifdef CONFIG_SYS_EXTBDINFO
    setup_board_extra,
#endif
    INIT_FUNC_WATCHDOG_RESET
    reloc_fdt,
    setup_reloc,
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
    copy_uboot_to_ram,
    clear_bss,
    do_elf_reloc_fixups,
#endif
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
    jump_to_copy,
 
首先,我们关注device tree的读入,FDT, flatted device tree,扁平设备树, 关于uboot的fdt,可以参考doc/README.fdt-control。
在启动函数init_sequence_f列表中,与FDT相关的函数主要有三个,如下:
 
static init_fnc_t init_sequence_f[] = {
...
#ifdef CONFIG_OF_CONTROL
    fdtdec_setup, // 获取dtb的地址,并且验证dtb的合法性
#endif
...
    reserve_fdt, // 为dtb分配新的内存地址空间
...
    reloc_fdt, // relocate dtb
...
}
 
fdtdec_setup函数中,首先根据宏定义的不同,而给全局的device_treegd->fdt_blob指针赋值,可能会有内部或者外部多种情况。
 
int fdtdec_setup(void)
{
#if CONFIG_IS_ENABLED(OF_CONTROL)
# ifdef CONFIG_OF_EMBED
    /* Get a pointer to the FDT */
    gd->fdt_blob = __dtb_dt_begin;
// 当使用CONFIG_OF_EMBED的方式时,也就是dtb集成到uboot的bin文件中时,通过__dtb_dt_begin符号来获取dtb地址。
 
# elif defined CONFIG_OF_SEPARATE
#  ifdef CONFIG_SPL_BUILD
    /* FDT is at end of BSS unless it is in a different memory region */
    if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
        gd->fdt_blob = (ulong *)&_image_binary_end;
    else
        gd->fdt_blob = (ulong *)&__bss_end;
 
#  else
    /* FDT is at end of image */
    gd->fdt_blob = (ulong *)&_end;
//当使用CONFIG_OF_SEPARATE的方式时,也就是dtb追加到uboot的bin文件后面时,通过_end符号来获取dtb地址。
#  endif
# elif defined(CONFIG_OF_HOSTFILE)
    if (sandbox_read_fdt_from_file()) {
        puts("Failed to read control FDT\n");
        return -1;
    }
# endif
# ifndef CONFIG_SPL_BUILD
    /* Allow the early environment to override the fdt address */
    gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
                        (uintptr_t)gd->fdt_blob);
// 可以通过环境变量fdtcontroladdr来指定gd->fdt_blob,也就是指定fdt的地址。
# endif
#endif
    return fdtdec_prepare_fdt();
}
函数fdtdec_prepare_fdt,只是检查了一下fdt的数据头,是否正确。
reserve_fdt、 reloc_fdt在分离dts的情况下,为fdt分配空间,重新指定内存中的地址
 
 
posted @ 2018-12-13 17:34  刘建章  阅读(2606)  评论(0编辑  收藏  举报