uboot main_loop函数分析

本文uboot代码版本: 2016.05
 
跑完spl之后,如果选择了引导uboot启动,最后会进入board_init_r函数,该函数中的数组init_sequence_r中的最后一个元素run_main_loop函数包含我们要介绍的main_loop函数。
 
位于common/board_r.c下的board_init_r函数, 会按顺序调用数组init_seqence_r里面的函数
 
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    int i;
#endif

#ifdef CONFIG_AVR32
    mmu_init_r(dest_addr);
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
    gd = new_gd;
#endif

#ifdef CONFIG_NEEDS_MANUAL_RELOC
    for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
        init_sequence_r[i] += gd->reloc_off;
#endif

    if (initcall_run_list(init_sequence_r))
        hang();

    /* NOTREACHED - run_main_loop() does not return */
    hang();
}
位于common/board_r.c 的数组 init_sequence_r,数组最后为run_main_loop函数
init_fnc_t init_sequence_r[] = {
    initr_trace,
    initr_reloc,
    /* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
    initr_caches,
    /* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
     *     A temporary mapping of IFC high region is since removed,
     *     so environmental variables in NOR flash is not availble
     *     until board_init() is called below to remap IFC to high
     *     region.
     */
#endif
    initr_reloc_global_data,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
    initr_unlock_ram_in_cache,
#endif
    initr_barrier,
    initr_malloc,
    initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
    initr_noncached,
#endif
    bootstage_relocate,
#ifdef CONFIG_DM
    initr_dm,
#endif
    initr_bootstage,
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
    board_init,    /* Setup chipselects */
#endif
    /*
     * TODO: printing of the clock inforamtion of the board is now
     * implemented as part of bdinfo command. Currently only support for
     * davinci SOC's is added. Remove this check once all the board
     * implement this.
     */
#ifdef CONFIG_CLOCKS
    set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
    efi_memory_init,
#endif
    stdio_init_tables,
    initr_serial,
    initr_announce,
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
    initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
    initr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
    board_early_init_r,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_LOGBUFFER
    initr_logbuffer,
#endif
#ifdef CONFIG_POST
    initr_post_backlog,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_SYS_DELAYED_ICACHE
    initr_icache_enable,
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
    /*
     * Do early PCI configuration _before_ the flash gets initialised,
     * because PCU ressources are crucial for flash access on some boards.
     */
    initr_pci,
#endif
#ifdef CONFIG_WINBOND_83C553
    initr_w83c553f,
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
    arch_early_init_r,
#endif
    power_init_board,
#ifndef CONFIG_SYS_NO_FLASH
    initr_flash,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) || \
    defined(CONFIG_SPARC)
    /* initialize higher level parts of CPU like time base and timers */
    cpu_init_r,
#endif
#ifdef CONFIG_PPC
    initr_spi,
#endif
#ifdef CONFIG_CMD_NAND
    initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
    initr_onenand,
#endif
#ifdef CONFIG_GENERIC_MMC
    initr_mmc,
#endif
#ifdef CONFIG_HAS_DATAFLASH
    initr_dataflash,
#endif
    initr_env,
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
    initr_malloc_bootparams,
#endif
    INIT_FUNC_WATCHDOG_RESET
    initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
    mac_read_from_eeprom,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
    /*
     * Do pci configuration
     */
    initr_pci,
#endif
    stdio_add_devices,
    initr_jumptable,
#ifdef CONFIG_API
    initr_api,
#endif
    console_init_r,        /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
    show_board_info,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
    arch_misc_init,        /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
    misc_init_r,        /* miscellaneous platform-dependent init */
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
    initr_kgdb,
#endif
    interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
    initr_enable_interrupts,
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K)
    timer_init,        /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED)
    initr_status_led,
#endif
    /* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
    initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
    board_late_init,
#endif
#if defined(CONFIG_CMD_AMBAPP)
    ambapp_init_reloc,
#if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP)
    initr_ambapp_print,
#endif
#endif
#ifdef CONFIG_CMD_SCSI
    INIT_FUNC_WATCHDOG_RESET
    initr_scsi,
#endif
#ifdef CONFIG_CMD_DOC
    INIT_FUNC_WATCHDOG_RESET
    initr_doc,
#endif
#ifdef CONFIG_BITBANGMII
    initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
    INIT_FUNC_WATCHDOG_RESET
    initr_net,
#endif
#ifdef CONFIG_POST
    initr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
    initr_pcmcia,
#endif
#if defined(CONFIG_CMD_IDE)
    initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
    INIT_FUNC_WATCHDOG_RESET
    /*
     * Some parts can be only initialized if all others (like
     * Interrupts) are up and running (i.e. the PC-style ISA
     * keyboard).
     */
    last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
    INIT_FUNC_WATCHDOG_RESET
    initr_bedbug,
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
    initr_mem,
#endif
#ifdef CONFIG_PS2KBD
    initr_kbd,
#endif
#if defined(CONFIG_SPARC)
    prom_init,
#endif
    run_main_loop,
};
位于同一目录下的run_main_loop函数,可以看到在一个死循环里面执行main_loop函数,也就是说run_main_loop永远不会返回。
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
    sandbox_main_loop_init();
#endif
    /* main_loop() can return to retry autoboot, if so just run it again */
    for (;;)
        main_loop();
    return 0;
}
以下为位于common/main.c下面的main_loop函数,bootstage_mark_name函数最终调用了show_boot_progress函数,该函数为空函数

/* 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");

#ifndef CONFIG_SYS_GENERIC_BOARD
    puts("Warning: Your board does not use generic board. Please read\n");
    puts("doc/README.generic-board and take action. Boards not\n");
    puts("upgraded by the late 2014 may break or be removed.\n");
#endif
#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");
}
setenv函数如下所示,用于设置环境变量ver为version_string
version_string在common/cmd_version.c定义为
const char __weak version_string[] = U_BOOT_VERSION_STRING;
U_BOOT_VERSION_STRING在include/version.h中定义为

#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \
    U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING
U_BOOT_VERSION, U_BOOT_DATE, U_BOOT_TIME, U_BOOT_TZ这几个宏在include/generated/version_autogenerated.h和include/generated/timestamp_autogenerated.h中被定义,CONFIG_IDENT_STRING在include/version.h中被定义为空("")

//nclude/generated/version_autogenerated.h  这个头文件在编译的时候自动生成
define PLAIN_VERSION "2016.05-00297-ge4b7ffa-dirty"
#define U_BOOT_VERSION "U-Boot " PLAIN_VERSION
#define CC_VERSION_STRING "arm-linux-gnueabihf-gcc (Linaro GCC 5.3-2016.02) 5.3.1 20160113"
#define LD_VERSION_STRING "GNU ld (GNU Binutils) 2.25.0 Linaro 2015_10"
//include/generated/timestamp_autogenerated.h 这个头文件在编译的时候自动生成
#define U_BOOT_DATE "Sep 11 2019"
#define U_BOOT_TIME "21:00:25"
#define U_BOOT_TZ "+0800"
#define U_BOOT_DMI_DATE "09/11/2019"
uboot起来之后可以通过printenv命令查看当前所设置的环境变量,其中就有ver这个环境变量
varname  =  "ver"
varvalue = "U-Boot 2016.05-00297-ge4b7ffa-dirty (Sep 11 2019 - 19:24:32 +0800)"
int setenv(const char *varname, const char *varvalue)
{
    const char * const argv[4] = { "setenv", varname, varvalue, NULL };

    /* before import into hashtable */
    if (!(gd->flags & GD_FLG_ENV_READY))
        return 1;

    if (varvalue == NULL || varvalue[0] == '\0')
        return _do_env_set(0, 2, (char * const *)argv, H_PROGRAMMATIC);
    else
        return _do_env_set(0, 3, (char * const *)argv, H_PROGRAMMATIC);
}
varname和varvalue都不为空,所以接着执行 _do_env_set(0, 3, (char * const *)argv, H_PROGRAMMATIC);

/*
 * Set a new environment variable,
 * or replace or delete an existing one.
 */
static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
{
    int   i, len;
    char  *name, *value, *s;
    ENTRY e, *ep;

    debug("Initial value for argc=%d\n", argc);
    while (argc > 1 && **(argv + 1) == '-') { //argv[1] = "ver", 跳过此循环
        char *arg = *++argv;
        --argc;
        while (*++arg) {
            switch (*arg) {
            case 'f':        /* force */
                env_flag |= H_FORCE;
                break;
            default:
                return CMD_RET_USAGE;
            }
        }
    }
    debug("Final value for argc=%d\n", argc);
    name = argv[1];//"ver"
    value = argv[2];//"U-Boot 2016.05-00297-ge4b7ffa-dirty (Sep 11 2019 - 20:31:15 +0800)"

    if (strchr(name, '=')) {//判断name中是否有 “=”这个字符,如没有返回NULL,此处条件不成立
        printf("## Error: illegal character '='"
               "in variable name \"%s\"\n", name);
        return 1;
    }

    env_id++;

    /* Delete only ? */
    if (argc < 3 || argv[2] == NULL) {//如果第3个参数为NULL,就删除这变量
        int rc = hdelete_r(name, &env_htab, env_flag);
        return !rc;
    }
    /*
     * Insert / replace new value
     */
    for (i = 2, len = 0; i < argc; ++i)
        len += strlen(argv[i]) + 1; 
  
    value = malloc(len);
    if (value == NULL) {
        printf("## Can't malloc %d bytes\n", len);
        return 1;
    }

    for (i = 2, s = value; i < argc; ++i) {
        char *v = argv[i];

        while ((*s++ = *v++) != '\0')
            ;
        printf("s: [%s]\n", s);
        *(s - 1) = ' ';
    }
    if (s != value)
        *--s = '\0';
    e.key    = name;
    e.data    = value;
    hsearch_r(e, ENTER, &ep, &env_htab, env_flag);
    free(value);
    if (!ep) {
        printf("## Error inserting \"%s\" variable, errno=%d\n",
            name, errno);
        return 1;
    }

    return 0;
}
接下来执行cli_init函数,在common/cli.c中对函数的定义如下,如果定义了CONFIG_SYS_HUSH_PARSER则支持hush shell,一个据说来自busybox的命令解析器

void cli_init(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
    u_boot_hush_start();
#endif

#if defined(CONFIG_HUSH_INIT_VAR)
    hush_init_var();
#endif
}
run_preboot_environment_command函数从环境变量中获取preboot的定义,该变量包含了一些预启动命令,一般环境变量中不包含该项配置。我目前所用的系统也没有定义该配置,因为没有定义CONFIG_UPDATE_TFTP,所以先不管update_tftp这个函数

static void run_preboot_environment_command(void)
{
#ifdef CONFIG_PREBOOT
    char *p;

    p = getenv("preboot");
    if (p != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
        int prev = disable_ctrlc(1);    /* disable Control C checking */
# endif

        run_command_list(p, -1, 0);

# ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
# endif
    }
#endif /* CONFIG_PREBOOT */
}
函数bootdelay_process在common/autoboot.c中定义,用于获取环境变量bootdelay的值并转成整数后保存在全局变量stored_bootdelay中,最后获取bootcmd的配置值作为函数的返回值。

const char *bootdelay_process(void)
{
    char *s;
    int bootdelay;
#ifdef CONFIG_BOOTCOUNT_LIMIT
    unsigned long bootcount = 0;
    unsigned long bootlimit = 0;
#endif /* CONFIG_BOOTCOUNT_LIMIT */

#ifdef CONFIG_BOOTCOUNT_LIMIT
    bootcount = bootcount_load();
    bootcount++;
    bootcount_store(bootcount);
    setenv_ulong("bootcount", bootcount);
    bootlimit = getenv_ulong("bootlimit", 10, 0);
#endif /* CONFIG_BOOTCOUNT_LIMIT */

    s = getenv("bootdelay");
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

#ifdef CONFIG_OF_CONTROL
    bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay",
            bootdelay);
#endif

    debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);

#if defined(CONFIG_MENU_SHOW)
    bootdelay = menu_show(bootdelay);
#endif
    bootretry_init_cmd_timeout();

#ifdef CONFIG_POST
    if (gd->flags & GD_FLG_POSTFAIL) {
        s = getenv("failbootcmd");
    } else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
    if (bootlimit && (bootcount > bootlimit)) {
        printf("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
               (unsigned)bootlimit);
        s = getenv("altbootcmd");
    } else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
        s = getenv("bootcmd");

    process_fdt_options(gd->fdt_blob);
    stored_bootdelay = bootdelay;

    return s;
}
函数cli_process_fdt,在这里一直是false,所以不会回执行cli_secure_boot_cmd函数

往下执行定义在common/autoboot.c的autoboot_command函数

void autoboot_command(const char *s)
{
    debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
        int prev = disable_ctrlc(1);    /* disable Control C checking */
#endif

        run_command_list(s, -1, 0);

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
        disable_ctrlc(prev);    /* restore Control C checking */
#endif
    }

#ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = getenv("menucmd");
        if (s)
            run_command_list(s, -1, 0);
    }
#endif /* CONFIG_MENUKEY */
}
上面第一个if中的函数abortboot函数如下,由于没有定义CONFIG_AUTOBOOT_KEYED,所以会调用abortboot_normal函数,在bootdelay时间内如果没有用户干预,没有按任何键盘键,则这个函数
顺利执行完,往下执行run_command_list函数,执行环境变量bootcmd里面的一大堆命令,尝试启动并计入linux内核;
 
假若在bootdelay时间按下了任何键,abortboot函数返回1。

tatic int abortboot(int bootdelay)
{
#ifdef CONFIG_AUTOBOOT_KEYED
    return abortboot_keyed(bootdelay);
#else
    return abortboot_normal(bootdelay);
#endif
}
则继续往下执行cli_loop运行hush shell解释器:

void cli_loop(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
    parse_file_outer();
    /* This point is never reached */
    for (;;);
#elif defined(CONFIG_CMDLINE)
    cli_simple_loop();
#else
    printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n");
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
parse_file_outer进行必要的初始化之后,将调用parse_strea_outer函数;

/* most recursion does not come through here, the exeception is
 * from builtin_source() */
static int parse_stream_outer(struct in_str *inp, int flag)
{

    struct p_context ctx;
    o_string temp=NULL_O_STRING;
    int rcode;
#ifdef __U_BOOT__
    int code = 1;
#endif
    do {
        ctx.type = flag;
        initialize_context(&ctx);
        update_ifs_map();
        if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
        inp->promptmode=1;
        rcode = parse_stream(&temp, &ctx, inp,
                     flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');
#ifdef __U_BOOT__
        if (rcode == 1) flag_repeat = 0;
#endif
        if (rcode != 1 && ctx.old_flag != 0) {
            syntax();
#ifdef __U_BOOT__
            flag_repeat = 0;
#endif
        }
        if (rcode != 1 && ctx.old_flag == 0) {
            done_word(&temp, &ctx);
            done_pipe(&ctx,PIPE_SEQ);
#ifndef __U_BOOT__
            run_list(ctx.list_head);
#else
            code = run_list(ctx.list_head);
            if (code == -2) {    /* exit */
                b_free(&temp);
                code = 0;
                /* XXX hackish way to not allow exit from main loop */
                if (inp->peek == file_peek) {
                    printf("exit not allowed from main input shell.\n");
                    continue;
                }
                break;
            }
            if (code == -1)
                flag_repeat = 0;
#endif
        } else {
            if (ctx.old_flag != 0) {
                free(ctx.stack);
                b_reset(&temp);
            }
#ifdef __U_BOOT__
            if (inp->__promptme == 0) printf("<INTERRUPT>\n");
            inp->__promptme = 1;
#endif
            temp.nonnull = 0;
            temp.quote = 0;
            inp->p = NULL;
            free_pipe_list(ctx.list_head,0);
        }
        b_free(&temp);
    /* loop on syntax errors, return on EOF */
    } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&
        (inp->peek != static_peek || b_peek(inp)));
#ifndef __U_BOOT__
    return 0;
#else
    return (code != 0) ? 1 : 0;
#endif /* __U_BOOT__ */
}
上面的do-while循环执行并解释uboot的命令,也就是我们看到的命令行模式

posted on 2019-09-11 23:26  车小车  阅读(1112)  评论(0编辑  收藏  举报