S5PV210-uboot解析(四)-main_loop函数分析

main_loop是在uboot启动最后阶段进行命令解析、引导内核的函数,在common/main.c中。

#include 
#include 
#include 
#ifdef CONFIG_MODEM_SUPPORT
#include       /* for free() prototype */
#endif
 
#ifdef CFG_HUSH_PARSER
#include 
#endif
 
#include 
#include 

这里面的command.h头文件中

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
};
 
typedef   struct   cmd_tbl_s  cmd_tbl_t;

这个数据结构就是用来存放uboot中的命令的结构体。 repeatable 是在uboot中在没有输入命令时自动重复上次的命令。

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

#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}

这个宏定义就是用来定义命令的结构体实例, Struct_Section 这个宏的作用就是添加属性,将这个结构体放到自定义段名为 .u_boot_cmd 的段中。unused 表示表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。

在uboot中,命令集的存放既不采用数组,也不采用链表,而是利用链接脚本,将所有段名为 .u_boot_cmd 的结构体全部放在一起,形成数组的效果。这种命令集的方式便于添加命令而不需要改动其他地方。

main_loop 函数前面,主要是获取 "bootdelay" "bootcmd" ,来实现倒计时后引导内核启动。代码如下:

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

这里首先从环境变量中获取 bootdelay ,其次再从默认配置的宏定义中获取。

s =  getenv   ( "bootcmd" );

之后从环境变量中获取 bootcmd ,环境变量的默认配置在 uboot/common/environment.c ,bootcmd宏定义如下:
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#define CONFIG_BOOTCOMMAND "movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000"
这里查找的时候需要注意文件名,x210有iNand和Nand两个版本,所以有两个头文件对应两种配置,用SI查找的时候会发现两种配置的宏都是有效的,实际上两个宏分别定义在两个文件,另一个宏是被注释掉了的。
从宏定义可以看到之后会执行三个命令,而引导内核启动是最后一个 bootm 命令。
 
     debug ( "### main_loop: bootcmd=\"%s\"\n" , s ? s :  "" );
 
     if   (bootdelay >= 0 && s && !abortboot (bootdelay)) {
#ifdef CONFIG_AUTOBOOT_KEYED
         int   prev = disable_ctrlc(1); /* disable Control C checking */
#endif
 
#ifndef  CFG_HUSH_PARSER
        run_command (s, 0);
#else
        parse_string_outer(s, FLAG_PARSE_SEMICOLON |
                    FLAG_EXIT_FROM_LOOP);
#endif
 
#ifdef CONFIG_AUTOBOOT_KEYED
        disable_ctrlc(prev);    /* restore Control C checking */
#endif
    }

abortboot函数中就进行了倒计时,并且只检测按键,如果没有按键按下就会进入到后面的 run_command (s, 0);语句。
在这里执行的是 bootm 命令,最后会跳转到 do_bootm函数去引导内核。


关于在uboot的命令行中的运行则主要看这一段:

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

从命令行中读取命令,实际调用的是 readline_into_buffer 这个函数。 CFG_PROMPT宏被定义为 "x210 # ",就是命令行前的提示。 readline_into_buffer 函数又是在 cread_line 函数中进行对控制台缓冲的处理。
 
         flag = 0; /* assume no special flags for now */
         if   (len > 0)
             strcpy   (lastcommand, console_buffer);
         else   if   (len == 0)
             flag |= CMD_FLAG_REPEAT;
 
判断读取到的命令长度,如果>0就将命令复制到lastcommand中。如果过等于0就令 flag |= CMD_FLAG_REPEAT。

         if   (len == -1)
             puts   ( "\n" );
         else
             rc = run_command (lastcommand, flag);
 
在common/main.c 中可以看到注释说明
* Return: number of read characters
* -1 if break
* -2 if timed out
如果len不等于-1,那么在之前的if语句中应该是len>0的,就执行   rc = run_command (lastcommand, flag);
run_command 就是用来执行命令的函数

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


main_loop函数中主要是对命令的获取,而 run_command则是对命令的解析和执行。下面对 run_command 函数分析


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);
 
这是在判断命令为空或命令超出最大字符限制。之后将命令复制到cmdbuf中。

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

将一个解析出来的命令给 token

         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

解析出一个命令,将 ';'改成 '\0',然后str指向下一个字符继续解析。

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

用来展开 token中(即解析出来的第一个命令)的宏定义。
 
         /* Extract arguments */
         if   ((argc = parse_line (finaltoken, argv)) == 0) {
             rc = -1;  /* no command at all */
             continue ;
         }

提取参数,parse_line 函数用来提取参数个数。
 
         /* 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 ;
         }
 
find_cmd 用来查找命令,里面还进行了进一步的关于 '.'的解析(比如 md.w 0),之后从段地址 __u_boot_cmd_start 开始到 __u_boot_cmd_end 内循环比较命令名称和命令长度,找到命令后返回命令结构体的地址。

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

这里检查命令的接受的参数个数是否超过命令结构体内的 maxargs

#if defined(CONFIG_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
 
         /* OK - call function to do the command */
         if   ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
             rc = -1;
         }
 
这里执行命令,调用命令结构体内的函数,名称一般是 do_xxx ,大部分常用的命令定义在uboot/common/common.c 添加的函数也在同一个文件夹,以 cmd_xx.c 的方式命名。

         repeatable &= cmdtp->repeatable;
 
         /* Did the user stop this? */
         if   (had_ctrlc ())
             return   -1; /* if stopped then not repeatable */
     }
 
     return   rc ? rc : repeatable;
}

main_loop 函数中,首先是获取环境变量中的 bootdelay bootcmd ,然后倒计时并引导内核,如果中间有串口输入(按键按下)则会进入到命令行中,在命令行中可以执行对应的 uboot 的命令。

你可能感兴趣的:(uboot)