uboot 命令解释机制及添加新命令的方法

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

Uboot中,通过terminal输入的命令最终都是通过调用run_command函数来解析并执行的。下面我们来分析一下该函数的功能是如何实现的。

首先,我们来分析一下run_command函数里面最重要的函数指针:cmd_tbl_t ,该结构体定义如下:

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;

 

*name :命令的名称,例如输入命令reboot,则该参数为”reboot”;

maxargs:最大参数个数;

repeatable:是否可重复,即执行完当前输入的命令后,在terminal按下回车键是否再一次重复执行该命令;

(*cmd)(struct cmd_tbl_s *, int, int, char *[]);:该命令对应的解析处理函数;

*usage:简要说明;

*help:详细帮助文档。

并且,该结构体被宏定义为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}

下面,我以bootm命令为例进行说明:

bootm命令是用于指定在内存的什么位置启动内核,例如bootm 0x30007FC0是在内存地址0x30007FC0启动内核。

对于bootm命令,在/common/cmd_bootm.c中定义如下:

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  

); 

根据宏定义,该结构体可以展开为

cmd_tbl_t __u_boot_cmd_bootm Struct_Section = { 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"}

而Struct_Section的宏定义如下:

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

在将Struct_Section展开后得到的结果是:

cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".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"}

其意思是,定义cmd_tbl_t的结构体__u_boot_cmd_bootm,并将其段属性强制转换为“.u_boot_cmd”类型。

.u_boot_cmd类型是uboot命令中的标志属性,在链接时会将此类型的函数统一链接在一起,在u-boot.lds文件中有说明:

SECTIONS  
{  
    . = 0x00000000;  
  
    . = ALIGN(4);  
    .text      :  
    {  
      cpu/arm920t/start.o   (.text)  
      *(.text)  
    }  
  
    . = ALIGN(4);  
    .rodata : { *(.rodata) }  
  
    . = ALIGN(4);  
    .data : { *(.data) }  
  
    . = ALIGN(4);  
    .got : { *(.got) }  
  
    . = .;  
    __u_boot_cmd_start = .;  
    .u_boot_cmd : { *(.u_boot_cmd) }  
    __u_boot_cmd_end = .;  
  
    . = ALIGN(4);  
    __bss_start = .;  
    .bss : { *(.bss) }  
    _end = .;  
}

根据以上分析,要在U-Boot源码中添加一个命令的方法其实很简单,只需要以下几个步骤:

  1. 建立命令功能函数;
  2. 创建U_BOOT_CMD结构体;
  3. 修改Makefile将该命令编译进U-Boot的bin文件中。

下面以建立hello命令为例,实现在terminal执行hello命令即可打印HelloWorld的功能。

首先,我们在/common/目录下新建cmd_hello.c文件,然后编写do_hello函数,并定义U_BOOT_CMD结构体,如下:

int do_hello(int argc, char const *argv[])  
{  
    printf("HelloWorld\n");  
    return 0;  
}  
  
U_BOOT_CMD   
{  
    hello,  CFG_MAXARGS,    1,  do_hello,  
    "hello -just for test uboot command",  
    "hello -hello help.................."  
}  

接着,修改common/Makefile文件,在COBJS新增编译目标文件cmd_hello.o

最后,重新编译Uboot即可将hello命令加入到Uboot中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值