main_loop()是uboot的主循环,位于\/uboot-root\/common\/main.c中。其代码如下:
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
modem_init();
cli_init();
run_preboot_environment_command();
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
}
首先用bootstage_mark_name()来标记uboot的运行状态。
接下来执行的是modem_init()。该函数什么都没做,忽略。
接下来执行的是位于\/uboot-root\/common\/cli.c中的cli_init()。里面就调用一个函数u_boot_hush_start()。该函数位于\/uboot-root\/common\/cli_hush.c中,主要是初始化一个变量top_vars,其代码如下:
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;
}
return 0;
}
通过代码分析,该函数的内容只执行一次。开始时top_vars为NULL,因此得到初始化。之后因为if判断不满足,所以以后不再执行。
接下来执行的是run_preboot_environment_command()。该函数什么都没做,忽略。
接下来执行的是bootdelay_process()。该函数位于\/uboot-root\/common\/autoboot.c中,其代码如下:
const char *bootdelay_process(void)
{
char *s;
int bootdelay;
s = getenv("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
bootretry_init_cmd_timeout();
s = getenv("bootcmd");
process_fdt_options(gd->fdt_blob);
stored_bootdelay = bootdelay;
return s;
}
在这个函数中,首先获取环境变量bootdelay的值,该值为0。这个值在最后存储在stored_bootdelay变量中。
然后执行的是位于\/uboot-root\/common\/bootretry.c中的bootretry_init_cmd_timeout()。这个函数主要设置了一个变量retry_time为0。
然后把环境变量bootcmd获取出来。
接下来的process_fdt_options(),里面并没有执行任何语句,忽略。
最后将bootcmd的内容返回上层。
返回到main_loop()。接下来执行的是cli_process_ftd()。由于该函数永远返回false,因此if判断永远不成立。
接下来执行的是位于\/uboot-root\/common\/autoboot.c中的autoboot_command()。其代码如下:
void autoboot_command(const char *s)
{
debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "" );
if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
run_command_list(s, -1, 0);
}
}
在这里有一个关键的函数abortboot()。由于stored_bootdelay为0,字符串s不为空。因此if判断成不成立全看abortboot()是否为0。如果为0,则运行if里的内容。
该函数里的内容很简单,只调用abortboot_normal()并返回其返回值。其代码如下:
static int abortboot_normal(int bootdelay)
{
int abort = 0;
unsigned long ts;
if (bootdelay >= 0)
printf("Hit any key to stop autoboot: %2d ", bootdelay);
/*
* Check if key already pressed
* Don't check if bootdelay < 0
*/
if (bootdelay >= 0) {
if (tstc()) { /* we got a key press */
(void) getc(); /* consume input */
puts("\b\b\b 0");
abort = 1; /* don't auto boot */
}
}
while ((bootdelay > 0) && (!abort)) {
--bootdelay;
/* delay 1000 ms */
ts = get_timer(0);
do {
if (tstc()) { /* we got a key press */
abort = 1; /* don't auto boot */
bootdelay = 0; /* no more delay */
(void) getc(); /* consume input */
break;
}
udelay(10000);
} while (!abort && get_timer(ts) < 1000);
printf("\b\b\b%2d ", bootdelay);
}
putc('\n');
return abort;
}
当bootdelay大于等于0的时候,程序先执行tstc(),这里这个函数会被定位于/uboot-root/drivers/serial/serial_pl01x.c中的pl01x_tstc()。这是通过读取UART0里的标志寄存器,里面有一位是指示RX缓冲是否有内容。当用户按下任意键时,键所对应的内容就会通过串口存到这个缓冲中。因此用这个标志位可以判断用户有没有按下键盘按键。若用户按下任意按键,则通过getc()去读取串口的RX缓冲,取得相应字符。然后标志abort变量,提示启动过程已被用户打断。
假设用户没有按下按键打断这个延迟启动过程,则继续往下走,进入一个while循环。在其它板子的Ubot中,可能会有延迟启动,用户可以看到”Hit any key to stop autoboot:”后面会有一个倒计时,就是在这个while循环里实现。代码比较简单,这里就不展开分析了。由于这的bootdelay为0,所以这个while循环并不成立。最后退出这个函数。
返回到autoboot_command()里,当延迟启动过程结束而用户又没打断该过程的话,就会直接运行run_command_list()。否则就会返回到main_loop(),执行cli_loop(),即Uboot的命令行模式。
由于run_command_list()与cli_loop()最终都会执行一个parse_stream_outer(),因此这里就不再分开情况讨论了。
从run_command_list()开始,就是对启动命令进行解析的过程,同时查找命令的实现。回忆启动命令:
$bloader 0x48000000 $kernel;$bloader 0x49000000 root.img.gz;bootm 0x48000000
启动命令一共有三条,全部存储在bootcmd这个环境变量中。run_command_list()就对这个命令进行解析。由于解析的过程比较复杂,这里就只简单说明解析中重要函数的调用过程,而不再详细展开。以下就是关键函数的调用过程顺序。
/uboot-root/common/cli.c => run_command_list()
/uboot-root/common/cli_hush.c => parse_string_outer()
/uboot-root/common/cli_hush.c => parse_stream_outer()
/uboot-root/common/cli_hush.c => run_list()
/uboot-root/common/cli_hush.c => run_list_real()
/uboot-root/common/cli_hush.c => run_pipe_real()
/uboot-root/common/command.c => cmd_process()
/uboot-root/common/command.c => cmd_call()
到这里为止,在cmd_call()就是用来调用UBoot的所有命令的一个函数,其代码如下:
/**
* Call a command function. This should be the only route in U-Boot to call
* a command, so that we can track whether we are waiting for input or
* executing a command.
*
* @param cmdtp Pointer to the command to execute
* @param flag Some flags normally 0 (see CMD_FLAG_.. above)
* @param argc Number of arguments (arg 0 must be the command text)
* @param argv Arguments
* @return 0 if command succeeded, else non-zero (CMD_RET_...)
*/
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", result);
return result;
}
可以看到,cmd_call()通过一个cmd_tbl_t结构,并调用其一个函数指针进行对实际命令的调用。UBoot的命令全部都位于/uboot-root/common/中,每一个命令都以”cmd_”开头的c文件形式存在。如bootm命令,则该命令的实现则存在于cmd_bootm.c中。关于UBoot添加自定义的命令的方法,可以参考下面的文章,这里就不再详细展开了。
http://www.cnblogs.com/sdphome/archive/2011/08/19/2146327.html
由于篇幅关系,在下一节会继续说明启动命令的执行过程。本节暂告一段落。