项目中使用的u-boot版本是u-boot-2016.09,在该版本中引入了Kconfig,可以通过menuconfig进行配置。但是,遗憾的是,menuconfig还不够成熟,很多配置还是需要在.h配置文件中进行配置。u-boot文档中也说明了该情况,现在是把之前的配置方式往menuconfig上搬移的过程,但是需要一定的时间,希望早日完成。顶层的Makefile就不去分析了,网上相关资料很多。通过该文件可以找到程序的入口函数。
_start (arch/arm/lib/vector.S)
reset (arch/arm/cpu/armv7/start.S)
cpu_init_crit (arch/arm/cpu/armv7/start.S)
lowlevel_init (arch/arm/cpu/armv7/lowlevel_init.S)
s_init (arch/arm/cpu/armv7/am33xx/board.c)
watchdog_disable
set_uart_mux_conf
setup_clocks_for_console
uart_soft_reset
_main (arch/arm/lib/crt0.S) SPL Section
--------------------------------------------------------------------------
board_init_f (arch/arm/cpu/armv7/am33xx/board.c)
board_early_init_f (arch/arm/cpu/armv7/am33xx/board.c)
prcm_init (arch/arm/cpu/armv7/am33xx/clock.c)
set_mux_conf_regs (board/ti/am335x/board.c)
board_init_r (common/spl/spl.c)
timer_init
spl_board_init
save_omap_boot_params
preloader_console_init
serial_init
gpmc_init
am33xx_spl_board_init (arch/arm/cpu/armv7/am33xx/board.c)
board_boot_order
spl_board_prepare_for_boot
jump_to_image_no_args
_main (arch/arm/lib/crt0.S) U-boot Section
--------------------------------------------------------------------------
board_init_f (common/board_f.c)
initcall_run_list(init_sequence_f)
... ...
jump_to_copy
board_init_r (common/board_r.c)
initcall_run_list(init_sequence_r)
... ...
run_main_loop
上面的代码是基于AM335x的u-boot代码的简单流程。
第一部分是汇编代码,这是SPL和u-boot都包含的代码。
第二部分是SPL私有的代码。
第三部分是u-boot私有的代码。
arch/arm/cpu/armv7/am33xx/board.c
void s_init(void)
{
/*
* The ROM will only have set up sufficient pinmux to allow for the
* first 4KiB NOR to be read, we must finish doing what we know of
* the NOR mux in this space in order to continue.
*/
#ifdef CONFIG_NOR_BOOT
enable_norboot_pin_mux();
#endif
watchdog_disable();
set_uart_mux_conf();
setup_clocks_for_console();
uart_soft_reset();
#if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC)
/* Enable RTC32K clock */
rtc32k_enable();
#endif
}
该函数实现的功能:
该函数是通过宏CONFIG_SKIP_LOWLEVEL_INIT来控制的,如果定义了该宏,则屏蔽掉该函数的实现。
一般来说,SPL阶段实现该函数,u-boot阶段屏蔽该函数,因为SPL已经设置好相应的硬件,如串口,
则u-boot不需要重复设置,后面很多函数都类似。
arch/arm/cpu/armv7/am33xx/board.c
#ifdef CONFIG_SPL_BUILD
void board_init_f(ulong dummy)
{
board_early_init_f();
sdram_init();
}
#endif
通过CONFIG_SPL_BUILD宏来标定是SPL阶段的代码。
arch/arm/cpu/armv7/am33xx/board.c
/*
* In the case of non-SPL based booting we'll want to call these
* functions a tiny bit later as it will require gd to be set and cleared
* and that's not true in s_init in this case so we cannot do it there.
*/
int board_early_init_f(void)
{
prcm_init();
set_mux_conf_regs();
return 0;
}
board_init_f功能如下:
common/spl/spl.c
void board_init_r(gd_t *dummy1, ulong dummy2)
{
int i;
debug(">>spl:board_init_r()\n");
#if defined(CONFIG_SYS_SPL_MALLOC_START)
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
gd->flags |= GD_FLG_FULL_MALLOC_INIT;
#endif
if (!(gd->flags & GD_FLG_SPL_INIT)) {
if (spl_init())
hang();
}
#ifndef CONFIG_PPC
/*
* timer_init() does not exist on PPC systems. The timer is initialized
* and enabled (decrementer) in interrupt_init() here.
*/
timer_init();
#endif
#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
#endif
board_boot_order(spl_boot_list);
for (i = 0; i < ARRAY_SIZE(spl_boot_list) &&
spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
announce_boot_device(spl_boot_list[i]);
if (!spl_load_image(spl_boot_list[i]))
break;
}
if (i == ARRAY_SIZE(spl_boot_list) ||
spl_boot_list[i] == BOOT_DEVICE_NONE) {
puts("SPL: failed to boot from all boot devices\n");
hang();
}
switch (spl_image.os) {
case IH_OS_U_BOOT:
debug("Jumping to U-Boot\n");
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug("Jumping to Linux\n");
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug("Unsupported OS image.. Jumping nevertheless..\n");
}
#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
gd->malloc_ptr / 1024);
#endif
debug("loaded - jumping to U-Boot...");
spl_board_prepare_for_boot();
jump_to_image_no_args(&spl_image);
}
该函数主要由两部分组成:
arch/arm/cpu/armv7/omap-common/boot-common.c
void spl_board_init(void)
{
/*
* Save the boot parameters passed from romcode.
* We cannot delay the saving further than this,
* to prevent overwrites.
*/
save_omap_boot_params();
/* Prepare console output */
preloader_console_init();
#if defined(CONFIG_SPL_NAND_SUPPORT) || defined(CONFIG_SPL_ONENAND_SUPPORT)
gpmc_init();
#endif
#ifdef CONFIG_SPL_I2C_SUPPORT
i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE);
#endif
#if defined(CONFIG_AM33XX) && defined(CONFIG_SPL_MUSB_NEW_SUPPORT)
arch_misc_init();
#endif
#if defined(CONFIG_HW_WATCHDOG)
hw_watchdog_init();
#endif
#ifdef CONFIG_AM33XX
am33xx_spl_board_init();
#endif
}
根据不同的宏开关来初始化对应的硬件信息,如Nand、I2C等。
最终加载u-boot之后,就通过jump_to_image_no_args函数跳转至u-boot中运行。
common/board_f.c
board_init_f
该函数主体没有太多东西,就是依次调用init_sequence_f函数指针数组中的函数去执行。
在这些函数中有env_init、serial_init等。
common/board_f.c
board_init_r
类似于board_init_f,依次调用init_sequence_r函数指针数组中的函数去执行。
初始化更高级些的东西,如Nand、Net等,最后调用run_main_loop函数,提供用户交互界面。
common/main.c
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
#ifdef CONFIG_VERSION_VARIABLE
setenv("ver", version_string); /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */
cli_init();
run_preboot_environment_command();
#if defined(CONFIG_UPDATE_TFTP)
update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
panic("No CLI available");
}
bootdelay_process函数用来获取环境变量bootdelay和bootcmd的内容。
void autoboot_command(const char *s)
{
debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "" );
if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
run_command_list(s, -1, 0);
}
}
bootdelay和bootcmd都有值的话,则去延时bootdelay。在延时过程中,如果用户按下了任意键,则进入交互界面。否则,执行bootcmd命令,该命令一般是加载内核的命令。