程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

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)      /* >=2nd pass */
#define FLAG_CONT_ON_NEWLINE (1 << 3)      /* continue when we see \n */

parse_stream_outer函数就是 hush shell的命令解释器,使用do-while循环接收命令行输入,然后利用函数parse_stream函数解析,调用run_list函数在经过一系列函数调用cmd_process函数来处理命令。

在将命令的处理之前,先看几个重要的数据结构:

/* This holds pointers to the various results of parsing */
struct p_context {
    struct child_prog *child;
    struct pipe *list_head;
    struct pipe *pipe;
    reserved_style w;
    int old_flag;                /* for figuring out valid reserved words */
    struct p_context *stack;
    int type;            /* define type of parser : ";$" common or special symbol */
    /* How about quoting status? */
};

该数据结构变量是一个命令索引,关键的就是其中的两个成员,struct pipe *list_head和struct pipe *pipe;

再来看数据结构struct  pipe,这个结构其实就是一个链表节点的结构:

struct pipe {
    int num_progs;                /* total number of programs in job */
    struct child_prog *progs;    /* array of commands in pipe */
    struct pipe *next;            /* to track background commands */
    pipe_style followup;        /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
    reserved_style r_mode;        /* supports if, for, while, until */
};

该数据结构用来存储用户的输入命令的,命令存储在成员变量struct child_prog* progs里面,其中的next用来指向下一个struct  pipe数据结构变量(当一次输入多个命令时用到);

struct child_prog {
    char **argv;                /* program name and arguments */
    /* was quoted when parsed; copy of struct o_string.nonnull field */
    int *argv_nonnull;
    int    argc;                            /* number of program arguments */
    struct pipe *group;            /* if non-NULL, first in group or subshell */
    int sp;                /* number of SPECIAL_VAR_SYMBOL */
    int type;
};

该数据结构中:

  • char ** argv就是存放命令的地方,比如一个命令由若干个单词组成,cd /home,则argv[0]=cd、argv[1]=/home;
  • argc表示该命令含有的参数的个数;

总的来说,他们的关系是:p_context的head_list成员变量指向第一个pipe结构变量(用来存储第一条命令),第一个pipe结构的next指向下一个pipe结构(用来存储下一条命令),以此类推,形成一个由命令组成的链表;

3.3 parse_stream解析命令行

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

/* return code is 0 for normal exit, 1 for syntax error */
static int parse_stream(o_string *dest, struct p_context *ctx,
            struct in_str *input, int end_trigger)
{
    unsigned int ch, m;
    int next;

    /* Only double-quote state is handled in the state variable dest->quote.
     * A single-quote triggers a bypass of the main loop until its mate is
     * found.  When recursing, quote state is passed in via dest->quote. */

    debug_printf("parse_stream, end_trigger=%d\n",end_trigger);
    while ((ch=b_getch(input))!=EOF) {  //EOF => ctrl + C
        m = map[ch];
        if (input->__promptme == 0) return 1;
        next = (ch == '\n') ? 0 : b_peek(input);

        debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n",
            ch >= ' ' ? ch : '.', ch, m,
            dest->quote, ctx->stack == NULL ? '*' : '.');

        if (m==0 || ((m==1 || m==2) && dest->quote)) {
            b_addqchr(dest, ch, dest->quote);
        } else {
            if (m==2) {  /* unquoted IFS */
                if (done_word(dest, ctx)) {
                    return 1;
                }
                /* If we aren't performing a substitution, treat a newline as a
                 * command separator.  */
                if (end_trigger != '\0' && ch=='\n')    // 最后执行到这里
                    done_pipe(ctx,PIPE_SEQ);
            }
            if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) {  //执行完毕,返回
                debug_printf("leaving parse_stream (triggered)\n");
                return 0;
            }
            if (m!=2) switch (ch) {
        case '#':
            if (dest->length == 0 && !dest->quote) {
                while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); }
            } else {
                b_addqchr(dest, ch, dest->quote);
            }
            break;
        case '\\':
            if (next == EOF) {
                syntax();
                return 1;
            }
            b_addqchr(dest, '\\', dest->quote);
            b_addqchr(dest, b_getch(input), dest->quote);
            break;
        case '$':
            if (handle_dollar(dest, ctx, input)!=0) return 1;
            break;
        case '\'':
            dest->nonnull = 1;
            while(ch=b_getch(input),ch!=EOF && ch!='\'') {
                if(input->__promptme == 0) return 1;
                b_addchr(dest,ch);
            }
            if (ch==EOF) {
                syntax();
                return 1;
            }
            break;
        case '"':
            dest->nonnull = 1;
            dest->quote = !dest->quote;
            break;
        case ';':
            done_word(dest, ctx);
            done_pipe(ctx,PIPE_SEQ);
            break;
        case '&':
            done_word(dest, ctx);
            if (next=='&') {
                b_getch(input);
                done_pipe(ctx,PIPE_AND);
            } else {
                syntax_err();
                return 1;
            }
            break;
        case '|':
            done_word(dest, ctx);
            if (next=='|') {
                b_getch(input);
                done_pipe(ctx,PIPE_OR);
            } else {
                /* we could pick up a file descriptor choice here
                 * with redirect_opt_num(), but bash doesn't do it.
                 * "echo foo 2| cat" yields "foo 2". */
                syntax_err();
                return 1;
            }
            break;
        case SUBSTED_VAR_SYMBOL:
            dest->nonnull = 1;
            while (ch = b_getch(input), ch != EOF &&
                ch != SUBSTED_VAR_SYMBOL) {
                debug_printf("subst, pass=%d\n", ch);
                if (input->__promptme == 0)
                    return 1;
                b_addchr(dest, ch);
            }
            debug_printf("subst, term=%d\n", ch);
            if (ch == EOF) {
                syntax();
                return 1;
            }
            break;
        default:
            syntax();   /* this is really an internal logic error */
            return 1;
            }
        }
    }
    /* complain if quote?  No, maybe we just finished a command substitution
     * that was quoted.  Example:
     * $ echo "`cat foo` plus more"
     * and we just got the EOF generated by the subshell that ran "cat foo"
     * The only real complaint is if we got an EOF when end_trigger != '\0',
     * that is, we were really supposed to get end_trigger, and never got
     * one before the EOF.  Can't use the standard "syntax error" return code,
     * so that parse_stream_outer can distinguish the EOF and exit smoothly. */
    debug_printf("leaving parse_stream (EOF)\n");
    if (end_trigger != '\0') return -1;
    return 0;
}

这里粗略介绍一下这段代码的流程:

  • b_getch#get_user_input:获取用户输入的命令行,存储在input->p缓冲区中(需要注意的是用户键盘按下Enter表示一个命令输入完毕,按下Enter=回车符\r+换行符\n,发送的是0x0DH、0x0AH两个字符);
  • 解析命令行中的每一个字符,处理特殊字符,比如${}、<、>、``等;
  • 将解析到的命令保存到ctx中,一个命令行可能解析到若干个命令;

举个例子:

1、当用户输入的命令为cmd1时

  • p_context.head_list->progs->argc= 1
  • p_context.head_list->progs->argv[0]=cmd

2、 当用户输入的命令为cmd1 option时

  • p_context.head_list->progs->argc= 2
  • p_context.head_list->progs->argv[0]=cmd1
  • p_context.head_list->progs->argv[1]=option

3、  当用户输入的命令为cmd1 option1;  cmd2 option2时(;表示前后输入的是两条命令)

  • p_context.head_list->progs->argc= 2
  • p_context.head_list->progs->argv[0]=cmd1
  • p_context.head_list->progs->argv[1]=option1
  • p_context.head_list->next->progs->argc= 2
  • p_context.head_list->next->progs->argv[0]=cmd2
  • p_context.head_list->next->progs->argv[1]=option2

b_getch定义:

#define b_getch(input) ((input)->get(input))

执行input->get方法,在执行的代码中已经出初始化了input->get = file_get,所以这里相当于执行了file_get(input):

/* This is the magic location that prints prompts
 * and gets data back from the user */
static int file_get(struct in_str *i)
{
    int ch;

    ch = 0;
    /* If there is data waiting, eat it up */
    if (i->p && *i->p) {     
        ch = *i->p++;
    } else {
        /* need to double check i->file because we might be doing something
         * more complicated by now, like sourcing or substituting. */
        while(! i->p  || strlen(i->p)==0 ) {
            get_user_input(i);
        }
        i->promptmode=2;
        if (i->p && *i->p) {
            ch = *i->p++;
        }
        debug_printf("b_getch: got a %d\n", ch);
    }
    return ch;
}

其中input->p为const char *类型,这里利用get_user_input读取用户输入的一行数据写入console_buffer缓冲区中,这里如果读取到一行数据的呢,当读取到\r\n时,将会在console_buffer末尾写入\n\0,再把console_buffer复制到the_command。然后设置input->p=the_command。

如果始终没有用户输入the_command[0] = '\0',将会一直等待用户输入。当读取到数据后,file_get将会返回按字符逐个返回读取的字符串。

3.4 run_list

当用户的命令按照以上的方式存储之后,就进入到run_list函数,函数位于common/cli_hush.c:

/* Select which version we will use */
static int run_list(struct pipe *pi)
{
    int rcode=0;
     rcode = run_list_real(pi);
    /* free_pipe_list has the side effect of clearing memory
     * In the long run that function can be merged with run_list_real,
     * but doing that now would hobble the debugging effort. */
    free_pipe_list(pi,0);
    return rcode;
}

p_context的head_list成员变量指向第一个pipe结构变量(用来存储第一条命令),第一个pipe结构的next指向下一个pipe结构(用来存储下一条命令),...,从而形成一个链表结构;

run_list(ctx.list_head)就是处理命令的入口函数,其中ctx是p_context结构变量,里面存储了用户所输入的命令,真正将处理落到实处的函数是run_list_real#run_pipe_real。

四、命令执行

4.1 run_list_real

run_list_real定义在common/cli_hush.c文件中,这个函数代码比较多:

static int run_list_real(struct pipe *pi)
{
     ...
     for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {  //遍历每一个命令
         ...
         rcode = run_pipe_real(pi);
         ...
     }
  ...

}

由命令行解析可能得到若干条命令,保存在ctx->list_head链表中,这里遍历链表,执行每一条命令。

4.2 run_pipe_real

run_pipe_real函数位于common/cli_hush.c:

/* run_pipe_real() starts all the jobs, but doesn't wait for anything
 * to finish.  See checkjobs().
 *
 * return code is normally -1, when the caller has to wait for children
 * to finish to determine the exit status of the pipe.  If the pipe
 * is a simple builtin command, however, the action is done by the
 * time run_pipe_real returns, and the exit code is provided as the
 * return value.
 *
 * The input of the pipe is always stdin, the output is always
 * stdout.  The outpipe[] mechanism in BusyBox-0.48 lash is bogus,
 * because it tries to avoid running the command substitution in
 * subshell, when that is in fact necessary.  The subshell process
 * now has its stdout directed to the input of the appropriate pipe,
 * so this routine is noticeably simpler.
 */
static int run_pipe_real(struct pipe *pi)
{
    int i;
    int nextin;
    int flag = do_repeat ? CMD_FLAG_REPEAT : 0;
    struct child_prog *child;
    char *p;

    nextin = 0;

    /* Check if this is a simple builtin (not part of a pipe).
     * Builtins within pipes have to fork anyway, and are handled in
     * pseudo_exec.  "echo foo | read bar" doesn't work on bash, either.
     */
    if (pi->num_progs == 1) child = & (pi->progs[0]);   //pi->progs存放实际命令
    if (pi->num_progs == 1 && child->group) {
        int rcode;
        debug_printf("non-subshell grouping\n");
        rcode = run_list_real(child->group);
        return rcode;
    } else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) {
        for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
        if (i!=0 && child->argv[i]==NULL) {
            /* assignments, but no command: set the local environment */
            for (i=0; child->argv[i]!=NULL; i++) {

                /* Ok, this case is tricky.  We have to decide if this is a
                 * local variable, or an already exported variable.  If it is
                 * already exported, we have to export the new value.  If it is
                 * not exported, we need only set this as a local variable.
                 * This junk is all to decide whether or not to export this
                 * variable. */
                int export_me=0;
                char *name, *value;
                name = xstrdup(child->argv[i]);
                debug_printf("Local environment set: %s\n", name);
                value = strchr(name, '=');
                if (value)
                    *value=0;

                free(name);
                p = insert_var_value(child->argv[i]);
                set_local_var(p, export_me);
                if (p != child->argv[i]) free(p);
            }
            return EXIT_SUCCESS;   /* don't worry about errors in set_local_var() yet */
        }
        for (i = 0; is_assignment(child->argv[i]); i++) {
            p = insert_var_value(child->argv[i]);
            set_local_var(p, 0);
            if (p != child->argv[i]) {
                child->sp--;
                free(p);
            }
        }
        if (child->sp) {
            char * str = NULL;

            str = make_string(child->argv + i,
                      child->argv_nonnull + i);
            parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING);
            free(str);
            return last_return_code;
        }
        /* check ";", because ,example , argv consist from
         * "help;flinfo" must not execute
         */
        if (strchr(child->argv[i], ';')) {
            printf("Unknown command '%s' - try 'help' or use "
                    "'run' command\n", child->argv[i]);
            return -1;
        }
        /* Process the command */
        return cmd_process(flag, child->argc, child->argv,
                   &flag_repeat, NULL);
    }
    return -1;
}

命令的执行实际在cmd_process函数中。

4.3 cmd_process

cmd_process函数位于common/command.c:

enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
                   int *repeatable, ulong *ticks)
{
    enum command_ret_t rc = CMD_RET_SUCCESS;
    cmd_tbl_t *cmdtp;

    /* Look up command in command table */
    cmdtp = find_cmd(argv[0]);
    if (cmdtp == NULL) {
        printf("Unknown command '%s' - try 'help'\n", argv[0]);
        return 1;
    }

    /* found - check max args */
    if (argc > cmdtp->maxargs)
        rc = CMD_RET_USAGE;
    /* avoid "bootd" recursion */
    else if (cmdtp->cmd == do_bootd) {
        if (flag & CMD_FLAG_BOOTD) {
            puts("'bootd' recursion detected\n");
            rc = CMD_RET_FAILURE;
        } else {
            flag |= CMD_FLAG_BOOTD;
        }
    }

    /* If OK so far, then do the command */
    if (!rc) {
        if (ticks)
            *ticks = get_timer(0);
        rc = cmd_call(cmdtp, flag, argc, argv);
        if (ticks)
            *ticks = get_timer(*ticks);
        *repeatable &= cmdtp->repeatable;
    }
    if (rc == CMD_RET_USAGE)
        rc = cmd_usage(cmdtp);
    return rc;
}

uboot中的每个命令都存放在.uboot_boot_list段中,每个命令都有一个名为do_xxx(xxx为具体的命令名)的函数,这个do_xxx函数就是具体的命令处理函数。

cmd_process通过调用find_cmd函数在命令表中找到指定的命令。

变量ticks用来记录命令的执行时间,repeatable为命令是否自动重复执行标志。这两个变量都将返回到上层的调用函数。函数cmd_call利用传入的参数,直接调用cmdtp->cmd,即:

(cmdtp->cmd)(cmdtp, flag, argc, argv);

最后,如果命令执行的返回值为CMD_RET_USAGE,代表命令执行出错,且置标CMD_RET_USAGE ,那么将调用cmd_usage,输出简短的命令使用帮助信息。

4.4 find_cmd

find_cmd函数位于common/command.c:

cmd_tbl_t *find_cmd(const char *cmd)
{
    cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);  // 需要注意的是这里会进行宏替换
    const int len = ll_entry_count(cmd_tbl_t, cmd);
    return find_cmd_tbl(cmd, start, len);
}

find_cmd函数的参数child->argv[i]通常情况下是child->argv[0],即认为整个命令的第一部分(第一个空格之前的字符)作为命令名称,其他的作为参数。它的作用就是到.u_boot_cmd段里面寻找child->argv[0],如果没找到,就返回NULL,并提示无效的命令;如果找到了,就将该命令以cmd_tbl_t结构变量的形式返回,这段代码具体流程:

  • 在find_cmd函数中通过函数ll_entry_start得到数组的第一个元素,也就是命令表起始地址,类型为cmd_tbl_t(命令表是cmd_tlb_t结构体数组);
  • 通过ll_entry_count得到数组长度,也就是命令表的长度;
  • 最后通过find_cmd_tlb,从 start到 start+len的array进行遍历,在命令表中根据命令名找到所需的命令

其中ll_entry_start和ll_entry_count定义在include/linker_lists.h文件中:、

/**
 * ll_entry_start() - Point to first entry of linker-generated array
 * @_type:    Data type of the entry
 * @_list:    Name of the list in which this entry is placed
 *
 * This function returns (_type *) pointer to the very first entry of a
 * linker-generated array placed into subsection of .u_boot_list section
 * specified by _list argument.
 *
 * Since this macro defines an array start symbol, its leftmost index
 * must be 2 and its rightmost index must be 1.
 *
 * Example:
 * struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub);
 */
#define ll_entry_start(_type, _list)                    \
({                                    \
    static char start[0] __aligned(4) __attribute__((unused,    \
        section(".u_boot_list_2_"#_list"_1")));            \
    (_type *)&start;                        \
})

/**
 * ll_entry_end() - Point after last entry of linker-generated array
 * @_type:    Data type of the entry
 * @_list:    Name of the list in which this entry is placed
 *        (with underscores instead of dots)
 *
 * This function returns (_type *) pointer after the very last entry of
 * a linker-generated array placed into subsection of .u_boot_list
 * section specified by _list argument.
 *
 * Since this macro defines an array end symbol, its leftmost index
 * must be 2 and its rightmost index must be 3.
 *
 * Example:
 * struct my_sub_cmd *msc = ll_entry_end(struct my_sub_cmd, cmd_sub);
 */
#define ll_entry_end(_type, _list)                    \
({                                    \
    static char end[0] __aligned(4) __attribute__((unused,        \
        section(".u_boot_list_2_"#_list"_3")));            \
    (_type *)&end;                            \
})
/**
 * ll_entry_count() - Return the number of elements in linker-generated array
 * @_type:    Data type of the entry
 * @_list:    Name of the list of which the number of elements is computed
 *
 * This function returns the number of elements of a linker-generated array
 * placed into subsection of .u_boot_list section specified by _list
 * argument. The result is of an unsigned int type.
 *
 * Example:
 * int i;
 * const unsigned int count = ll_entry_count(struct my_sub_cmd, cmd_sub);
 * struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub);
 * for (i = 0; i < count; i++, msc++)
 *         printf("Entry %i, x=%i y=%i\n", i, msc->x, msc->y);
 */
#define ll_entry_count(_type, _list)                    \
    ({                                \
        _type *start = ll_entry_start(_type, _list);        \
        _type *end = ll_entry_end(_type, _list);        \
        unsigned int _ll_result = end - start;            \
        _ll_result;                        \
    })
View Code

这里使用到了gcc __attribute__((section("section_name"))) ,gcc的__attribute__编译属性有很多子项,用于改变作用对象的特性。这里简单介绍一下section子项的作用。

__attribute__的section子项使用方式为:

__attribute__((section("section_name")))

其作用是将作用的函数或数据放入指定名为"section_name"的段。

我们再来看ll_entrt_start中的如下代码:

static char start[0] __aligned(4) __attribute__((unused, section(".u_boot_list_2_"#_list"_1"))); 

定义一个包含0个字节的数组start[0],且把它放在.u_boot_list_2_cmd_1段中,该段属性为unsued。这里简单介绍一下为什么会替换成u_boot_list_2_cmd_1:

#include <stdio.h>
#define ll_entry_start(_list) "u_boot_list_2_"#_list"_1"  //#_list会将_list替换成实际值,并加上""

int main(void) { 
    printf(ll_entry_start(cmd));
    return 0;
}

回到u-boot,注意在u-boot.lds中定义有:

 .u_boot_list : {
  KEEP(*(SORT(.u_boot_list*)));
 }

u_boot_list中所有符号是按字符表的先后顺序排列的,u_boot_list_2_cmd_1会放在所有使用U_BOOT_CMD和U_BOOT_CMD_COMPLETE定义的符号段的最前面,即.u_boot_list_2_cmd_1为会在u_boot_list_2_cmd_2开头的符号段之前

段u_boot_list_2_cmd_1它定义为0个字节的数组start[0],编译器并不为它分配存储空间,那么start将指向段.u_boot_list_2_cmd_2的起始地址。u_boot_list为命令cmd生成的段内存分布大致如下:

.u_boot_list_2_cmd_1  长度为0
.u_boot_list_2_cmd_2_xx  由U_BOOT_CMD和U_BOOT_CMD_COMPLETE定义的命令所在的符号段 
.u_boot_list_2_cmd_3  长度为0

同理ll_entry_end用end[0]来标识.u_boot_list_2_cmd_3段的结尾,接下来的函数ll_entry_count返回的就是start - end的值,长度单位cmd_tbl_t,即符号段.u_boot_list_2_cmd_2的总字节长度。

然后调用find_cmd_tbl,根据传入的.u_boot_list_2_cmd_2段的首地址和函数ll_entry_count 返回的长度,根据命令名查找相应的符号段,即相命令对应的cmd_tbl_t结构体变量,然后返回该结构体指针。

五、命令实现介绍

这里我们介绍一下uboot是如何保存各种命令的,以及命令的实现方式。

5.1 cmd_tbl_t命令相关信息结构体

typedef struct cmd_tbl_s    cmd_tbl_t;

我们首先从cmd_tbl_s结构体说起,cmd_tbl_s用来保存每一个命令相关信息。

/*
 * Monitor Command Table
 */
struct cmd_tbl_s {
    char        *name;        /* 命令名            */
    int        maxargs;    /* 命令最大参数个数    */
    int        repeatable;    /* 自动重复?        */
    /* cmd为函数指针    */
    int        (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
   /* 用法字符串 */
    char        *usage;        /* Usage message    (short)    */
  /* 帮助信息 */
#ifdef   CONFIG_SYS_LONGHELP
    char        *help;        /* Help  message    (long)    */
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments */
    int        (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

该结构体包含了命令名,命令实现函数,命令使用简短说明usage的输出字符串,帮助回调函数,参数变量自动完成函数等。u-boot使用该结构体来描述一个完整的命令。

5.2 u-boot命令定义

u-boot中使用宏U_BOOT_CMD来定义命令,该宏在include/command.h中定义如下:

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)        \
    U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

U_BOOT_CMD是宏U_BOOT_CMD_COMPLETE最后一个参数_comp为NULL的特例,_comp表示变量是否自动完成:

#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
    ll_entry_declare(cmd_tbl_t, _name, cmd) =            \  
        U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,    \
                        _usage, _help, _comp);

其中包含的宏U_BOOT_CMD_MKENT_COMPLETE定义为:

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,        \
                _usage, _help, _comp)            \
        { #_name, _maxargs, _rep, _cmd, _usage,            \
            _CMD_HELP(_help) _CMD_COMPLETE(_comp) }

上面的_CMD_HELP根据配置可选为使用完整或简短帮助说明。_CMD_COMPLETE则根据配置决定是否使用自动完成函数。U_BOOT_CMD_MKENT_COMPLETE宏其实是组织输入参数,对ll_entry_declare进行数据填充。ll_entry_declare在文件include/linker_lists.h中定义:

/**
 * ll_entry_declare() - Declare linker-generated array entry
 * @_type:    Data type of the entry
 * @_name:    Name of the entry
 * @_list:    name of the list. Should contain only characters allowed
 *        in a C variable name!
 *
 * This macro declares a variable that is placed into a linker-generated
 * array. This is a basic building block for more advanced use of linker-
 * generated arrays. The user is expected to build their own macro wrapper
 * around this one.
 *
 * A variable declared using this macro must be compile-time initialized.
 *
 * Special precaution must be made when using this macro:
 *
 * 1) The _type must not contain the "static" keyword, otherwise the
 *    entry is generated and can be iterated but is listed in the map
 *    file and cannot be retrieved by name.
 *
 * 2) In case a section is declared that contains some array elements AND
 *    a subsection of this section is declared and contains some elements,
 *    it is imperative that the elements are of the same type.
 *
 * 4) In case an outer section is declared that contains some array elements
 *    AND an inner subsection of this section is declared and contains some
 *    elements, then when traversing the outer section, even the elements of
 *    the inner sections are present in the array.
 *
 * Example:
 * ll_entry_declare(struct my_sub_cmd, my_sub_cmd, cmd_sub) = {
 *         .x = 3,
 *         .y = 4,
 * };
 */
#define ll_entry_declare(_type, _name, _list)                \
    _type _u_boot_list_2_##_list##_2_##_name __aligned(4)        \
            __attribute__((unused,                \
            section(".u_boot_list_2_"#_list"_2_"#_name)))   //#_list会将_list替换成实际值,并加上""

参数_type为cmd_tbl_t,这里定义一个cmd_tbl_t结构体,并把它放在符号段_u_boot_list_2_"#_list"_2_"#_name中,其中_list和_name根据宏参数进行字符串替换。

在cmd目录下定义了各种命令,每个文件都对应一个或者若干个命令。

比如:在cmd/bootm.c中有U_BOOT_CMD的实参:

U_BOOT_CMD(
    bootm,    CONFIG_SYS_MAXARGS,    1,    do_bootm,
    "boot application image from memory", bootm_help_text
);

我们编译完u-boot,查看u-boot.map可以看到段u_boot_list的内存分布:

.u_boot_list    0x000771e8      0x88c
 *(SORT(.u_boot_list*))
 .u_boot_list_2_cmd_1
                0x000771e8        0x0 cmd/built-in.o
 .u_boot_list_2_cmd_1
                0x000771e8        0x0 common/built-in.o
 .u_boot_list_2_cmd_2_base
                0x000771e8       0x18 cmd/built-in.o
                0x000771e8                _u_boot_list_2_cmd_2_base
 .u_boot_list_2_cmd_2_bdinfo
                0x00077200       0x18 cmd/built-in.o
                0x00077200                _u_boot_list_2_cmd_2_bdinfo
 .u_boot_list_2_cmd_2_boot
                0x00077218       0x18 cmd/built-in.o
                0x00077218                _u_boot_list_2_cmd_2_boot
 .u_boot_list_2_cmd_2_bootd
                0x00077230       0x18 cmd/built-in.o
                0x00077230                _u_boot_list_2_cmd_2_bootd
 .u_boot_list_2_cmd_2_bootelf
                0x00077248       0x18 cmd/built-in.o
                0x00077248                _u_boot_list_2_cmd_2_bootelf
 .u_boot_list_2_cmd_2_bootm
                0x00077260       0x18 cmd/built-in.o
                0x00077260                _u_boot_list_2_cmd_2_bootm
 .u_boot_list_2_cmd_2_bootp
                0x00077278       0x18 cmd/built-in.o
                0x00077278                _u_boot_list_2_cmd_2_bootp
 .u_boot_list_2_cmd_2_bootvx
                0x00077290       0x18 cmd/built-in.o
                0x00077290                _u_boot_list_2_cmd_2_bootvx
 .u_boot_list_2_cmd_2_chpart
                0x000772a8       0x18 cmd/built-in.o
                0x000772a8                _u_boot_list_2_cmd_2_chpart
 .u_boot_list_2_cmd_2_cmp
                0x000772c0       0x18 cmd/built-in.o
                0x000772c0                _u_boot_list_2_cmd_2_cmp
 .u_boot_list_2_cmd_2_coninfo
                0x000772d8       0x18 cmd/built-in.o
                0x000772d8                _u_boot_list_2_cmd_2_coninfo
 .u_boot_list_2_cmd_2_cp
                0x000772f0       0x18 cmd/built-in.o
                0x000772f0                _u_boot_list_2_cmd_2_cp
 .u_boot_list_2_cmd_2_crc32
                0x00077308       0x18 cmd/built-in.o
                0x00077308                _u_boot_list_2_cmd_2_crc32
 .u_boot_list_2_cmd_2_date
                0x00077320       0x18 cmd/built-in.o
                0x00077320                _u_boot_list_2_cmd_2_date
 .u_boot_list_2_cmd_2_dcache
                0x00077338       0x18 cmd/built-in.o
                0x00077338                _u_boot_list_2_cmd_2_dcache
 .u_boot_list_2_cmd_2_dhcp
                0x00077350       0x18 cmd/built-in.o
                0x00077350                _u_boot_list_2_cmd_2_dhcp
 .u_boot_list_2_cmd_2_echo
                0x00077368       0x18 cmd/built-in.o
                0x00077368                _u_boot_list_2_cmd_2_echo
 .u_boot_list_2_cmd_2_editenv
                0x00077380       0x18 cmd/built-in.o
                0x00077380                _u_boot_list_2_cmd_2_editenv
 .u_boot_list_2_cmd_2_env
                0x00077398       0x18 cmd/built-in.o
                0x00077398                _u_boot_list_2_cmd_2_env
 .u_boot_list_2_cmd_2_erase
                0x000773b0       0x18 cmd/built-in.o
                0x000773b0                _u_boot_list_2_cmd_2_erase
 .u_boot_list_2_cmd_2_exit
                0x000773c8       0x18 cmd/built-in.o
                0x000773c8                _u_boot_list_2_cmd_2_exit
 .u_boot_list_2_cmd_2_ext2load
                0x000773e0       0x18 cmd/built-in.o
                0x000773e0                _u_boot_list_2_cmd_2_ext2load
 .u_boot_list_2_cmd_2_ext2ls
                0x000773f8       0x18 cmd/built-in.o
                0x000773f8                _u_boot_list_2_cmd_2_ext2ls
 .u_boot_list_2_cmd_2_false
                0x00077410       0x18 cmd/built-in.o
                0x00077410                _u_boot_list_2_cmd_2_false
 .u_boot_list_2_cmd_2_fatinfo
                0x00077428       0x18 cmd/built-in.o
                0x00077428                _u_boot_list_2_cmd_2_fatinfo
 .u_boot_list_2_cmd_2_fatload
                0x00077440       0x18 cmd/built-in.o
                0x00077440                _u_boot_list_2_cmd_2_fatload
 .u_boot_list_2_cmd_2_fatls
                0x00077458       0x18 cmd/built-in.o
                0x00077458                _u_boot_list_2_cmd_2_fatls
 .u_boot_list_2_cmd_2_fatsize
                0x00077470       0x18 cmd/built-in.o
                0x00077470                _u_boot_list_2_cmd_2_fatsize
 .u_boot_list_2_cmd_2_flinfo
                0x00077488       0x18 cmd/built-in.o
                0x00077488                _u_boot_list_2_cmd_2_flinfo
 .u_boot_list_2_cmd_2_go
                0x000774a0       0x18 cmd/built-in.o
                0x000774a0                _u_boot_list_2_cmd_2_go
 .u_boot_list_2_cmd_2_help
                0x000774b8       0x18 cmd/built-in.o
                0x000774b8                _u_boot_list_2_cmd_2_help
 .u_boot_list_2_cmd_2_icache
                0x000774d0       0x18 cmd/built-in.o
                0x000774d0                _u_boot_list_2_cmd_2_icache
 .u_boot_list_2_cmd_2_iminfo
                0x000774e8       0x18 cmd/built-in.o
                0x000774e8                _u_boot_list_2_cmd_2_iminfo
 .u_boot_list_2_cmd_2_imls
                0x00077500       0x18 cmd/built-in.o
                0x00077500                _u_boot_list_2_cmd_2_imls
 .u_boot_list_2_cmd_2_imxtract
                0x00077518       0x18 cmd/built-in.o
                0x00077518                _u_boot_list_2_cmd_2_imxtract
 .u_boot_list_2_cmd_2_itest
                0x00077530       0x18 cmd/built-in.o
                0x00077530                _u_boot_list_2_cmd_2_itest
 .u_boot_list_2_cmd_2_loadb
                0x00077548       0x18 cmd/built-in.o
                0x00077548                _u_boot_list_2_cmd_2_loadb
 .u_boot_list_2_cmd_2_loads
                0x00077560       0x18 cmd/built-in.o
                0x00077560                _u_boot_list_2_cmd_2_loads
 .u_boot_list_2_cmd_2_loadx
                0x00077578       0x18 cmd/built-in.o
                0x00077578                _u_boot_list_2_cmd_2_loadx
 .u_boot_list_2_cmd_2_loady
                0x00077590       0x18 cmd/built-in.o
                0x00077590                _u_boot_list_2_cmd_2_loady
 .u_boot_list_2_cmd_2_loop
                0x000775a8       0x18 cmd/built-in.o
                0x000775a8                _u_boot_list_2_cmd_2_loop
 .u_boot_list_2_cmd_2_md
                0x000775c0       0x18 cmd/built-in.o
                0x000775c0                _u_boot_list_2_cmd_2_md
 .u_boot_list_2_cmd_2_mm
                0x000775d8       0x18 cmd/built-in.o
                0x000775d8                _u_boot_list_2_cmd_2_mm
 .u_boot_list_2_cmd_2_mtdparts
                0x000775f0       0x18 cmd/built-in.o
                0x000775f0                _u_boot_list_2_cmd_2_mtdparts
 .u_boot_list_2_cmd_2_mw
                0x00077608       0x18 cmd/built-in.o
                0x00077608                _u_boot_list_2_cmd_2_mw
 .u_boot_list_2_cmd_2_nand
                0x00077620       0x18 cmd/built-in.o
                0x00077620                _u_boot_list_2_cmd_2_nand
 .u_boot_list_2_cmd_2_nboot
                0x00077638       0x18 cmd/built-in.o
                0x00077638                _u_boot_list_2_cmd_2_nboot
 .u_boot_list_2_cmd_2_nfs
                0x00077650       0x18 cmd/built-in.o
                0x00077650                _u_boot_list_2_cmd_2_nfs
 .u_boot_list_2_cmd_2_nm
                0x00077668       0x18 cmd/built-in.o
                0x00077668                _u_boot_list_2_cmd_2_nm
 .u_boot_list_2_cmd_2_ping
                0x00077680       0x18 cmd/built-in.o
                0x00077680                _u_boot_list_2_cmd_2_ping
 .u_boot_list_2_cmd_2_printenv
                0x00077698       0x18 cmd/built-in.o
                0x00077698                _u_boot_list_2_cmd_2_printenv
 .u_boot_list_2_cmd_2_protect
                0x000776b0       0x18 cmd/built-in.o
                0x000776b0                _u_boot_list_2_cmd_2_protect
 .u_boot_list_2_cmd_2_question_mark
                0x000776c8       0x18 cmd/built-in.o
                0x000776c8                _u_boot_list_2_cmd_2_question_mark
 .u_boot_list_2_cmd_2_reginfo
                0x000776e0       0x18 cmd/built-in.o
                0x000776e0                _u_boot_list_2_cmd_2_reginfo
 .u_boot_list_2_cmd_2_reset
                0x000776f8       0x18 cmd/built-in.o
                0x000776f8                _u_boot_list_2_cmd_2_reset
 .u_boot_list_2_cmd_2_run
                0x00077710       0x18 cmd/built-in.o
                0x00077710                _u_boot_list_2_cmd_2_run
 .u_boot_list_2_cmd_2_saveenv
                0x00077728       0x18 cmd/built-in.o
                0x00077728                _u_boot_list_2_cmd_2_saveenv
 .u_boot_list_2_cmd_2_setenv
                0x00077740       0x18 cmd/built-in.o
                0x00077740                _u_boot_list_2_cmd_2_setenv
 .u_boot_list_2_cmd_2_showvar
                0x00077758       0x18 common/built-in.o
                0x00077758                _u_boot_list_2_cmd_2_showvar
 .u_boot_list_2_cmd_2_sleep
                0x00077770       0x18 cmd/built-in.o
                0x00077770                _u_boot_list_2_cmd_2_sleep
 .u_boot_list_2_cmd_2_source
                0x00077788       0x18 cmd/built-in.o
                0x00077788                _u_boot_list_2_cmd_2_source
 .u_boot_list_2_cmd_2_test
                0x000777a0       0x18 cmd/built-in.o
                0x000777a0                _u_boot_list_2_cmd_2_test
 .u_boot_list_2_cmd_2_tftpboot
                0x000777b8       0x18 cmd/built-in.o
                0x000777b8                _u_boot_list_2_cmd_2_tftpboot
 .u_boot_list_2_cmd_2_true
                0x000777d0       0x18 cmd/built-in.o
                0x000777d0                _u_boot_list_2_cmd_2_true
 .u_boot_list_2_cmd_2_ubi
                0x000777e8       0x18 cmd/built-in.o
                0x000777e8                _u_boot_list_2_cmd_2_ubi
 .u_boot_list_2_cmd_2_ubifsload
                0x00077800       0x18 cmd/built-in.o
                0x00077800                _u_boot_list_2_cmd_2_ubifsload
 .u_boot_list_2_cmd_2_ubifsls
                0x00077818       0x18 cmd/built-in.o
                0x00077818                _u_boot_list_2_cmd_2_ubifsls
 .u_boot_list_2_cmd_2_ubifsmount
                0x00077830       0x18 cmd/built-in.o
                0x00077830                _u_boot_list_2_cmd_2_ubifsmount
 .u_boot_list_2_cmd_2_ubifsumount
                0x00077848       0x18 cmd/built-in.o
                0x00077848                _u_boot_list_2_cmd_2_ubifsumount
 .u_boot_list_2_cmd_2_usb
                0x00077860       0x18 cmd/built-in.o
                0x00077860                _u_boot_list_2_cmd_2_usb
 .u_boot_list_2_cmd_2_usbboot
                0x00077878       0x18 cmd/built-in.o
                0x00077878                _u_boot_list_2_cmd_2_usbboot
 .u_boot_list_2_cmd_2_version
                0x00077890       0x18 cmd/built-in.o
                0x00077890                _u_boot_list_2_cmd_2_version
 .u_boot_list_2_cmd_2_ydevconfig
                0x000778a8       0x18 cmd/built-in.o
                0x000778a8                _u_boot_list_2_cmd_2_ydevconfig
 .u_boot_list_2_cmd_2_ydevls
                0x000778c0       0x18 cmd/built-in.o
                0x000778c0                _u_boot_list_2_cmd_2_ydevls
 .u_boot_list_2_cmd_2_yls
                0x000778d8       0x18 cmd/built-in.o
                0x000778d8                _u_boot_list_2_cmd_2_yls
 .u_boot_list_2_cmd_2_ymkdir
                0x000778f0       0x18 cmd/built-in.o
                0x000778f0                _u_boot_list_2_cmd_2_ymkdir
 .u_boot_list_2_cmd_2_ymount
                0x00077908       0x18 cmd/built-in.o
                0x00077908                _u_boot_list_2_cmd_2_ymount
 .u_boot_list_2_cmd_2_ymv
                0x00077920       0x18 cmd/built-in.o
                0x00077920                _u_boot_list_2_cmd_2_ymv
 .u_boot_list_2_cmd_2_yrd
                0x00077938       0x18 cmd/built-in.o
                0x00077938                _u_boot_list_2_cmd_2_yrd
 .u_boot_list_2_cmd_2_yrdm
                0x00077950       0x18 cmd/built-in.o
                0x00077950                _u_boot_list_2_cmd_2_yrdm
 .u_boot_list_2_cmd_2_yrm
                0x00077968       0x18 cmd/built-in.o
                0x00077968                _u_boot_list_2_cmd_2_yrm
 .u_boot_list_2_cmd_2_yrmdir
                0x00077980       0x18 cmd/built-in.o
                0x00077980                _u_boot_list_2_cmd_2_yrmdir
 .u_boot_list_2_cmd_2_ytrace
                0x00077998       0x18 cmd/built-in.o
                0x00077998                _u_boot_list_2_cmd_2_ytrace
 .u_boot_list_2_cmd_2_yumount
                0x000779b0       0x18 cmd/built-in.o
                0x000779b0                _u_boot_list_2_cmd_2_yumount
 .u_boot_list_2_cmd_2_ywr
                0x000779c8       0x18 cmd/built-in.o
                0x000779c8                _u_boot_list_2_cmd_2_ywr
 .u_boot_list_2_cmd_2_ywrm
                0x000779e0       0x18 cmd/built-in.o
                0x000779e0                _u_boot_list_2_cmd_2_ywrm
 .u_boot_list_2_cmd_3
                0x000779f8        0x0 cmd/built-in.o
 .u_boot_list_2_cmd_3
                0x000779f8        0x0 common/built-in.o
 .u_boot_list_2_env_clbk_1
                0x000779f8        0x0 common/built-in.o
 .u_boot_list_2_env_clbk_2_baudrate
                0x000779f8        0x8 drivers/serial/built-in.o
                0x000779f8                _u_boot_list_2_env_clbk_2_baudrate
 .u_boot_list_2_env_clbk_2_bootfile
                0x00077a00        0x8 net/built-in.o
                0x00077a00                _u_boot_list_2_env_clbk_2_bootfile
 .u_boot_list_2_env_clbk_2_callbacks
                0x00077a08        0x8 common/built-in.o
                0x00077a08                _u_boot_list_2_env_clbk_2_callbacks
 .u_boot_list_2_env_clbk_2_console
                0x00077a10        0x8 common/built-in.o
                0x00077a10                _u_boot_list_2_env_clbk_2_console
 .u_boot_list_2_env_clbk_2_ethaddr
                0x00077a18        0x8 net/built-in.o
                0x00077a18                _u_boot_list_2_env_clbk_2_ethaddr
 .u_boot_list_2_env_clbk_2_flags
                0x00077a20        0x8 common/built-in.o
                0x00077a20                _u_boot_list_2_env_clbk_2_flags
 .u_boot_list_2_env_clbk_2_gatewayip
                0x00077a28        0x8 net/built-in.o
                0x00077a28                _u_boot_list_2_env_clbk_2_gatewayip
 .u_boot_list_2_env_clbk_2_ipaddr
                0x00077a30        0x8 net/built-in.o
                0x00077a30                _u_boot_list_2_env_clbk_2_ipaddr
 .u_boot_list_2_env_clbk_2_loadaddr
                0x00077a38        0x8 common/built-in.o
                0x00077a38                _u_boot_list_2_env_clbk_2_loadaddr
 .u_boot_list_2_env_clbk_2_netmask
                0x00077a40        0x8 net/built-in.o
                0x00077a40                _u_boot_list_2_env_clbk_2_netmask
 .u_boot_list_2_env_clbk_2_nvlan
                0x00077a48        0x8 net/built-in.o
                0x00077a48                _u_boot_list_2_env_clbk_2_nvlan
 .u_boot_list_2_env_clbk_2_serverip
                0x00077a50        0x8 net/built-in.o
                0x00077a50                _u_boot_list_2_env_clbk_2_serverip
 .u_boot_list_2_env_clbk_2_vlan
                0x00077a58        0x8 net/built-in.o
                0x00077a58                _u_boot_list_2_env_clbk_2_vlan
 .u_boot_list_2_env_clbk_3
                0x00077a60        0x0 common/built-in.o
 .u_boot_list_2_part_driver_1
                0x00077a60        0x0 disk/built-in.o
 .u_boot_list_2_part_driver_2_dos
                0x00077a60       0x14 disk/built-in.o
                0x00077a60                _u_boot_list_2_part_driver_2_dos
 .u_boot_list_2_part_driver_3
                0x00077a74        0x0 disk/built-in.o
                0x00077a74                . = ALIGN (0x4)
View Code

5.3 案例

下面,我们举例说明上述宏的实现机制。比如有如下的定义:

U_BOOT_CMD(
    env, CONFIG_SYS_MAXARGS, 1, do_env,
    "environment handling commands", env_help_text
);  

即:

U_BOOT_CMD_COMPLETE (
    env, CONFIG_SYS_MAXARGS, 1, do_env,
    "environment handling commands", env_help_text,NULL
);

带入宏参及其展开为 :

U_BOOT_CMD_COMPLETE(env, CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text , NULL ) \
    ll_entry_declare(cmd_tbl_t, env , cmd) =           \
        U_BOOT_CMD_MKENT_COMPLETE(env , CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text , NULL);

其中的ll_entry_declare带入宏参及其展开为 :

ll_entry_declare(cmd_tbl_t , env , cmd )            \
    cmd_tbl_t _u_boot_list_2_cmd_2_env __aligned(4)       \
            __attribute__((unused,              \
            section(".u_boot_list_2_cmd_2_env" )))

其中的U_BOOT_CMD_MKENT_COMPLETE带入宏参及其展开为:

U_BOOT_CMD_MKENT_COMPLETE(env , CONFIG_SYS_MAXARGS , 1, do_env , _usage, _help, _comp)     \
        { "env", CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text ,NULL}

那么上述U_BOOT_CMD_COMPLETE最终展开为:

cmd_tbl_t _u_boot_list_2_cmd_2_env __aligned(4)       \
            __attribute__((unused, section(".u_boot_list_2_cmd_2_env" ))) =
{ "env", CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text ,NULL}

U_BOOT_CMD_COMPLETE宏用来定义一个cmd_tbl_t结构体变量,初始化该结构体中的相应成员,并把该结构体变量存放在4字节对齐的.u_boot_list_2_cmd_2_env符号段中。

如前所述,宏U_BOOT_CMD将最后一个参数_comp置为NULL,对U_BOOT_CMD_COMPLETE做了进一步的封装。

所有使用U_BOOT_CMD和U_BOOT_CMD_COMPLETE定义的命令最后都集中放在以.u_boot_list_2_cmd_2开头的符号段中。即.u_boot_list_2_cmd_2_##name,这里的name是命令名。

5.4 自制u-boot命令

参考(五) u-boot 命令执行过程解析与添加自定义命令

参考文章

[1]从零开始之uboot、移植uboot2017.01(八、命令解析与实现)

[2]uboot启动流程关键函数的介绍(二)

[3]uboot系列之-----命令的处理过程(源码)

[4]函数main_loop和u-boot命令执行

posted @ 2021-11-27 15:22  大奥特曼打小怪兽  阅读(792)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步