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 = ; 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分配空间,重新指定内存中的地址