u-boot的命令详解

进入U-BOOT的控制界面后,可以运行各种命令,比如下载文件到内存,擦除、读写FLASH,运行内存、NOR FLASH中的程序,查看、修改、比较内存中的数据等。U-BOOT的命令可以说是U-BOOT学习的核心,启动kernel就是通过U-BOOT命令来启动的。
本文记录了关于U-BOOT命令的学习和分析。U-BOOT命令的学习大致可以分为三个部分:U-BOOT命令的存储格式、命令的检索和命令的执行。

一、U-BOOT命令的存储格式

U-BOOT是一个结构体,由6个成员组成。
@name: 命令的名称
@maxargs:命令参数的最大个数
@repeatable:命令是否允许repeat
@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  CFG_LONGHELP
    char        *help;      /* Help  message    (long)  */
#endif
};

为方便使用,定义了一个宏,这个宏用来定义U-BOOT命令。

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

以do_bootm命令为例,其定义方法为:

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

经编译器预编译后,do_bootm的命令成为:

cmd_tbl_t __u_boot_cmd_bootm 
__attribute__ ((unused,section (".u_boot_cmd")))= 
{bootm, CFG_MAXARGS, 1, do_bootm, "boom...\n", "[addr...\n"}

__u_boot_cmd_bootm结构体的属性为
attribute ((unused,section (“.u_boot_cmd”)))
使得U-BOOT所有的命令都放到一段名为”.u_boot_cmd”存储空间中。

二、U-BOOT命令的检索

下图是命令检索的过程。
这里写图片描述

命令检索函数find_cmd定义在common/command.c中。
参数为需要检索的命令,若可以找到返回该命令的地址,否则返回NULL。

cmd_tbl_t *find_cmd (const char *cmd)
{
    cmd_tbl_t *cmdtp;
    // 指针指向第一条命令的首地址
    cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;    /*Init value */
    const char *p;
    int len;
    int n_found = 0;

    /*
     * Some commands allow length modifiers (like "cp.b");
     * compare command name only until first dot.
     */
     // 计算命令的长度
    len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);

    // 从u-boot命令存储的首地址__u_boot_cmd_start至末尾地址__u_boot_cmd_end开始检索命令
    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;   /* full match */

            cmdtp_temp = cmdtp; /* abbreviated command ? */
            n_found++;
        }
    }
    if (n_found == 1) {         /* exactly one match */
        return cmdtp_temp;
    }

    return NULL;    /* not found or ambiguous command */
}

三、U-BOOT命令的执行

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;

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

    // 若为空指令则退出
    if (!cmd || !*cmd) {
        return -1;  /* empty command */
    }

    // 若指令超长(大于等于CFG_CBSIZE),则退出
    if (strlen(cmd) >= CFG_CBSIZE) {
        puts ("## Command too long!\n");
        return -1;
    }

    // 拷贝命令到cmdbuf
    strcpy (cmdbuf, cmd);

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

    while (*str) {

        /*
         * Find separator, or string end
         * Allow simple escape of ';' by writing "\;"
         */
         // 找出当前str所指向的命令的结尾,忽略注释部分('xxxxxxx')和(\;),
         // sep指向末尾 ';' 或 '\0'
        for (inquotes = 0, sep = str; *sep; sep++) {
            // 用inquotes表示当前索引是否在注释部分。
            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;
        // sep指向的内容不是'\0',表明还有下一条命令。
        if (*sep) {
            str = sep + 1;  /* start of command for next pass */
            *sep = '\0';
        }
        // sep为'\0',表明命令结束。
        else
            str = sep;  /* no more commands for next pass */


        /* find macros in this token and replace them */
        // 寻找在token中的宏,并用宏代表的值替换,如token为test$(macro),
        // macro的值为MACRO,则输出结果finaltoken为testMACRO
        process_macros (token, finaltoken);

        /* Extract arguments */
        // 提取命令,将finaltoken表示的命令放到argv指向的数组中,如finaltoken表示的命令为cmd para1 para2,
        // 则argv[0]=cmd, argv[1]=para1, argv[2]=para2,返回值为分解命令的个数3. 
        if ((argc = parse_line (finaltoken, argv)) == 0) {
            rc = -1;    /* no command at all */
            continue;
        }

        /* Look up command in command table */
        // 在u-boot命令表中查找argv[0]中的命令,若找到则将地址赋给cmdtp,否则返回NULL
        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;
        }

        // 执行该命令。
        /* 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;
}

parse_line()函数用来解析命令
@char *line,是命令行字符串的首地址,命令行的命令或参数以’ ‘或’\t’分开,以’\0’为结尾。
@char *argv[],存放解析后的命令
@return value: number of argv element.
例如. *line = “bootm 0x30007FC0”
则argv[0] = “bootm”
argv[1] = “0x30007FC0”
return value is 2

int parse_line (char *line, char *argv[])
{
    int nargs = 0;

    while (nargs < CFG_MAXARGS) {
        /* skip any white space */
        // 忽略命令行中的空格和制表符
        while ((*line == ' ') || (*line == '\t')) {
            ++line;
        }

        // 若没有其它的参数了。
        if (*line == '\0') {    /* end of line, no more args    */
            argv[nargs] = NULL;
#ifdef DEBUG_PARSER
        printf ("parse_line: nargs=%d\n", nargs);
#endif
            return (nargs);
        }
        // 将查找到的命令的首地址赋给argv。
        argv[nargs++] = line;   /* begin of argument string */

        /* find end of string */
        // 找到当前参数的末尾,末尾有三种情况:'\0', ' ' or '\t'
        while (*line && (*line != ' ') && (*line != '\t')) {
            ++line;
        }

        // 若再没有其他的参数了
        if (*line == '\0') {    /* end of line, no more args    */
            argv[nargs] = NULL;
#ifdef DEBUG_PARSER
        printf ("parse_line: nargs=%d\n", nargs);
#endif
            return (nargs);
        }

        // 若还有其他的参数,则先结束当前的参数。
        *line++ = '\0';     /* terminate current arg     */
    }

    printf ("** Too many args (max. %d) **\n", CFG_MAXARGS);

#ifdef DEBUG_PARSER
    printf ("parse_line: nargs=%d\n", nargs);
#endif
    return (nargs);
}

四、总结

U-BOOT命令是结构体形式的,通过关键字attribute ((unused,section (“.u_boot_cmd”)))将所有定义的命令都存放在一块连续的存储空间中,这种存储方式的好处是减少了命令检索的时间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值