一. 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
有定义。
第 205 行,执行 parse_file_outer函数。
第
207
行,是死循环,永远不会执行到这里。
函数
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
函数来处理命令。
三. 总结
函数调用关系如下: