一. uboot启动
uboot命令模式:uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,那么就会进入 uboot 的命令模式。
如果在 uboot 倒计时结束以后都没有按下回车键,就会自动启动 Linux 内核 , 这 个 功 能 就 是 由 run_main_loop 函 数 来 完 成 的 。
二. run_main_loop函数 到 cmd_process处理
1. run_main_loop函数
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;
}
" for (;;) " 和 " while(1) " 功能一样,死循环里面就一个 main_loop 函数。
2. main_loop 函数
main_loop 函数定义在common/main.c 文件 里面。代码如下:
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();
}
main_loop 函数中:
第 5 行,调用 bootstage_mark_name 函数,打印出启动进度。
第 13 行,如果定义了宏 CONFIG_VERSION_VARIABLE,就会执行函数 setenv,设置
变量 ver 的值为 version_string,也就是设置版本号环境变量。version_string 定义在文件
cmd/version.c 中,定义如下:
const char __weak version_string[] = U_BOOT_VERSION_STRING;
经过搜索,一系列分析可知,Uboot打印如下:
U-Boot 2016.03 (Jul 07 2023 - 17:11:27 +0800)
第 17 行,cli_init 函数,跟命令初始化有关,初始化 hush shell 相关的变量。
第 19 行,run_preboot_environment_command 函数,获取环境变量 perboot 的内容,perboot 是一些预启动命令,一般不使用这个环境变量。
第 25 行,bootdelay_process 函数,此函数会读取环境变量 bootdelay 和 bootcmd 的内容, 然后将 bootdelay 的值赋值给全局变量 stored_bootdelay,返回值为环境变量 bootcmd 的值。
第 26 行,如果定义了 CONFIG_OF_CONTROL 的话函数 cli_process_fdt 就会实现,如果没有定义 CONFIG_OF_CONTROL 的话函数 cli_process_fdt 直接返回一个 false。在本 uboot 中没有定义 CONFIG_OF_CONTROL,因此 cli_process_fdt 函数返回值为 false。
第 29 行,autoboot_command 函数,此函数就是检查倒计时是否结束?倒计时结束之前有 没有被打断?此函数定义在文件 common/autoboot.c 。
第 74 行的 cli_loop 函数,这个就是命令处理函数。如果倒计时结束之前按下按键, 那么就会执行 cli_loop 函数,负责接收好处理输入的命令。
cli_loop 函数是 uboot 的命令行处理函数,我们在 uboot 中输入各种命令,进行各种操作就
是有 cli_loop 来处理的,此函数定义在文件 common/cli.c 中。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*/
}
void cli_init(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start();
#endif
#if defined(CONFIG_HUSH_INIT_VAR)
hush_init_var();
#endif
}
在文件 include/configs/mx6_common.h 中有定义宏 CONFIG_SYS_HUSH_PARSER,而正点原子的 I.MX6ULL 开发板配置头文件 mx6ullevk.h 里面会引用 mx_common.h 这个头文件,因此,宏 CONFIG_SYS_HUSH_PARSER 有定义。
第 4 行,执行 parse_file_outer函数。
第 6行,是死循环,永远不会执行到这里。
函数 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;
}
第 6 行,调用函数 setup_file_in_str 初始化变量 input 的成员变量。
第 7 行,调用函数 parse_stream_outer函数,这个函数就是 hush shell 的命令解释器,负责接收命令行输入,然后解析并执行相应的命令,函数 parse_stream_outer 定义在文件 common/cli_hush.c中,精简版的函数内容如下:
static int parse_stream_outer(struct in_str *inp, int flag)
{
......
do {
......
if (rcode != 1 && ctx.old_flag == 0) {
{
run_list(ctx.list_head);
}
......
} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&
(inp->peek != static_peek || b_peek(inp)));
......
}
第 7~21 行中的 do-while 循环就是处理输入命令的。
第 9 行调用函数 parse_stream 进行命令解析。
第 14 行调用调用 run_list 函数来执行解析出来的命令。
函数 run_list 会经过一系列的函数调用,最终通过调用 cmd_process 函数来处理命令。
三. 总结
函数调用关系如下: