U_BOOT_CMD

http://www.360doc.com/content/11/0826/12/7473909_143404764.shtml

uboot命令解释与运行分析

 

 

http://blog.chinaunix.net/uid-20665047-id-3485444.html

添加命令 U_BOOT_CMD

 

http://blog.sina.com.cn/s/blog_500bd63c0101bnvk.html

 

http://blog.sina.com.cn/s/blog_6947718c010171hg.html

 

uboot第三阶段启动内核等命令实现

思路是命令是一个结构体,结构体中有name functionname表示command_namefunction表示如何执行和处理这个command_name。具体的结构体形式是这样的:

struct cmd_tbl_s {
 char  *name;  
 int  maxargs; 
 int  repeatable; 
这里指的是执行完命令之后如果再次摁下回车能不能自动执行。
     
 int  (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
这个是实现函数的函数指针
 char  *usage;  指的是短的帮助信息,例如输入help命令后,出现的所有命令的简单的介绍
#ifdef CFG_LONGHELP
 char  *help;  
指的是长的帮助信息,例如输入help md就会显示出来md命令详细的信息。
#endif
#ifdef CONFIG_AUTO_COMPLETE
 
 int  (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

下面分析run_command如何实现:

run_command函数是在command/main.c

 

---------------------------run_command函数开始-------------------------------------

int run_command (const char *cmd, int flag)
{
 cmd_tbl_t *cmdtp;
 char cmdbuf[CFG_CBSIZE]; 
 char *token;   
 char *sep;   
 char finaltoken[CFG_CBSIZE];
 char *str = cmdbuf;
 char *argv[CFG_MAXARGS + 1]; 
 int argc, inquotes;
 int repeatable = 1;
 int rc = 0;

#ifdef DEBUG_PARSER
 printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
 puts (cmd ? cmd : "NULL"); 
 puts ("\"\n");
#endif

 clear_ctrlc();  

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

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

 strcpy (cmdbuf, cmd);

 

#ifdef DEBUG_PARSER
 printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
 while (*str) {
这里实现把命令解析出来,一个一个提取出来

  
  for (inquotes = 0, sep = str; *sep; sep++) {
   if ((*sep=='\'') &&
       (*(sep-1) != '\\'))
    inquotes=!inquotes;

   if (!inquotes &&
       (*sep == ';') && 
这里分号表示如果有两个命令的话用分号隔开,也就是说处理了输入多个命令的情况。
       ( sep != str) && 
       (*(sep-1) != '\\')) 
    break;
  }

  
  token = str;
  if (*sep) {
   str = sep + 1; 
   *sep = '\0';
  }
  else
   str = sep; 
#ifdef DEBUG_PARSER
  printf ("token: \"%s\"\n", token);
#endif

  
  process_macros (token, finaltoken);
处理宏,例如当我们用网卡或者usb下载文件到内存中时候会临时生成一些环境变量或者宏,

  
  if ((argc = parse_line (finaltoken, argv)) == 0) {
解析输入的命令文字,比如md.w 0,成为一个字符数组如argc[0]="md.w" argc[1]="0",英文是extract arguments,提取参数。
   rc = -1; 
   continue;
  }

  
  if ((cmdtp = find_cmd(argv[0])) == NULL) {
这里是查找命令,一般argv[0]中存放的是命令,后面的几个是命令的参数,
   printf ("Unknown command '%s' - try 'help'\n", argv[0]);
   rc = -1; 
   continue;
  }

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

#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
  
  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 

  
  if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
   rc = -1;
  }

  repeatable &= cmdtp->repeatable;

  
  if (had_ctrlc ())
   return 0; 
 }

 return rc ? rc : repeatable;
}

 

 

---------------------------run_command函数结束-------------------------------------

 

 

 

---------------------------find_cmd函数开始---------------------------------------

 * find command table entry for a command
 */
cmd_tbl_t *find_cmd (const char *cmd)
{
 cmd_tbl_t *cmdtp;
 cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; 
 const char *p;
 int len;
 int n_found = 0;

 
 len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);

 for (cmdtp = &__u_boot_cmd_start;这两个参数在文件中是没有的,但是查看链接脚本有这样几句话_u_boot_cmd_start = .;

.u_boot_cmd:{ *(.u_boot_cmd) }

_u_boot_cmd_end = .;

是一个地址,这个循环实现从命令开始存放地址到结束地址遍历命令进行查找。所有的命令全都会被集中到u_boot_cmd这个区域里面来,具体的实现办法是:#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))这句话,同理可以找到如何将所有文件的代码段放到.text的,关键的是结构体cmd_tbl_t,也即cmd_tablet表示结构体,有这个属性attribute

      cmdtp != &__u_boot_cmd_end;
      cmdtp++) {
  if (strncmp (cmd, cmdtp->name, len) == 0) {
   if (len == strlen (cmdtp->name))
    return cmdtp; 

   cmdtp_temp = cmdtp; 
   n_found++;
  }
 }
 if (n_found == 1) {   
  return cmdtp_temp;
 }

 return NULL; 
}

 

---------------------------find_cmd函数开始---------------------------------------

例如U_BOOT_CMD命令的定义

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"
#ifdef CONFIG_OF_FLAT_TREE
 "\tWhen booting a Linux kernel which requires a flat device-tree\n"
 "\ta third argument is required which is the address of the of the\n"
 "\tdevice-tree blob. To boot that kernel without an initrd image,\n"
 "\tuse a '-' for the second argument. If you do not pass a third\n"
 "\ta bd_info struct will be passed instead\n"
#endif
);
按照结构体中去拆开就是 bootmname,最大参数个数,可以repeat,具体实现函数指针,简要帮助信息,详细帮助信息。

 

 

uboot下用do_run()函数来执行U_BOOT_CMD定义的命令

今天研究了一下uboot中怎么用do_run()函数来执行定义好的一些命令,发现其实也挺简单的,关键是得把do_run函数的几个参数吃透,这几个参数都是由uboot commmod 的cmd_tbl_s结构体决定的,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 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    CONFIG_SYS_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
}

        对比两者后很容易就明白了怎样去定义一个uboot下的命令。

        接下来看看do_run函数的定义(在../boot/common/main.c下):

#if defined(CONFIG_CMD_RUN)
int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
    int i;

    if (argc < 2) {                                 //
参数个数小于2则提示重新输入
        cmd_usage(cmdtp);
        return 1;
    }

    for (i=1; i<argc; ++i) {
        char *arg;

        if ((arg = getenv (argv[i])) == NULL) {
            printf ("## Error: \"%s\" not defined\n", argv[i]);
            return 1;
        }
#ifndef CONFIG_SYS_HUSH_PARSER
        
    
        if (run_command (arg, flag) == -1){     //
调用run_command函数,先查找命令,然后执行命令
            return 1;
        }
#else
        if (parse_string_outer(arg,
            FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0){
            return 1;
        }
#endif
    }
    return 0;
}

      其中第一个参数cmd_tbl_t * cmdtp为Command Table,即命令表参数,该结构体的定义在common.h下,一般设为NULL。

 

      第二个参数int flag为Command Flag,用来标志命令来自哪里,跟它相关的有两个宏:

#define CMD_FLAG_REPEAT        0x0001    /* repeat last command        */
#define CMD_FLAG_BOOTD        0x0002    /* command is from bootd    */

       它被传给run_command (const char *cmd, int flag),一般设为0,表示bootd,设为1则表示repeat(递归式)。

        第三个int argc表示输入参数的个数,小于2则提示重新输入。第三个char *argv[]是输入命令字符的指针,用来传递定义好的指针数组。下面通过一个简单的例子来说明怎么使用do_run()函数,yaffs2ram的作用是实现从yaffs文件系统中拷贝logo.bmp到指定的RAM地址上:

int yaffs2ram(void)

{

  char *argv[5];   //定义5个指针数组,用来存放各个字符命令的指针

  char buf[100];

  char *name_buf = "/logo/logo.bmp";

  int ret;

   if(!yaffs_file_exist(name_buf))//检查文件系统中是否存在logo.bmp
    {
        dprintf_line("%s NOT Exist!",name_buf);
        return -1;
    }

 

//yrdm  /logo/logo.bmp  0x8000_0000命令传给bufloadaddr为环境变量)

    sprintf(buf,"yrdm %s ${loadaddr}",name_buf);

    setenv("upgradetemp", buf);//设置临时环境变量upgradetemp的值
    argv[0] = "run";
    argv[1] = "upgradetemp";
    argv[2] = NULL;
    ret = do_run(NULL, 0, 2, argv); 
  //
运行yrdm  /logo/logo.bmp  0x8000_0000命令,把logo.bmp拷贝到0x8000_000地址上

    if ( ret == 1)

       {

         printf("do_run error!"

        return -1;

         }

    setenv("upgradetemp", NULL); //清空临时环境变量的值

return 0;

}

 

注:由于do_yrdm函数规定输入命令的个数必须为3(如下所示),所以我们加了一个argv[2] = NULL命令,该命令什么都不做。

U_BOOT_CMD(
    yrdm,   3,  0,  do_yrdm,
    "read file to memory from yaffs",
    "filename offset"
);

 

 

 

 

uboot命令解释与运行分析

题记: 省略200字

这一回来分析一下uboot中命令行的解释, 所以我们直接从main_loop开始分析.

1. 从汇编阶段进入c阶段的第一个函数是start_xxx, 如/lib_unicore/board.c中的start_unicoreboot. 前半部分调用了若干初始化函数来进行部分硬件的初始化, 并设置一下环境. 这里不是我们本回要讨论的所以一一跳过. 在start_xxx的最后调用了main_loop(), 而且还是被一个死循环死死圈住了;

for (;;) {
  main_loop ();
 }


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

      //从环境变量里找bootdelay 
 s = getenv ("bootdelay");
      //如果环境变量中有定义, 将delay数值赋值给变量bootdelay, 否则赋值CONFIG_BOOTDELAY.
 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
      //获取bootcmd
 s = getenv ("bootcmd");
 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
      //如果bootdelay和bootcmd都存在, 那么在abortboot()中延时bootdelay, 调用run_command执行在bootcmd中定义的默认命令

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


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

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

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

  flag = 0;
  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()来处理的.

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


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

      //str就是指向cmdbuf的指针, 其实这些东西都是针对的刚才那行命令
        while (*str) {
            //注释很清楚, 找到;作为命令结束符, 因为多个命令可以一次输入, 并以;分割. 忽略'\;'
  
  
for (inquotes = 0, sep = str; *sep; sep++) {
   if ((*sep=='\'') &&
       (*(sep-1) != '\\'))
    inquotes=!inquotes;
   if (!inquotes &&
       (*sep == ';') &&
       ( sep != str) &&
       (*(sep-1) != '\\'))
    break;
  }
            //如果上面for循环找到一条以';'结束的命令, 那么sep指向命令末尾

  
  token = str;
  if (*sep) {
   str = sep + 1;
   *sep = '\0';
  }
  else
   str = sep;
  
  process_macros
(token, finaltoken);
  
  
if ((argc = parse_line (finaltoken, argv)) == 0) {
   rc = -1;
   continue;
  }
  
  
if ((cmdtp = find_cmd(argv[0])) == NULL) {
   printf ("Unknown command '%s' - try 'help'\n", argv[0]);
   rc = -1;
   continue;
  }


4.就此打断一下, 我们要分析一下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是命令的结构体类型, 这里不罗列了,成员包括命令名字, 参数的最大个数, 使用说明等等. Struct_Section定义为

      #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
      //所以, U_BOOT_CMD将会创建一个类型为cmd_tbl_t的结构体对象, 名字为__u_boot_cmd_name, 存储在__.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}

 //这个for循环是find_cmd()的核心, __u_boot_cmd_start在lds连接脚本中, 指定的是在__.u_boot_cmd的开始地址, __u_boot_cmd_end就是相对应的结束地址. 注意:for中的cmdtp++每次加的不是1二是sizeof(cmd_tbl_t). 所以在这个循环中将会遍历在__.u_boot_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;
   cmdtp_temp = cmdtp;
   n_found++;
  }
 }


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


  if (argc > cmdtp->maxargs) {
   printf ("Usage:\n%s\n", cmdtp->usage);
   rc = -1;
   continue;
  }
  //这是啥, 我不管它了, 掠过这条小溪
#if defined(CONFIG_CMD_BOOTD)
  
  
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!

  
  
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
   rc = -1;
  }
  repeatable &= cmdtp->repeatable;
  
  
if (had_ctrlc ())
   return -1;
 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值