前言
第一次学u-boot启动流程的时候我也是一头雾水,当然啦看一遍留个大概印象就好!
要想都学透的话也要花费很大的时间精力
本系列blog分三次写。本节讲最后一讲
在虚拟机中我的Vcode比较卡,所以就在window环境下演示的,不过效果都是相同的,所以无需担心!!!如有错误的地方请及时联系我,我们一起学习!!!
板子是正点原子提供的IMX6ULL系列,所以有些函数例如判断CPU、board类型的时候默认跳过
目录
run_main_loop 函数详解
u-boot启动后会进入3秒倒计时(可以在secureCRT里设置环境变量),如果在 3 秒倒计时结束之前按下按下回车键,那么就会进入 uboot 的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动 Linux 内核 ,这个功能就是由 run_main_loop 函 数来完成的
run_main_loop定义在common/board_r.c 中,代码如下
代码可以看出来进入到run_main_loop函数,里面有一个死循环,紧接着进入main_loop函数
main_loop
main_loop 函数定义在文件 common/main.c 里面,代码如下
48 行:调用 bootstage_mark_name 函数,打印出启动进度
56 行:如果定义了宏 CONFIG_VERSION_VARIABLE 的话就会执行函数 setenv,设置
换将变量 ver 的值为 version_string,也就是设置版本号环境变量。
60 行:run_preboot_environment_command 函数,获取环境变量 perboot 的内容, perboot
是一些预启动命令,一般不使用这个环境变量。
65 行:bootdelay_process 函数,此函数会读取环境变量 bootdelay 和 bootcmd 的内容,
然后将 bootdelay 的值赋值给全局变量 stored_bootdelay,返回值为环境变量 bootcmd 的值。bootdelay就是开头我们说的三秒倒计时
68 行:autoboot_command 函数,此函数就是检查倒计时是否结束?倒计时结束之前有
没有被打断?
此函数定义在文件common/autoboot.c中,代码如下
当以下三条条件全部成立的话,就会执行389行run_command_list函数
①、stored_bootdelay 不等于-1。相当于环境变量bootdelay的值
②、s 不为空。s是bootcmd的值
③、函数 abortboot 返回值为 0
abortboot的函数定义在文件common/autoboot.c中,代码如下(跟紧!要开始套娃了)
因为宏 CONFIG_AUTOBOOT_KEYE 未定义,因此执行函数 abortboot_normal
abortboot_normal函数定义在common/autoboot.c(有关于一些通用的定义,大部分都在common根目录下)代码如下
删除掉条件编译之后的代码如下
static int abortboot_normal(int bootdelay)
2 {
3 int abort = 0;
4 unsigned long ts;
5
6 if (bootdelay >= 0)
7 printf("Hit any key to stop autoboot: %2d ", bootdelay);
8
9 while ((bootdelay > 0) && (!abort)) {
10 --bootdelay;
11 /* delay 1000 ms */
12 ts = get_timer(0);
13 do {
14 if (tstc()) { /* we got a key press */
15 abort = 1; /* don't auto boot */
16 bootdelay = 0; /* no more delay */
17 (void) getc(); /* consume input */
18 break;
19 }
20 udelay(10000);
21 } while (!abort && get_timer(ts) < 1000);
22
23 printf("\b\b\b%2d ", bootdelay);
24 }
25 putc('\n');
26 return abort;
27 }
3 行:abort 是函数 abortboot_normal 的返回值,默认值为 0。
7 行:通过串口输出“Hit any key to stop autoboot”字样
9-21 行:倒计时的具体实现
14 行:判断键盘是否有按下,是否打断倒计时。如果有键盘按下的话就执行相应的分支
26 行:返回abort的值,如果倒计时自然结束,没有被打断abort就为0,否则abort的值为1
显然易见,当倒计时自然结束,三条条件全部成立,就会执行行run_command_list函数
此函数会执行参数 s 指定的一系列命令,也就是环境变量 bootcmd 的命令,
bootcmd 里面保存着默认的启动命令,因此 linux 内核启动!这个就是 uboot 中倒计时结束以后自动启动 linux 内核的原理。如果倒计时结束之前按下了键盘上的按键
那么 run_command_list函数就不会执行,相当于 autoboot_command 是个空函数。
我们回到main_loop中,继续执行cli_loop函数
这个就是命令处理函数,负责接受好处理输入的命令
cli_loop函数详解
cli_loop 函数是 uboot 的命令行处理函数,我们在 uboot 中输入各种命令,进行各种操作就
是有 cli_loop 来处理的
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;
}
4 行:调用函数 setup_file_in_str 初始化变量 input 的成员变量。
5 行:调用函数 parse_stream_outer,这个函数就是 hush shell 的命令解释器,负责接收命
令行输入,然后解析并执行相应的命令
parse_stream_outer函数定义在文件common/cli_hush.c中,去掉条件编译后代码如下
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 {
......
rcode = parse_stream(&temp, &ctx, inp,
flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');
......
if (rcode != 1 && ctx.old_flag == 0) {
......
run_list(ctx.list_head);
......
} else {
......
}
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 0;
}
5 行:run_list 调用 run_list_real 函数
16 行:run_list_real 函数调用 run_pipe_real 函数
36 行:run_pipe_real 函数调用 cmd_process 函数
最终通过函数 cmd_process 来处理命令,接下来就是分析 cmd_process 函数。
完整版u-boot流程图