看到这里的同学,恭喜你,马上就看完了u-boot的源码了。
我们介绍到了init_sequence_r的最后一个函数run_main_loop,该函数位于common/board_r.c文件中。
一、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; }
进入命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应工作。
二、main_loop(common/main.c)
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配置中的命令,默认配置未启动linux内核;如果有按键按下或者倒计时结束,将会进入uboot命令行模式;
- cli_llp:死循环,进入uboot命令行,解析命令,并执行对应函数;
2.1 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(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(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(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中配置中的命令,bootcmd中保存着默认的启动命令,因此linux内核启动。
如果在倒计时结束前按下回车键,run_command_list就不会执行,autoboot_command相当于空函数,然后执行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(common/cli_hush.c)
#ifndef __U_BOOT__ static int parse_file_outer(FILE *f) #else int parse_file_outer(void) #endif { int rcode; struct in_str input; #ifndef __U_BOOT__ setup_file_in_str(&input, f); #else setup_file_in_str(&input); #endif rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); return rcode; }
__U_BOOT__在common/cli_hush.c文件中定义。其中setup_file_in_str函数初始化了input结构参数:
#ifndef __U_BOOT__ static void setup_file_in_str(struct in_str *i, FILE *f) #else static void setup_file_in_str(struct in_str *i) #endif { i->peek = file_peek; i->get = file_get; i->__promptme=1; i->promptmode=1; #ifndef __U_BOOT__ i->file = f; #endif i->p = NULL; }
3.2 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; #ifdef __U_BOOT__ int code = 1; #endif do { ctx.type = flag; initialize_context(&ctx); update_ifs_map(); if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0); inp->promptmode=1; rcode = parse_stream(&temp, &ctx, inp, flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n'); #ifdef __U_BOOT__ if (rcode == 1) flag_repeat = 0; #endif if (rcode != 1 && ctx.old_flag != 0) { syntax(); #ifdef __U_BOOT__ flag_repeat = 0; #endif } if (rcode != 1 && ctx.old_flag == 0) { done_word(&temp, &ctx); done_pipe(&ctx,PIPE_SEQ); #ifndef __U_BOOT__ run_list(ctx.list_head); #else code = run_list(ctx.list_head); if (code == -2) { /* exit */ b_free(&temp); code = 0; /* XXX hackish way to not allow exit from main loop */ if (inp->peek == file_peek) { printf("exit not allowed from main input shell.\n"); continue; } break; } if (code == -1) flag_repeat = 0; #endif } else { if (ctx.old_flag != 0) { free(ctx.stack); b_reset(&temp); } #ifdef __U_BOOT__ if (inp->__promptme == 0) printf("<INTERRUPT>\n"); inp->__promptme = 1; #endif temp.nonnull = 0; temp.quote = 0; inp->p = NULL; free_pipe_list(ctx.list_head,0); } b_free(&temp); /* loop on syntax errors, return on EOF */ } while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) && (inp->peek != static_peek || b_peek(inp))); #ifndef __U_BOOT__ return 0; #else return (code != 0) ? 1 : 0; #endif /* __U_BOOT__ */ }
部分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函数来处理命令。
3.4 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; #ifndef __U_BOOT__ int redir_fd; redir_type redir_style; #endif 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) { m = map[ch]; #ifdef __U_BOOT__ if (input->__promptme == 0) return 1; #endif 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 0 if (ch=='\n') { /* Yahoo! Time to run with it! */ done_pipe(ctx,PIPE_SEQ); run_list(ctx->list_head); initialize_context(ctx); } #endif 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!='\'') { #ifdef __U_BOOT__ if(input->__promptme == 0) return 1; #endif b_addchr(dest,ch); } if (ch==EOF) { syntax(); return 1; } break; case '"': dest->nonnull = 1; dest->quote = !dest->quote; break; #ifndef __U_BOOT__ case '`': process_command_subs(dest, ctx, input, '`'); break; case '>': redir_fd = redirect_opt_num(dest); done_word(dest, ctx); redir_style=REDIRECT_OVERWRITE; if (next == '>') { redir_style=REDIRECT_APPEND; b_getch(input); } else if (next == '(') { syntax(); /* until we support >(list) Process Substitution */ return 1; } setup_redirect(ctx, redir_fd, redir_style, input); break; case '<': redir_fd = redirect_opt_num(dest); done_word(dest, ctx); redir_style=REDIRECT_INPUT; if (next == '<') { redir_style=REDIRECT_HEREIS; b_getch(input); } else if (next == '>') { redir_style=REDIRECT_IO; b_getch(input); } else if (next == '(') { syntax(); /* until we support <(list) Process Substitution */ return 1; } setup_redirect(ctx, redir_fd, redir_style, input); break; #endif 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 { #ifndef __U_BOOT__ done_pipe(ctx,PIPE_BG); #else syntax_err(); return 1; #endif } 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". */ #ifndef __U_BOOT__ done_command(ctx); #else syntax_err(); return 1; #endif } break; #ifndef __U_BOOT__ case '(': case '{': if (parse_group(dest, ctx, input, ch)!=0) return 1; break; case ')': case '}': syntax(); /* Proper use of this character caught by end_trigger */ return 1; break; #endif 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; }
3.4 cmd_process函数
在uboot中,命令是通过宏U-BOOT-CMD来定义,最终目的是为了定义一个cmd_tbl_t类型的变量,并初始化这个变量的各个成员。Uboot中的每个命令都存放在.uboot_boot_list段中,每个命令都有一个名为do_xxx(xxx为具体的命令名)的函数,这个do_xxx函数就是具体的命令处理函数。了解了uboot中命令的组成再来看一下cmd_process函数的处理过程。
cmd_process在common/command.c中定义。通过调用find_cmd函数在命令表中找到指定的命令,命令表其实就是cmd_tlb_t结构体数组,在find_cmd函数中通过函数ll_entry_start得到数组的第一个元素,也就是命令表起始地址,通过ll_entry_count得到数组长度,也就是命令表的长度,最后通过find_cmd_tlb在命令表中找到所需的命令。
在命令表中找到命令以后,就调用cmd_call函数来执行具体的命令
参考文章