下面详细介绍board_init_f函数的调用流程
board_init_f主要做一些运行环境的初始化,如:
获取uboot 的长度,获取fdt的地址,并进行合法化检查,初始化trace,初始化mem malloc,配置console buffer,cpu 架构的初始化,环境变量的初始化,串口的初始化,打印板子的一些信息,然后对内存进行reserve划分,初始化gd里面的变量,重定位fdt,最后重定位uboot自己,不在返回。
1、board_init_f函数很简单,关键就是遍历执行数组init_sequence_f里面的函数
void board_init_f(ulong boot_flags)
{
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) && !CONFIG_IS_ENABLED(X86_64) && \
!defined(CONFIG_ARC)
/* NOTREACHED - jump_to_copy() does not return */
hang(); /* 除了上面的core, 其他的应该在initcall_run_list 就不再返回了,否则就是出错了 */
#endif
}
2、init_sequence_f 数组内容,也就是board_init_f调用的回调函数。
static const init_fnc_t init_sequence_f[] = {
setup_mon_len, //设置 gd 的 mon_len 成员变量,此处为 __bss_end - _start,即整个代码的长度
#ifdef CONFIG_OF_CONTROL
/*
* 对dtb进行合法性验证:
* 1.获取fdt的地址.
* 2.通过fdtdec_prepare_fdt来对fdt进行合法性检查,判断dtb是否存在,以及是否有四个字节对齐。
* 3.然后再调用fdt_check_header看看头部是否正常,fdt_check_header主要是检查dtb的magic是否正确
*/
fdtdec_setup,
#endif
#ifdef CONFIG_TRACE_EARLY
trace_early_init,//用于初始化trace,以便u-boot进行trace debug
#endif
initf_malloc, //初始化 gd 中跟 malloc 有关的成员变量,比如 malloc_limit,表示 malloc 内存池大小
//初始化list gd->log_head,把能输出log的device-driver加入到list里面
log_init,
initf_bootstage, /* uses its own timer, so does not need DM, 在gd->bootstage里面找一个地方,记录board_init_f()的启动状态 */
#ifdef CONFIG_BLOBLIST
bloblist_init,
#endif
setup_spl_handoff, /* CONFIG_HANDOFF未配置,因此该函数仅返回0 */
initf_console_record,/*申请gd->console_out和gd->console_in,用于启动控制台的记录buffer*/
#if defined(CONFIG_HAVE_FSP)
arch_fsp_init,/* only for intel platform */
#endif
/*
* 用于基于特定架构下的cpu初始化操作。该函数是一个弱函数,如果特定架构下的代码没有重新实现这个函数功能,那么这个函数将什么也不做
* risc-v 架构没看到实现
*/
arch_cpu_init, /* basic arch cpu dependent setup */
/*
* 与arch_cpu_init函数类似,该函数是一个弱函数,SoC/平台相关的CPU设置
*/
mach_cpu_init, /* SoC/machine dependent CPU setup */
/*
* dm:驱动模型,类似于kernel的driver的通用框架,使用probe进行初始化驱动
* 使用 initf_dm创建一个dm_root并将其绑定到"root driver”(driver),
* 然device_probe 来激活这个设备。第二步使用dm_scan来绑定设备树中的设备和驱动到dm_root下面。
*/
initf_dm,
/* 做cpu的一些初始化 */
arch_cpu_init_dm,
#if defined(CONFIG_BOARD_EARLY_INIT_F)
// 这个函数由移植者提供,通常定义在开发板目录下,做一些板级的初始化。
// 提供这个函数的同时还需要定义配置宏CONFIG_BOARD_EARLY_INIT_F,这样才能将该函数真正编译进去
board_early_init_f,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
/* get CPU and bus clocks according to the environment variable */
get_clocks, /* get CPU and bus clocks (etc.),获取时钟参信息,并填到gd里面 */
#endif
#if !defined(CONFIG_M68K)
//与具体体系结构和处理器芯片相关,初始化定时器,通过这个定时器来为u-boot提供时间参数
timer_init, /* initialize timer */
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
board_postclk_init,
#endif
/*
* 该函数用于存储环境变量的环境初始化。
* 该函数的实现本质是:设置gd的成员变量env_addr,也就是环境变量的保存地址。
*/
env_init, /* initialize environment */
//初始化波特率,根据CONFIG_BAUDRATE来初始化gd->baudrate
init_baud_rate, /* initialze baudrate settings */
//初始化串口,通过default_serial_console获取当前的串口驱动,
//根据自己的板子上的串口类型,实现串口驱动.
serial_init, /* serial communications setup */
//控制台初始化
console_init_f, /* stage 1 init of console */
//输出一些关于u-boot的一些信息,如版本,编译时间等等
display_options, /* say that we are here */
//输出text,bss段的信息
display_text_info, /* show debugging info if required */
//打印出cpu相关的信息,根据不同架构或者芯片来实现该函数
checkcpu,
#if defined(CONFIG_SYSRESET)
print_resetinfo,
#endif
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed),根据框架实现,基本上都是打印cpu的一些信息*/
#endif
#if defined(CONFIG_DTB_RESELECT)
embedded_dtb_select,
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
show_board_info, //打印板子的信息
#endif
INIT_FUNC_WATCHDOG_INIT //看门狗的初始化
#if defined(CONFIG_MISC_INIT_F)
misc_init_f, //misc设备的初始化
#endif
INIT_FUNC_WATCHDOG_RESET //看门狗reset,避免watchdog触发板子的启动
#if defined(CONFIG_SYS_I2C)
init_func_i2c, //初始化iic
#endif
#if defined(CONFIG_VID) && !defined(CONFIG_SPL)
init_func_vid,
#endif
announce_dram_init, //only 打印 DRAM:
dram_init, /* configure available RAM banks,不是初始化DRAM,只是设置gd->sram_sized的值,并打印dram的信息 */
#ifdef CONFIG_POST
post_init_f,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_DRAM_TEST)
testdram, //测试dram,板级代码
#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, //打印各种地址信息
#ifdef CONFIG_OF_BOARD_FIXUP
fix_fdt,
#endif
// 下面reserve_开头的函数都是预留一些地址,填充gd里面的信息,方便后续使用
#ifdef CONFIG_PRAM
reserve_pram,
#endif
reserve_round_4k,
arch_reserve_mmu,
reserve_video,
reserve_trace,
reserve_uboot,
reserve_malloc,
reserve_board,
setup_machine,
reserve_global_data,
reserve_fdt,
reserve_bootstage,
reserve_bloblist,
reserve_arch,
reserve_stacks,
dram_init_banksize,
show_dram_config, // 打印出dram的配置信息
INIT_FUNC_WATCHDOG_RESET
setup_bdinfo,//设置board相关的信息,即设置gd中的bd结构下的变量
display_new_sp, //打印新的堆栈信息
INIT_FUNC_WATCHDOG_RESET
//重定位下面的地址
reloc_fdt,
reloc_bootstage,//用于重定位已经存在的启动阶段中的记录
reloc_bloblist, //重定位bloblist
setup_reloc,//启动重定位标志来进行启动重定位, 重定位gd地址
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
copy_uboot_to_ram,
do_elf_reloc_fixups,
#endif
clear_bss,//清bss段
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!CONFIG_IS_ENABLED(X86_64)
jump_to_copy, //riscv会走到这里,会在这里进行重定位,arm的会在后门进行重定位,都少不了的
// 最终会调用relocate_code,位于start.S,进行重定位,不会返回了,接着往下走
#endif
NULL,
};