NanoPC-T2 Uboot启动过程分析 - 3-1 main_loop()初认识

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

由于篇幅关系,在下一节会继续说明启动命令的执行过程。本节暂告一段落。

你可能感兴趣的:(嵌入式,NanoPC-T2,u-boot)