Mini2440之uboot移植之源码分析命令解析(五)

看到这里的同学,恭喜你,马上就看完了u-boot的源码了。

一、run_main_loop

我们介绍到了init_sequence_r的最后一个函数run_main_loop,该函数位于common/board_r.c文件中。

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

如果我们配置了CONFIG_BOOTCOMMAND 那么u-boot将会启动linux内核。

否则,进入命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后解析命令,执行命令。

我们先放一张main_loop的执行流程图,可以对着后面的讲解来看:

二、main_loop

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

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

该函数做的都是与平台无关的工作,主要包括:

  • cli_init:用来初始化hush shell使用的变量top_vars ;
  • bootdelay_process和autoboot_command:u-boot预启动相关函数,读取环境变量bootdelay和bootcmd的配置值,在u-boot启动延时计数期间内如无用户按键输入干预,那么将执行bootcmd配置中的命令(如果配置宏CONFIG_BOOTCOMMAND);如果有按键按下或者倒计时结束,将会进入u-boot命令行模式;
  • cli_loop:死循环,进入u-boot命令行模式,解析命令,并执行对应函数;
2.1 cli_init

cli_init函数定义在common/cli.c:

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

#if defined(CONFIG_HUSH_INIT_VAR)
    hush_init_var();
#endif
}

CONFIG_SYS_HUSH_PARSER在CONFIG_SYS_HUSH_PARSER中定义。 u_boot_hush_start(common/cli_hush.c) 定义:

int u_boot_hush_start(void)
{
    if (top_vars == NULL) {
        top_vars = malloc(sizeof(struct variables));
        top_vars->name = "HUSH_VERSION";
        top_vars->value = "0.01";
        top_vars->next = NULL;
        top_vars->flg_export = 0;
        top_vars->flg_read_only = 1;
#ifdef CONFIG_NEEDS_MANUAL_RELOC
        u_boot_hush_reloc();
#endif
    }
    return 0;
}

其中top_vars定义:

struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 };
struct variables *top_vars = &shell_ver;

从上可知:cli_init用来初始化hush shell使用的变量top_vars 。

2.2 run_preboot_environment_command

run_preboot_environment_command函数定义在common/main.c:

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 */
}

如果定义了CONFIG_PREBOOT,该函数将会从环境变量获取preboot的定义,该变量包含了一些预启动命令,一般环境变量中不包含该项配置。

由于CONFIG_PREBOOT宏未定义,所以这里均不执行。

2.3 bootdelay_process

bootdelay_process函数定义在common/autoboot.c:

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

bootdelay_process从环境变量获取bootdelay和bootcmd配置值,将提取的bootdelay配置值转换成整数,赋值给全局变量stored_bootdelay。最后返回bootcmd的配置值。

u-boot在执行中,会输出如下调试信息:

initcall: 0000f4e4 (relocated to 33f304e4)
### main_loop entered: bootdelay=5
2.4 autoboot_command

autoboot_command函数定义在common/autoboot.c:

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 */
}

stored-bootdelay为u-boot的启动延时计数值,如果倒计时正常结束,那么将执行run_command_list,此函数会执行参数s指定的一系列命令,也就是bootcmd中配置中的命令,一般配置为linux内核启动命令,因此linux内核启动。

如果在倒计时结束前按下回车键,run_command_list就不会执行,autoboot_command相当于空函数,然后执行cli_loop函数,这个是命令行处理函数,负责接收处理输入命令。

三、cli_loop命令行解析

由于cli_loop的实现比较复杂,这里单独介绍。cli_loop定义在common/cli.c文件中:

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*/
}

这里进行了循环执行命令的代码:

  • 第一种形式是采用HUSH解析的方式;
  • 第二种形式是采用cli_simple_loop的方式;

第二种调用比较简单。主要是直接从串口读取一行命令:

len = cli_readline(CONFIG_SYS_PROMPT);

然后调用如下函数开始执行:

rc = run_command_repeatable(lastcommand, flag);->cli_simple_run_command

然而smdk2410默认采用的HUSH解析的方式。下面我们重点介绍第一种,这种方式的函数调用流程如下图:

3.1 parse_file_outer

parse_file_outer函数定义在common/cli_hush.c:

int parse_file_outer(void)
{
    int rcode;
    struct in_str input;
    setup_file_in_str(&input);
    rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
    return rcode;
}

__U_BOOT__在common/cli_hush.c文件中定义,这里省略了该宏相关的代码。其中setup_file_in_str函数初始化了input结构参数:

static void setup_file_in_str(struct in_str *i)
{
    i->peek = file_peek;
    i->get = file_get;
    i->__promptme=1;
    i->promptmode=1;
    i->p = NULL;
}
3.2 parse_stream_outer

parse_stream_outer函数定义在common/cli_hush.c:

/* 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;
    int code = 1;
    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');
        if (rcode == 1) flag_repeat = 0;
        if (rcode != 1 && ctx.old_flag != 0) {
            syntax();
            flag_repeat = 0;
        }
        if (rcode != 1 && ctx.old_flag == 0) {
            done_word(&temp, &ctx);
            done_pipe(&ctx,PIPE_SEQ);
            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;
        } else {
            if (ctx.old_flag != 0) {
                free(ctx.stack);
                b_reset(&temp);
            }
            if (inp->__promptme == 0) printf("<INTERRUPT>\n");
            inp->__promptme = 1;
            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)));
    return (code != 0) ? 1 : 0;
}

部分FLAG宏定义如下:

#define FLAG_EXIT_FROM_LOOP 1
#define FLAG_PARSE_SEMICOLON (1 << 1)      /* symbol ';' is special for parser */
#define FLAG_REPARSING       (1 << 2)      /* >&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值