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; \ })
这里使用到了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)
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 命令执行过程解析与添加自定义命令。
参考文章