现在的位置: 首页 > 综合 > 正文

uboot启动第二阶段详细分析(2)

2019年04月20日 ⁄ 综合 ⁄ 共 14623字 ⁄ 字号 评论关闭

uboot中 main_loop  函数的详细分析

函数位置:common/main.c

函数功能:在标准转入设备中接受命令行,然后分析,查找,执行

温习:Uboot在硬件初始化完成后将进入main_loop()函数,main_loop()函数将进入一个无限循环,当用户输入命令后,首先将调用run_command()命令进行处理,在run_command()函数中,将调用find_cmd()函数把用户从终端输入的命令进行比较,当find_cmd()返回值不为0时证明系统支持用户输入的命令,在对命令进行检验后最后将调用命令处理函数。

void main_loop (void)
{
#ifndef CFG_HUSH_PARSER         /*smdk2410没定义*/
    static char lastcommand[CFG_CBSIZE] = { 0, };
    int len;
    int rc = 1;
    int flag;
#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    char *s;
    int bootdelay;

#endif

/*中间很多代码都不在smdk2410编译范围中,所以删了减少空间*/

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    s = getenv ("bootdelay");            //getenv()函数功能:从环境变量中查找这个参数对应的地址,并返回,这里查找的应该是内核启动前的等待时间,没找到则返回NULL
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);     //打印延时启动内核的时间

# ifdef CONFIG_BOOT_RETRY_TIME        /*smdk2410没定义*/
    init_cmd_timeout ();
# endif    /* CONFIG_BOOT_RETRY_TIME */

#ifdef CONFIG_BOOTCOUNT_LIMIT          /*smdk2410没定义*/
    if (bootlimit && (bootcount > bootlimit)) {
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
                (unsigned)bootlimit);
        s = getenv ("altbootcmd");
    }
    else
#endif /* CONFIG_BOOTCOUNT_LIMIT */   
        s = getenv ("bootcmd");        // 获取引导命令 
取得环境中设置的启动命令行
,smdk2410下这个环境变量是空的

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

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {   
//如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。abortboot函数的分析见下面

# ifdef CONFIG_AUTOBOOT_KEYED              /*smdk2410没定义*/
        int prev = disable_ctrlc(1);    /* disable Control C checking */
# endif

# ifndef CFG_HUSH_PARSER
        run_command (s, 0);       ////运行引导内核的命令。这个命令是在配置头文件中定义的,smdk2410.h中没有定义CONFIG_BOOTCOMMAND,所以没有命令执行。
# else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif

# ifdef CONFIG_AUTOBOOT_KEYED         /*smdk2410没定义*/
        disable_ctrlc(prev);    /* restore Control C checking */
# endif

    }

# ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = getenv("menucmd");
        if (s) {
# ifndef CFG_HUSH_PARSER
        run_command (s, 0);
# else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
# endif
        }
    }
#endif /* CONFIG_MENUKEY */

#endif    /* CONFIG_BOOTDELAY */

#ifdef CONFIG_AMIGAONEG3SE
    {
        extern void video_banner(void);
        video_banner();
    }
#endif

    /*
     * Main Loop for Monitor Command Processing   最核心的就是这个死循环
     */
#ifdef CFG_HUSH_PARSER
    parse_file_outer();
    /* This point is never reached */
    for (;;);
#else

    for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
        if (rc >= 0) {
            /* Saw enough of a valid command to
             * restart the timeout.
             */
            reset_cmd_timeout();
        }
#endif

        len = readline (CFG_PROMPT);    //读取键入的命令行到console_buffersmdk2410中有 #define    CFG_PROMPT        "SMDK2410 # "

        flag = 0;    /* assume no special flags for now */
        if (len > 0)
            strcpy (lastcommand, console_buffer);      //拷贝命令行到lastcommand.

        else if (len == 0)
            flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
        else if (len == -2) {
            /* -2 means timed out, retry autoboot
             */
            puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
            /* Reinit board to run initialization code again */
            do_reset (NULL, 0, 0, NULL);
# else
            return;        /* retry autoboot */
# endif
        }
#endif

        if (len == -1)
            puts ("<INTERRUPT>\n");
        else
            rc = run_command (lastcommand, flag);    //执行这个命令行。

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

}  //end main_loop

分析main_loop中三个重要函数

   1. readline函数

位置:common/main.c

功能:提示用户输入,然后一直等待用户输入,指到用户输入完成, 然后获取用户数据,并存入全局变量console_buffer中

/*
 * Prompt for input and read a line.
 * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
 * time out when time goes past endtime (timebase time in ticks).
 * Return:    number of read characters
 *        -1 if break
 *        -2 if timed out
 */

int readline (const char *const prompt)
{
#ifdef CONFIG_CMDLINE_EDITING     /*sdmk2410没定义*/
    char *p = console_buffer;
    unsigned int len=MAX_CMDBUF_SIZE;
    int rc;
    static int initted = 0;

    if (!initted) {
        hist_init();
        initted = 1;
    }

    puts (prompt);

    rc = cread_line(p, &len);
    return rc < 0 ? rc : len;
#else
    char   *p = console_buffer;
    int    n = 0;                /* buffer index        */
    int    plen = 0;            /* prompt length    */
    int    col;                /* output column cnt    */
    char    c;

    /* print prompt */
    if (prompt) {          //smdk2410中传入的是  "SMDK2410 # "
        plen = strlen (prompt);
        puts (prompt);       //输出提示符
    }
    col = plen;

    for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME         /*sdmk2410没定义*/
        while (!tstc()) {    /* while no incoming data */
            if (retry_time >= 0 && get_ticks() > endtime)
                return (-2);    /* timed out */
        }
#endif

        WATCHDOG_RESET();        /* Trigger watchdog, if needed */

#ifdef CONFIG_SHOW_ACTIVITY       /*sdmk2410没定义*/
        while (!tstc()) {
            extern void show_activity(int arg);
            show_activity(0);
        }
#endif
 
        c = getc();      /*从串口获取用户输入的命令*/

        /*
         * Special character handling
         */

        /*下面这个switch就是处理不同的字符了*/
        switch (c) {
        case '\r':                /* Enter        */
        case '\n':
            *p = '\0';
            puts ("\r\n");
            return (p - console_buffer);     /*输入结束*/

        case '\0':                /* nul            忽略,继续*/
            continue;

        case 0x03:                /* ^C - break    linux下的ctrl+c功能    */
            console_buffer[0] = '\0';    /* discard input */
            return (-1);

        case 0x15:                /* ^U - erase line    删除*/
            while (col > plen) {
                puts (erase_seq);
                --col;
            }
            p = console_buffer;
            n = 0;
            continue;

        case 0x17:                /* ^W - erase word   删除 */
            p=delete_char(console_buffer, p, &col, &n, plen);
            while ((n > 0) && (*p != ' ')) {
                p=delete_char(console_buffer, p, &col, &n, plen);
            }
            continue;

        case 0x08:                /* ^H  - backspace    */
        case 0x7F:                /* DEL - backspace    */
            p=delete_char(console_buffer, p, &col, &n, plen);
            continue;

        default:             /*获取常规字符*/
            /*
             * Must be a normal character then
             */
            if (n < CFG_CBSIZE-2) {
                if (c == '\t') {    /* expand TABs        */
#ifdef CONFIG_AUTO_COMPLETE
                    /* if auto completion triggered just continue    自动补全功能*/
                    *p = '\0';
                    if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
                        p = console_buffer + n;    /* reset */
                        continue;
                    }
#endif
                    puts (tab_seq+(col&07));
                    col += 8 - (col&07);
                } else {
                    ++col;        /* echo input        */
                    putc (c);
                }
                *p++ = c;   /*把字符保存在buffer中*/
                ++n;
            } else {            /* Buffer full        */
                putc ('\a');
            }
        }
    }
#endif /* CONFIG_CMDLINE_EDITING */
}

     2. run_command

位置:common/main.c

功能:在命令table中查找匹配的命令名称,得到对应命令结构体变量指针,以解析得到的参数调用其处理函数执行命令

/****************************************************************************
 * returns:
 *    1  - command executed, repeatable
 *    0  - command executed but not repeatable, interrupted commands are
 *         always considered not repeatable
 *    -1 - not executed (unrecognized, bootd recursion or too many args)
 *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
 *           considered unrecognized)
 *
 * WARNING:
 *
 * We must create a temporary copy of the command since the command we get
 * may be the result from getenv(), which returns a pointer directly to
 * the environment data, which may change magicly when the command we run
 * creates or modifies environment variables (like "bootp" does).
 */

int run_command (const char *cmd, int flag)
{
    cmd_tbl_t *cmdtp;
    char cmdbuf[CFG_CBSIZE];    /* working copy of cmd        */
    char *token;            /* start of token in cmdbuf    */
    char *sep;            /* end of token (separator) in cmdbuf */
    char finaltoken[CFG_CBSIZE];
    char *str = cmdbuf;
    char *argv[CFG_MAXARGS + 1];    /* NULL terminated    */
    int argc, inquotes;
    int repeatable = 1;
    int rc = 0;

#ifdef DEBUG_PARSER
    printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
    puts (cmd ? cmd : "NULL");    /* use puts - string may be loooong */
    puts ("\"\n");
#endif

    clear_ctrlc();        /* forget any previous Control C */

    if (!cmd || !*cmd) {
        return -1;    /* empty command */
    }

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

    strcpy (cmdbuf, cmd);

    /* Process separators and check for invalid
     * repeatable commands
     */

#ifdef DEBUG_PARSER
    printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
    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 */
#ifdef DEBUG_PARSER
        printf ("token: \"%s\"\n", token);
#endif

        /* find macros in this token and replace them */
        process_macros (token, finaltoken);

        /* Extract arguments */
        if ((argc = parse_line (finaltoken, argv)) == 0) {
            rc = -1;    /* no command at all */
            continue;
        }

        /* 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;
        }

        /* found - check max args */
        if (argc > cmdtp->maxargs) {
            printf ("Usage:\n%s\n", cmdtp->usage);
            rc = -1;
            continue;
        }

#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
        /* avoid "bootd" recursion */
        if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
            printf ("[%s]\n", finaltoken);
#endif
            if (flag & CMD_FLAG_BOOTD) {
                puts ("'bootd' recursion detected\n");
                rc = -1;
                continue;
            } else {
                flag |= CMD_FLAG_BOOTD;
            }
        }
#endif    /* CFG_CMD_BOOTD */

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

        repeatable &= cmdtp->repeatable;

        /* Did the user stop this? */
        if (had_ctrlc ())
            return 0;    /* if stopped then not repeatable */
    }

    return rc ? rc : repeatable;
}

3.  abortboot函数的分析

功能: abortboot是uboot在引导期间的延时函数。期间可以按键进入uboot的命令行。
位置:common/main.c
static __inline__ int abortboot(int bootdelay)
{
    int abort = 0;
    printf("Hit any key to stop autoboot: %2d ", bootdelay);

#if defined CONFIG_ZERO_BOOTDELAY_CHECK   //如果定义了这个宏,即使定义延时为0,也会检查一次是否有按键按下。只要在这里执行之前按键,还是能进入uboot的命令行。

    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    */修改标记,停止自动引导

        }
    }
#endif

    while ((bootdelay > 0) && (!abort)) { //如果延时大于零并且停止标记没有赋值则进入延时循环,直到延时完或者接收到了按
        int i;

        --bootdelay;
        /* delay 100 * 10ms */ 每秒中测试按键100次,之后延时10ms。

        for (i=0; !abort && i<100; ++i) {
            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);//延时10000us,也就是10ms
        }

        printf("/b/b/b%2d ", bootdelay);//打印当前剩余时间

    }

    putc('/n');
    return abort;//返回结果:1-停止引导,进入命令行; 0-引导内核。
}

可以看到uboot延时的单位是秒,如果想提高延时的精度,比如想进行10ms级的延时,将udelay(10000)改为udelay(100)就可以了

关于命令的知识点

命令结构体:  在include/command.h中定义如下

/*
 * Monitor Command Table
 */

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)   简短的帮助信息   */
#ifdef    CFG_LONGHELP
    char        *help;        /* Help  message    (long)    较详细的帮助信息  */
#endif
#ifdef CONFIG_AUTO_COMPLETE
    /* do auto completion on the arguments   自动补全参数 */
    int        (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

小提示:内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/smdk2410.h中定义了CFG_LONGHELP宏,

                则在U-Boot中使用help命令查看某个命令的帮助信息时将显示 usage和help字段的内容,否则就只显示usage字段的内容。

习惯上通用命令源代码放在common目录下,并且习惯以cmd_<命令名>.c”为文件名。

接着看如何定义命令:

         //定义section属性的结构体,编译的时候会单独生成一个名为.u_boot_cmd的section段,而凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,

         并且即使该变量没有在代码中显式的使用编译器也不产生警告信息)

        在 include/command.h  中有定义  #define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))


    1.    每个命令就用下面这个宏来定义,并加进系统命令表中

#ifdef  CFG_LONGHELP

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

                                                                     ##”#”都是预编译操作符,##”有字符串连接的功能,#”表示后面紧接着的是一个字符串。它定义一个command变量,并把它放入".u_boot_cmd"节中*/

#else       /* no long help info */

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

#endif     /* CFG_LONGHELP */

定义命令结构体变量,如:
    U_BOOT_CMD(
         dcache,   2,   1,     do_dcache,
         "dcache  - enable or disable data cache/n",
         "[on, off]/n"
         "    - enable or disable data (writethrough) cache/n"
        );
 其实就是定义了一个cmd_tbl_t类型的结构体变量,这个结构体变量名为__u_boot_cmd_dcache。
    其中变量的五个域初始化为括号的内容。分别指明了命令名,参数个数,重复数,执行命令的函数,命令提示。
    每个命令都对应这样一个变量,同时这个结构体变量的section属性为.u_boot_cmd.也就是说每个变量编译结束
    在目标文件中都会有一个.u_boot_cmd的section.一个section是连接时的一个输入段,如.text,.bss,.data等都是section名。
    最后由链接程序把所有的.u_boot_cmd段连接在一起,这样就组成了一个命令结构体数组。
    u-boot.lds中相应脚本如下:
      . = .;
      __u_boot_cmd_start = .;
      .u_boot_cmd : { *(.u_boot_cmd) }
      __u_boot_cmd_end = .;
    可以看到所有的命令结构体变量集中在__u_boot_cmd_start开始到__u_boot_cmd_end结束的连续地址范围内,
    这样形成一个cmd_tbl_t类型的数组,run_command函数就是在这个数组中查找命令的。

 2. 实现命令处理函数。

   命令处理函数的格式:
   void function (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

 

3. 将自己添加的命令文件添加到编译过程中

仿照uboot的格式: CFG_CMD_* 命令选项位标志。在include/cmd_confdefs.h 中定义。
     每个板子的配置文件(如include/config/smdk2410.h)中都可以定义u-boot
     需要的命令,如果要添加一个命令,必须添加相应的命令选项。如下:
      #define CONFIG_COMMANDS /
    (CONFIG_CMD_DFL  | /
    CFG_CMD_CACHE  | /
    /*CFG_CMD_NAND  |*/ /
    /*CFG_CMD_EEPROM |*/ /
    /*CFG_CMD_I2C  |*/ /
    /*CFG_CMD_USB  |*/ /
    CFG_CMD_REGINFO  | /
    CFG_CMD_DATE  | /
    CFG_CMD_ELF)
    定义这个选项主要是为了编译命令需要的源文件,大部分命令都在common文件夹下对应一个源文件
    cmd_*.c ,如cmd_cache.c实现cache命令。 文件开头就有一行编译条件:
    #if(CONFIG_COMMANDS&CFG_CMD_CACHE)
    也就是说,如果配置头文件中CONFIG_COMMANDS不添加相应命令的选项,这里就不会被编译。

   我感觉这里还是要再makefile文件中添加.o文件名

也可以直接将自己添加的文件,无条件编译

做法: 在common/Makefile中加入如下代码:

         COBJS   = main.o ACEX1K.o altera.o bedbug.o circbuf.o \

          cmd_ace.o cmd_autoscript.o cmd_<命令名>.o \

         重新编译下载U-Boot就可以使用添加的命令

至此命令的知识点基本完了

抱歉!评论已关闭.