u-boot代码分析

简介

项目中使用的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不需要重复设置,后面很多函数都类似。

SPL阶段的代码

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);
}

该函数主要由两部分组成:

  • 初始化板级硬件
  • 根据ROM code传递的启动源,来加载u-boot

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中运行。

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 : "<UNDEFINED>");

    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
        run_command_list(s, -1, 0);
    }
}

bootdelay和bootcmd都有值的话,则去延时bootdelay。在延时过程中,如果用户按下了任意键,则进入交互界面。否则,执行bootcmd命令,该命令一般是加载内核的命令。

猜你喜欢

转载自blog.csdn.net/donglicaiju76152/article/details/77917340