2017.01 UBoot包含两个阶段的启动,一个是SPL启动,一个是正常的启动我们称为第二阶段Uboot。当然,我们也可以选择使用SPL和不使用。
在编译的过程中,是先编译第二阶段Uboot,然后在编译SPL的。这两个阶段的编译时分离的。
拥有不同的配置,所以许多地方的宏是和SPL的不一样。而且链接的文件也不一致。
所以接下来,我们也会分为两个部分进行分析。
在AM437X平台中,实际的SPL启动就是MLO启动阶段。其运行空间为片内DRAM,所以对其大小有较为严格的限制。
通常使用SPL初始化基本设备后,将第二阶段UBoot加载进行SDRAM中执行。
其源码位于arch/arm/cpu/armv7/start.s中。
其中关键的调用关系如下:
调用:cpu_init_crit
b lowlevel_init
==>arch/arm/cpu/armv7/lowlevel_init.S
b s_init
在第一阶段初始化完成后,便调用了s_init函数,其实现的源码是arch/arm/mach-omap2/an33xx/Borad.c。
该函数就调用了一个函数rtc_only();然后便返回到lowlevel_init函数中。
此时,lowlevel_init也已经完成他的任务,就返回到cpu_init_crit中。
然而,cpu_init_crit这个函数的工作也全都干完了,便回到start.S中,然后便是
b _main
而这个_main函数位于:
arch/arm/lib/crt0.S中
在_main函数中,就调用了一个跨时代的函数。
board_init_f (arch/arm/mach-omap2/am33xx/Borad.c)
在上面,我们可以知道 _main函数会调用board_init_f。实际上,这个函数便是和板级相关连的函数了。
然而,在SPL启动中,他指向了下面的函数:
void board_init_f(ulong dummy)
{
early_system_init();
board_early_init_f();
sdram_init();
/* dram_init must store complete ramsize in gd->ram_size */
gd->ram_size = get_ram_size(
(void *)CONFIG_SYS_SDRAM_BASE,
CONFIG_MAX_RAM_BANK_SIZE);
}
其中的sdram_ini(); 便是初始化SDRAM的关键代码,所以,如果我们有自己的板子,我们也可以实现sdram_init函数来完成我们板子的SDRAM的初始化。
其调用关系如下:
board_init_f
==>early_system_init()
==>set_uart_mux_conf(); // 设置相应的PIN为串口
==>board_early_init_f(); // 不同的板子可以实现这个函数
==>
==>sdram_init(); // 初始化SDRAM
执行完borad_init_f后,回到_main
这个时候,会去执行spl_relocate_stack_gd
重新定位在栈中的global_data。
如果定义了CONFIG_SPL_STACK_R,那么就将之前在片内内存中的global_data对象重定位到SDRAM中,并且将栈指针指向SDRAM中的某一地址
最后,就会调用borad_init_r函数:
borad_init_r(common/spl/spl.c)
==>spl_board_init(); // 初始化串口
==> if (boot_from_devices(&spl_image, spl_boot_list,
ARRAY_SIZE(spl_boot_list))) {
puts("SPL: failed to boot from all boot devices\n");
hang();
}
=>遍历loader,看哪个loader和设备匹配。
==> spl_load_image
==>spl_mmc_load_image
==>spl_mmc_do_fs_boot
==>spl_load_image_fat(uboot.img) // 将uboot.img加载到内存,并进行解析
将镜像加载到内存的时候,此时SPL进行判断,加载的镜像是uboot还是kernel然后便跳转到镜像的入口中进行执行。此时,SPL的使命便完成了
==>jump_to_image_no_args(&spl_image);
此阶段的UBoot是在内存中的了,内存已经初始化完毕,并且许多的引脚复用在上一个阶段就已经复用完毕了。
在编译的过程中,是先编译第二阶段Uboot,然后在编译SPL的。这两个阶段的编译时分离的。拥有不同的配置,所以许多地方的宏是和SPL的不一样。而且
链接的文件也不一致。
通过验证,就board_init_r函数而言,在SPL中是链接向spl.c中的board_init_r函数,而在Uboot中,是链接向board_r.c中的borad_init_r函数。
那么,我们就从borad_init_r函数入手,看看第二阶段的Uboot是怎么启动的。
在分析的过程中,
我们直接在board_init_r函数实现成一个死循环,但是发现还是有信息能够输出,说明在board_init_r还进行了初始化的工作。
U-Boot 2017.01-g03781bc-dirty (Jul 14 2017 - 13:55:58 +0800)
CPU : AM437X-GP rev 1.2
Model: TI AM437x GP EVM
I2C: ready
DRAM: 1 GiB
我们发现,其实在board_init_f中都已经链接在别的文件中了,他是位于board_f.c文件中
void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
/*
* For some architectures, 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 value 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
}
其中关键的函数就是 initcall_run_list(init_sequence_f);
我们来看看init_sequence_f是个什么东西。
static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOX
setup_ram_buf,
#endif
setup_mon_len,
#ifdef CONFIG_OF_CONTROL
fdtdec_setup, // 初始化FDT
#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 */
mach_cpu_init, /* SoC/machine dependent CPU 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_SH) || 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
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) || defined(CONFIG_SH)
checkcpu,
#endif
print_cpuinfo, /* display cpu info (and speed) */
#if defined(CONFIG_MPC5xxx)
prt_mpc5xxx_clks,
#endif /* CONFIG_MPC5xxx */
#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,
#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) || \
defined(CONFIG_SH)
dram_init, /* configure available RAM banks */ // 设置一些gd相关的参数
#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) || defined(CONFIG_XTENSA)
/* 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) && !defined(CONFIG_XTENSA)
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_M68K) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || \
defined(CONFIG_SH)
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_XTENSA)
clear_bss,
#endif
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
jump_to_copy,
#endif
NULL,
};
我们发现这个是个类似数组的玩意。通过函数名,我们大概可以知道,就是一个个按顺序去调用这个数组里面的函数。