一、u-boot对命令行的命令解析过程分析
u-boot的主线运行过程中,在board_init_r()
函数进行u-boot初始化过程的最后一个过程则是:运行主函数循环
。该函数名为
run_main_loop()
。定义如下:
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop(); //此处是一个死循环调用main_loop()函数
return 0;
}
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 */
//初始化hush shell相关的变量
cli_init();
//获取环境变量preboot的内容,preboot是一些预启动命令。
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);
//该函数检测在u-boot启动过程中的【倒计时过程】是否结束?【倒计时】结束之前是否被打断
autoboot_command(s);
//该函数是u-boot的命令行处理函数,当在终端输入命令时,就是依靠该函数来处理
cli_loop();
}
由以上代码可见,有两个重要函数:
(1)autoboot_command(s)
函数:用于检测u-boot启动过程中的【倒计时过程】是否结束。【倒计时】结束之前是否被打断。
(2)cli_loop()
函数:该函数是u-boot的命令行处理函数,当我们在u-boot命令行输入命令时,则依靠该函数来处理。
接下来,就来分析以下这两个函数:
autoboot_command()函数定义如下:
void autoboot_command(const char *s)
{
debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
int prev = disable_ctrlc(1); /* disable Control C checking */
#endif
run_command_list(s, -1, 0);
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
disable_ctrlc(prev); /* restore Control C checking */
#endif
}
#ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s)
run_command_list(s, -1, 0);
}
#endif /* CONFIG_MENUKEY */
}
cli_loop()函数的定义如下:
void cli_loop(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
cli_simple_loop();
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
parse_file_outer()函数定义如下:
int parse_file_outer(void)
#endif
{
int rcode;
struct in_str input;
#ifndef __U_BOOT__
setup_file_in_str(&input, f); //用于初始化变量input的成员变量值
#else
setup_file_in_str(&input);
#endif
//负责接收命令行输入,然后解析并执行相应的命令 【重要函数】
rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);
return rcode;
}
parse_stream_outer()函数定义如下:
/* most recursion does not come through here, the exeception is
* from builtin_source() */
static int parse_stream_outer(struct in_str *inp, int flag)
{
struct p_context ctx;
o_string temp=NULL_O_STRING;
int rcode;
#ifdef __U_BOOT__
int code = 1;
#endif
do {
ctx.type = flag;
initialize_context(&ctx);
update_ifs_map();
if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
inp->promptmode=1;
rcode = parse_stream(&temp, &ctx, inp,
flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');
#ifdef __U_BOOT__
if (rcode == 1) flag_repeat = 0;
#endif
if (rcode != 1 && ctx.old_flag != 0) {
syntax();
#ifdef __U_BOOT__
flag_repeat = 0;
#endif
}
if (rcode != 1 && ctx.old_flag == 0) {
done_word(&temp, &ctx);
done_pipe(&ctx,PIPE_SEQ);
#ifndef __U_BOOT__
run_list(ctx.list_head);
#else
code = run_list(ctx.list_head);
if (code == -2) { /* exit */
b_free(&temp);
code = 0;
/* XXX hackish way to not allow exit from main loop */
if (inp->peek == file_peek) {
printf("exit not allowed from main input shell.\n");
continue;
}
break;
}
if (code == -1)
flag_repeat = 0;
#endif
} else {
if (ctx.old_flag != 0) {
free(ctx.stack);
b_reset(&temp);
}
#ifdef __U_BOOT__
if (inp->__promptme == 0) printf("<INTERRUPT>\n");
inp->__promptme = 1;
#endif
temp.nonnull = 0;
temp.quote = 0;
inp->p = NULL;
free_pipe_list(ctx.list_head,0);
}
b_free(&temp);
/* loop on syntax errors, return on EOF */
} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&
(inp->peek != static_peek || b_peek(inp)));
#ifndef __U_BOOT__
return 0;
#else
return (code != 0) ? 1 : 0;
#endif /* __U_BOOT__ */
}
在parse_stream_outer()函数中,有个大的do-while用于处理输入命令。在其中会调用parse_stream函数进行命令解析,调用run_list函数【该函数调用逻辑特别长】来执行解析出来的命令操作。经过一系列函数调用操作后,run_list()函数将会调用到cmd_process()
函数来处理命令。cmd_process函数定义如下:
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;
#if defined(CONFIG_CMD_BOOTD)
/* 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;
}
}
#endif
/* 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;
}
当cmd_process()函数中找到了命令后,会调用cmd_call()
函数来执行命令了,该函数定义如下:
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
int result;
//调用注册的具体的命令执行函数来执行命令操作
result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
if (result)
debug("Command failed, result=%d\n", result);
return result;
}
在以上代码中:
result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
则是调用注册的具体命令执行函数来执行命令操作了。
二、总结
在开发中,如果使用u-boot作为硬件板卡的引导加载程序,那么肯定会接触到其命令行,u-boot的命令行非常好用,但u-boot对输入命令的解析过程较为复杂,u-boot对命令的解析过程大致如下图所示【从源码角度分析】:
(1)在u-boot启动主线中,被board_init_r()函数调用的run_main_loop()函数承载了命令行解析的顶层入口。在run_main_loop()函数中会调用main_loop()函数,命令的解析则在该函数中展开。
(2)在u-boot启动过程中,【倒计时过程】是由autoboot_command(s)
函数完成。
(3)cmd_process()
函数是核心的命令解析函数。
(4)当找到对应的命令后,则调用cmd_call()
函数来执行对应命令下的具体操作。