UBoot命令解析与执行流程

本文为CP根据网上内容以及U-Boot 2014.4版本进行整理而成。

原文地址为 http://blog.chinaunix.net/uid-8867796-id-358806.html

———————————————————————————————————————————————————

1:uboot的命令解析执行代码在main_loop()函数中实现,其向上的调用层次是:main_loop()//common\Main.cßboard_init_r()//arch\arm\lib\board.cßctr0.S//arch\arm\libßstart.S//arch\arm\cpu\arm7.

 

2. 现在我们已经进入了这个圈套那么只能往里钻了. common/main.c文件中的main_loop().

Main_loop会进入process_boot_delay()来处理boot延时。在process_boot_delay()内有如下操作:

 // 宏定义CONFIG_BOOTCOUNT_LIMIT猜测是限制启动次数的功能

#ifdef CONFIG_BOOTCOUNT_LIMIT 

   bootcount = bootcount_load(); 

 

//从环境变量里找bootdelay 
 s = getenv ("bootdelay"); 
      //如果环境变量中有定义, delay数值赋值给变量bootdelay, 否则赋值CONFIG_BOOTDELAY.
 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
    

//获取bootcmd 以作为默认启动参数开始启动
 s = getenv ("bootcmd");

//如果在延时时间内没有按键进入,就会执行s命令

run_command_list(s, -1, 0);


上面代码主要是对自启动部分的描述, 其中命令执行部分是在run_command中进行的, 这个等在后文分析. 如果我们没有bootcmd或者在延时中被打断, 那么代码会继续向下执行
 

//下面for循环是uboot处理终端命令的主循环, 这时等待我们从键盘中输入一行命令, 真正的人机交互从这里开始

for (;;) {
            // readline中首先先显示CFG_PROMPT定义的字符串, 然后等待键盘输入
            // 每次从终端读入一个字符, 先判断是否是正常字符(ctrl+c, 回车等属于非正常字符)
            // 对与正常字符那么将其存入console_uffer, 并在终端回显
  len = readline (CFG_PROMPT);
            // 这时我们已经收到屏幕上的一行输入, 而且命令已经存console_uffer

  flag = 0; /* assume no special flags for now */
  if (len > 0)
   strcpy (lastcommand, console_buffer);
  else if (len == 0)
   flag |= CMD_FLAG_REPEAT;
            // 命令从console_buffer搬运到lastcommand, 乾坤大挪移
  rc = run_command (lastcommand, flag);


3.read_line()读取到命令行后会调用common/main.c文件中的run_command(). 现在是分析run_command()的时候了, 不管是从环境变量还是终端获得命令, 都是由run_command()来处理的.

run_command()函数实际执行的是

builtin_run_command(cmd, flag)函数

//下面if语句判断命令是否太长, 还是避免有些变态的家伙输入了超过CFG_CBSIZE个字符的命令
  if (strlen(cmd) >= CFG_CBSIZE) {
  puts ("## Command too long!\n");
  return -1;
 }
      //这个世界到处体现了自私, 对命令又进行了一次拷贝, 安全吗?
      strcpy (cmdbuf, cmd);

下面要进入处理cmdbuf的循环中了, 长征马上开始

      //str就是指向cmdbuf的指针, 其实这些东西都是针对的刚才那行命令
        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;
  }
            //如果上面for循环找到一条以';'结束的命令, 那么sep指向命令末尾

  /*
   * Limit the token to data between separators
   */

  token = str;
  if (*sep) {
   str = sep + 1; /* str为下一条命令开始start of command for next pass */
   *sep = '\0'; /* 作为token的结束 */
  }
  else
   str = sep; /* 只有一条命令 */
  /* 找到命令中的宏兵替换宏, , 现在命令已经转移到finaltoken find macros in this token and replace them */
  process_macros (token, finaltoken);
  /* 将命令行中的关键词取出放入argv, 注意, 命令行被分解到argv数组中, 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;
  }


4.
上述命令获取完成之后,会进入命令执行。其函数为cmd_process(flag,argc, argv, &repeatable, NULL)。首要会执行find_cmd.find_cmd()在.u_boot_cmd段中寻找该命令的cmd_tbl_t结构, 找到后返回该结构. 该命令的结构是通过定义在include/command.h中的宏定义U_BOOT_CMD登记进.u_boot_cmd段中的.

//先研究一下这个神奇的U_BOOT_CMD, 定义如下, 其中cmd_tbl_t是命令的结构体类型, 这里不罗列了,成员包括命令名字, 参数的最大个数, 使用说明等等. U_BOOT_CMD定义为

     #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,    \

             _usage, _help, _comp)           \

      { #_name, _maxargs, _rep, _cmd, _usage,          \

          _CMD_HELP(_help) _CMD_COMPLETE(_comp) }

#define U_BOOT_CMD_MKENT(_name, _maxargs, _rep, _cmd, _usage, _help) \

   U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,      \

                 _usage, _help, NULL)

#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \

   ll_entry_declare(cmd_tbl_t, _name, cmd) =        \

      U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,  \

                    _usage, _help, _comp);

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)    \

   U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

cmd_tbl_t *find_cmd (const char *cmd)函数会找到命令区的入口以及命令总数,并最终调用

cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)

找到指定命令的入口
 //这个for循环是find_cmd()的核心, _table指定的是在__.u_boot_cmd的开始地址, table + table_len就是相对应的结束地址. 注意:for中的cmdtp++每次加的不是1二是sizeof(cmd_tbl_t). 所以在这个循环中将会遍历在__.u_boot_cmd段中的所有命令, 并查找与本次命令向对应的那个命令的结构体

 for (cmdtp = table;
      cmdtp != table + table_len;
      cmdtp++) {
  if (strncmp (cmd, cmdtp->name, len) == 0) {
   if (len == strlen (cmdtp->name))
    return cmdtp; /* 命令完全匹配, 返回命令结构体 full match */
   cmdtp_temp = cmdtp; /* 部分匹配, 后面会返回cmdtp_temp, abbreviated command ? */
   n_found++;
  }
 }


5.
刚才我们在长征的半路翻越了一座雪山, 现在继续回到while循环中

/* 雪山之后就是平坦的草原, 是不是觉得下面这个判断很简单, found - check max args */
  if (argc > cmdtp->maxargs) {
   printf ("Usage:\n%s\n", cmdtp->usage);
   rc = -1;
   continue;
  }
  //这是啥, 我不管它了, 掠过这条小溪
#if defined(CONFIG_CMD_BOOTD)
  /* avoid"bootd" recursion */
  if (cmdtp->cmd == do_bootd) {
   if (flag & CMD_FLAG_BOOTD) {
    puts ("'bootd' recursion detected\n");
    rc = -1;
    continue;
   } else {
    flag |= CMD_FLAG_BOOTD;
   }
  }
#endif
       //长征马上结束, 胜利就在眼前! 调用结构体中注册的cmd函数, 何时注册的呢? 上面不远处介绍的U_BOOT_CMD!

  /* OK - call functionto do the command */
  if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
   rc = -1;
  }
  repeatable &= cmdtp->repeatable;
  /* ctrl+c 会终止, Did the user stop this? */
  if (had_ctrlc ())
   return -1; /*if stopped then not repeatable */
 }


你可能感兴趣的:(UBoot命令解析与执行流程)