初步了解UBOOT (3)

进入到main_loop函数中,大致内容如下:

void main_loop (void)
{

...

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

...
        s = getenv ("bootcmd");

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {

    ...

        {
            printf("Booting Linux ...\n");            
            run_command (s, 0);
        }
    ...

    }

...

    /*
        run_command("menu", 0);   此处为厂家自己添加,若在uboot读秒时没有按下空格键则执行该命令
     */

...

    for (;;) {

        len = readline (CFG_PROMPT);

        if (len > 0)
            strcpy (lastcommand, console_buffer);
        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME

        ...     

            rc = run_command (lastcommand, flag);
    }
}

可以将main_loop的流程归于以下流程图:

初步了解UBOOT (3)_第1张图片

在main_loop函数中,首先读取环境变量bootdelay的值。

假如在规定的秒数内没有按下空格键的话,则会执行 bootcmd 环境变量的值。
在我的JZ2440中,该环境变量的值为:bootcmd = nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
即会执行run_command (“nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0”, 0);

若在规定的秒数内按下空格,则会执行 len = readline (CFG_PROMPT),该语句是读取串口的输入,然后再执行 rc = run_command (lastcommand, flag),即执行串口输入的字符串。

所以还需要去深入了解run_command函数的执行过程。

以下是run_command函数:

int run_command (const char *cmd, int flag)
{
    ...

    char *str = cmdbuf;

    if (strlen(cmd) >= CFG_CBSIZE) {
        puts ("## Command too long!\n");
        return -1;
    }

    strcpy (cmdbuf, cmd);

    ...

    while (*str) {

        /*
         * Find separator, or string end
         * Allow simple escape of ';' by writing "\;"
         */
        for (inquotes = 0, sep = str; *sep; sep++) {
            if ((*sep=='\'') &&
                (*(sep-1) != '\\'))
                inquotes=!inquotes;

            if (!inquotes &&
                (*sep == ';') &&    /* separator        */
                ( sep != str) &&    /* past string start    */
                (*(sep-1) != '\\')) /* and NOT escaped  */
                break;
        }

        /*
         * Limit the token to data between separators
         */
        token = str;
        if (*sep) {
            str = sep + 1;  /* start of command for next pass */
            *sep = '\0';
        }
        else
            str = sep;  /* no more commands for next pass */

        ...

        /* Look up command in command table */
        if ((cmdtp = find_cmd(argv[0])) == NULL) {
            printf ("Unknown command '%s' - try 'help'\n", argv[0]);
            rc = -1;    /* give up after bad command */
            continue;
        }

        ...

        /* OK - call function to do the command */
        if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
            rc = -1;
        }

    }
}

在run_command函数中,主要是对字符串做一些解析,关键的部分在于find_cmd函数的调用,该函数的作用就是在一个结构体链表中去查找有无与该指令(argv[0])所相对应的结构体。若找到该结构体,则执行结构体中所涉及到的函数。

所以继续深入了解,find_cmd函数的内容如下:

cmd_tbl_t *find_cmd (const char *cmd)
{
    ...

    /*
     * Some commands allow length modifiers (like "cp.b");
     * compare command name only until first dot.
     */
    len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);

    for (cmdtp = &__u_boot_cmd_start;
         cmdtp != &__u_boot_cmd_end;
         cmdtp++) {
        if (strncmp (cmd, cmdtp->name, len) == 0) {
            if (len == strlen (cmdtp->name))
                return cmdtp;   /* full match */

            cmdtp_temp = cmdtp; /* abbreviated command ? */
            n_found++;
        }
    }
    if (n_found == 1) {         /* exactly one match */
        return cmdtp_temp;
    }

    return NULL;    /* not found or ambiguous command */
}

该函数对传入进来的参数(argv[0],即cmd)做一些解析,接着在__u_boot_cmd_start与__u_boot_cmd_end的数据段中寻找有无与该参数相关的cmd_tbl_t结构体。该结构体内容如下:

struct cmd_tbl_s {
    char        *name;      /* Command Name */
    int     maxargs;    /* maximum number of arguments */
    int     repeatable; /* autorepeat allowed? */
                    /* Implementation function */
    int     (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
    char        *usage;     /* Usage message (short) */
    char        *help;      /* Help message (long) */
    ...
};

其中第一个就是命令名,即 argv[0] (cmd),若匹配到的话则返回该结构体。

至于为什么会在__u_boot_cmd_start与__u_boot_cmd_end之间保存这些结构体,则与以下几个宏有关。

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))

将宏替换掉则为:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ 
cmd_tbl_t __u_boot_cmd_##name __attribute__ ((unused,section (".u_boot_cmd"))) = 
{#name, maxargs, rep, cmd, usage}

举个例子,对于 bootcmd 环境变量的值 = nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
对于其中的bootm,则在uboot源码中的Cmd_bootm.c中,则有

U_BOOT_CMD(
    bootm,  CFG_MAXARGS,    1,  do_bootm,
    "bootm - boot application image from memory\n",
    "[addr [arg ...]]\n - boot application image stored in memory\n"
    "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
    "\t'arg' can be the address of an initrd image\n"
);

所以将U_BOOT_CMD进行宏替换之后则变成

cmd_tbl_t __u_boot_cmd_bootm  __attribute__ ((unused,section (".u_boot_cmd"))) = 
{   
    bootm, CFG_MAXARGS, 1, do_bootm, 
    "[addr [arg ...]]\n - boot application image stored in memory\n"
    "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
    "\t'arg' can be the address of an initrd image\n"
};

这段代码的作用这是将该结构体变量放在.u_boot_cmd段之中,它位于__u_boot_cmd_start与__u_boot_cmd_end之间。至于.u_boot_cmd该段则是在链接文件u-boot.lds(可以查看我写的《初步了解UBOOT (2)》)中定义的。

从 bootcmd 环境变量的值 nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0,可以得知,一定存在两个U_BOOT_CMD,分别对应nand指令,还有bootm指令,即输入nand的话有与之对应的函数可以执行,输入bootm也有与之对应的函数可以执行,从上面的__u_boot_cmd_bootm定义可以得知,对于bootm有函数do_bootm对应,即会执行该函数,对于nand read.jffs2 0x30007FC0 kernel的作用很明白可以知道将内核从NAND FLASH的内核分区中读到0x30007FC0这个地址上,而指令bootm 0x30007FC0则是去这个地址上启动内核。

我们进一步的来分析do_bootm这个函数具体做了什么事情。

你可能感兴趣的:(u-boot,bootm,内核启动,jz2440)