频道栏目
首页 > 资讯 > 其他 > 正文

uboot命令体系和命令的执行

16-10-21        来源:[db:作者]  
收藏   我要投稿

uboot命令体系

boot命令体系的实现代码在uboot/common/cmd_xxx.c中。有若干个.c文件和命令体系有关。(还有command.c main.c也是和命令有关的)。

每一个uboot的命令背后都对应一个函数。我们要找到每一个命令背后所对应的那个函数,而且要分析这个函数和这个命令是怎样对应起来的。

下面介绍一个uboot命令是如何实现的:

分析可能的管理方式
数组:结构体数组,数组中每一个结构体成员就是一个命令的所有信息。
链表:链表的每个节点data段就是一个命令结构体,所有的命令都放在一条链表上。这样就解决了数组方式的不灵活。坏处是需要额外的内存开销,然后各种算法(遍历、插入、删除等)需要一定复杂度的代码执行。

有第三种吗?uboot没有使用数组或者链表,而是使用了一种新的方式来实现这个功能。

一个uboot命令的实现:
/* 每一个命令都将对应这样一个结构体 */
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) */
    char    *help;      /* Help  message    (long)  */  
    /* do auto completion on the arguments */
    int     (*complete)(int argc, char *argv[], char last_char,int maxv, char *cmdv[]);   
}

typedef struct cmd_tbl_s    cmd_tbl_t;

/* 给命令结构体实例附加特定段属性 */
#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}

int do_version (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    printf ("\n%s\n", "Uboot-1.3.4");
    return 0;
}

U_BOOT_CMD(
    version,    1,      1,  do_version,
    "version - print monitor version\n",
    NULL
);

命令结构体cmd_tbl_t

name:命令名称,字符串格式。
maxargs:命令最多可以接收多少个参数
repeatable:指示这个命令是否可重复执行。重复执行是uboot命令行的一种工作机制,就是直接按回车则执行上一条执行的命令。
cmd:函数指针,命令对应的函数的函数指针,将来执行这个命令的函数时使用这个函数指针来调用。
usage:命令的短帮助信息。对命令的简单描述。
help:命令的长帮助信息。细节的帮助信息。
complete:函数指针,指向这个命令的自动补全的函数。

uboot的命令体系在工作时,一个命令对应一个cmd_tbl_t结构体的一个实例,然后uboot支持多少个命令,就需要多少个结构体实例。uboot的命令体系把这些结构体实例管理起来,当用户输入了一个命令时,uboot会去这些结构体实例中查找(查找方法和存储管理的方法有关)。如果找到则执行命令,如果未找到则提示命令未知。

U_BOOT_CMD
U_BOOT_CMD这个宏定义在uboot/common/command.h中。

U_BOOT_CMD(
    version,  1,  1,  do_version,
    "version - print monitor version\n",
    NULL
);

对U_BOOT_CMD进行宏展开,依次进行替换:

/* 名字使用##作为连字符 */
__u_boot_cmd_##name   —>  __u_boot_cmd_version  

/* 附加了用户自定义段属性.u_boot_cmd,以保证链接时将这些数据结构链接在一起排布。 */
Struct_Section   —>  __attribute__ ((unused,section (".u_boot_cmd")))

/* #name的作用就是将命令替换成字符串 */
{#name,  maxargs,  rep, cmd,   usage,  help}  —>  
{"version",   1, 1, do_version, "version - print monitor version\n", NULL}

最终结果为:
cmd_tbl_t __u_boot_cmd_version __attribute__ ((unused,section (".u_boot_cmd"))) = 
{"version", 1, 1, do_version, "version - print monitor version\n", NULL}

这个宏其实就是定义了一个命令对应的结构体变量,并给命令结构体实例附加特定段属性(用户自定义段),链接时将带有该段属性的内容链接在一起排列。

uboot重定位时将该段整体加载到DDR中。加载到DDR中的uboot镜像中带有特定段属性的这一段其实就是命令结构体的集合,有点像一个命令结构体数组。

命令的执行

uboot启动后进入命令行环境下,在此输入命令按回车结束,uboot会收取这个命令然后解析,然后执行。
在uboot启动的第二阶段,在初始化了所有该初始化的东西后,进入了一个死循环,死循环的循环体就是main_loop。main_loop函数就是一个获取命令、解析命令、执行命令的过程。其中run_command函数就是用来执行命令的函数。

int run_command (const char *cmd, int flag)
{
    cmd_tbl_t *cmdtp;

    while (*cmd) {
        /* Extract arguments */
        argc = parse_line (finaltoken, argv);

        /* Look up command in command table */
        cmdtp = find_cmd(argv[0]);

        /* OK - call function to do the command */
        (cmdtp->cmd) (cmdtp, flag, argc, argv) 
    }   
}
命令解析。对于命令”md 30000000 10”,parse_line函数将其解析成argv[0]=md, argv[1]=30000000,argv[2]=10; 命令集中查找命令。find_cmd(argv[0])函数去uboot的命令集合当中搜索有没有argv[0]这个命令。 执行命令。最后用函数指针的方式调用执行了对应函数。

其中find_cmd函数如下:


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;

    /* 不管这个命令中是否有点这个符号,都按点符号前面的命令长度计算 */
    len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - 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;   /* 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 */
}

find_cmd函数的任务是从当前uboot的命令集中查找是否有某个命令。如果找到则返回这个命令结构体的指针,如果未找到返回NULL。

段起始地址和结束地址(链接地址、定义在u-boot.lds中)决定了这些命令集的开始和结束地址。

函数的实现思路很简单,如果不考虑命令带点的情况(md.b, md.w这种)就更简单了。查找命令的思路其实就是for循环遍历数组的思路,不同的是数组的起始地址和结束地址是用地址值来给定的,数组中的元素个数是结构体变量类型。

uboot中增加自定义命令

在已有的c文件中直接添加命令

(1)在uboot/common/command.c中添加一个命令,叫:mycmd
(2)在已有的.c文件中添加命令比较简单,直接使用U_BOOT_CMD宏即可添加命令,给命令提供一个do_xxx的对应的函数这个命令就齐活了。
(3)添加完成后要重新编译工程(make distclean; make x210_sd_config; make),然后烧录新的uboot去运行即可体验新命令。
(4)还可以在函数中使用argc和argv来验证传参。

自建一个c文件并添加命令

(1)在uboot/common目录下新建一个命令文件,叫cmd_test.c(对应的命令名就叫test,对应的函数就叫do_test函数),然后在c文件中添加命令对应的U_BOOT_CMD宏和函数。注意头文件包含不要漏掉。
(2)在uboot/common/Makefile中添加上test.o,目的是让Make在编译时能否把cmd_test.c编译链接进去。
(3)重新编译烧录。重新编译步骤是:make distclean; make x210_sd_config; make

总结

uboot的命令体系本身稍微复杂,但是他写好之后就不用动了。后面在移植uboot时也不会去动uboot的命令体系,最多就是向uboot中去添加命令。

相关TAG标签
上一篇:大数据系列修炼:Scala课程101
下一篇:FBI及捷克警察逮捕对美国发动网络攻击的俄罗斯黑客
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站