看到这里的同学,恭喜你,马上就看完了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) /* >&