对u-boot 之 main_loop()函数的理解

/*
** 对u-boot-1.1.6中 main_loop()函数的理解。
** main_loop function in diractory: /common/main.c。
** u-boot启动内核过程:start.S -> start_armboot() -> main_loop() -> run_command() -> do_bootm_linux() -> 
   theKernel (0, bd->bi_arch_number, bd->bi_boot_params) ,then start kernel core.
*/

/*
** 由于官方发布的源文件是通用版本,使用了很多条件编译,代码看起来很乱。
** 这里根据实际使用情况(默认条件),去掉一些条件编译,使得代码清晰,然后来分析。
*/

void main_loop (void)
{
    static char lastcommand[CFG_CBSIZE] = { 0, };
    int len;
    int rc = 1;
    int flag;

    char *s;
    int bootdelay;
    
    /*
    ** 函数getenv从环境变量内存块中查找环境变量值bootdelay,如果存在,就返回在环境变量内存块的地址。
    ** 如果不存在这个环境变量值,那么返回NULL。
    ** 调用关系:getenv -> env_get_char -> env_get_char_init -> env_get_char_spec.
    */

    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 : "");
    
    /*
    ** abortboot()函数是获取串口输入,如果在设定的延时时间bootdelay内没有字符,那么返回0,否则返回1.
    ** 1.如果返回0,说明没有输入任何命令,那么就执行run_command()函数,这里的s = bootcmd,
    ** 就是执行引导内核的命令,u-boot的任务就此完成,进入Linux的世界;
    ** 2.如果返回1,这个if条件不满足,就执行下面的for 死循环,等待串口输入命令,并且执行命令;
    */

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) 
    {
        run_command (s, 0);
    }

    /*
     * Main Loop for Monitor Command Processing
     */

    for (;;) {

        len = readline (CFG_PROMPT);

        flag = 0;    /* assume no special flags for now */

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

        if (len == -1)
            puts ("\n");
        else
            rc = run_command (lastcommand, flag);

        if (rc <= 0) {
            /* invalid command or not repeatable, forget it */
            lastcommand[0] = 0;
        }
    }
}

/*
** Look up variable from environment,return address of storage for that variable,or NULL if not found.
** dir: common/cmd_nvedit.c   
*/

char *getenv (char *name)
{
    int i, nxt;

    WATCHDOG_RESET();
    
    /*
    ** 在文件dir: common/common.c 中定义:uchar (*env_get_char)(int) = env_get_char_init;   
    ** 所以,env_get_char是一个函数指针,指向了函数env_get_char_init (dir:common.c)。
    ** 函数env_get_char_init又调用了env_get_char_spec (dir:env_flash.c)。
    ** 我认为这就是一种封装思维,分文件(cmd_nvedit.c) ->总文件(common.c) -分文件(cmd_flash.c),
    ** common.c就是一个接口文件,分文件通过这个接口调用别的分文件,而不是直接调用别的分文件。
    ** 整个函数getenv的思路是:
    ** 1.先明白环境变量的储存方式:"bootargs="    CONFIG_BOOTARGS    "\0" "bootcmd="    CONFIG_BOOTCOMMAND    "\0" ......;
    ** 2.从环境变量分区的开始地址gd->env_addr,按byte by byte的往后查找,先找出第一个环境变量的值,然后用name去比较
         环境变量的值,如果匹配,则到下一步,计算环境变量地址值,且退出整个getenv函数。如果不匹配,继续找出下一个
         环境变量值,再与name比较,如此循环,直到环境变量内存区的结束地址为止。
    ** 3.如果环境变量校验错误,使用默认环境变量数组的值,直接使用数组下标查找,这样代码更容易理解些。
    */

    for (i=0; env_get_char(i) != '\0'; i=nxt+1) 
    {
        int val;
        
        /*
        ** 从当前地址开始找到第一个字符\0,即找出了一个环境变量值,然后退出循环。
        ** 如果一直没有找到,且超过了环境变量最大长度CFG_ENV_SIZE则退出所有for循环返回NULL。
        */

        for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) 
        {
            if (nxt >= CFG_ENV_SIZE) 
            {
                return (NULL);
            }
        }
        
        /*
        ** dir: common/cmd_nvedit.c 
        ** 如果匹配到了环境变量值,则返回此环境变量值在环境变量内存区的地址偏移量。
        */

        if ((val=envmatch((uchar *)name, i)) < 0)
            continue;
        /*
        ** dir: common/cmd_nvedit.c 
        ** 匹配环境变化成功,说明我们确实定义了这个环境变量值,计算出此环境变量在环境变量内存区的地址。
        */

        return ((char *)env_get_addr(val));
    }

    return (NULL);
}

static uchar env_get_char_init (int index) //dir:common.c
{
    uchar c;

    /* if crc was bad, use the default environment */
    if (gd->env_valid)
    {
        c = env_get_char_spec(index);
    } else {
        c = default_environment[index];
    }

    return (c);
}

uchar env_get_char_spec (int index)   //dir:env_flash.c
{
    return ( *((uchar *)(gd->env_addr + index)) );
}

你可能感兴趣的:(u-boot)