其中U_BOOT_CMD命令格式如下:

U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")

 各个参数的意义如下:

  1. name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
  2. maxargs:命令的最大参数个数
  3. repeatable:是否自动重复(按Enter键是否会重复执行)
  4. command:该命令对应的响应函数指针
  5. usage:简短的使用说明(字符串)
  6. help:较详细的使用说明(字符串)

U_BOOT_CMD宏在include/command.h中定义:

  1. #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
  2.     cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

“##”“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。

其中Struct_Section在include/command.h中定义如下:

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

凡是带有attribute ((unused,section (“.u_boot_cmd”))属性声明的变量都将被存放在”.u_boot_cmd”段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。

在u-Boot连接脚本 u-boot.lds中定义了”.u_boot_cmd”段:

  1. . = .;
  2. __u_boot_cmd_start = .; /*将 __u_boot_cmd_start指定为当前地址 */
  3. .u_boot_cmd : { *(.u_boot_cmd) }
  4. __u_boot_cmd_end = .; /* 将__u_boot_cmd_end指定为当前地址 */

这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。

这样只要将u-boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在 __u_boot_cmd_start 与 __u_boot_cmd_end 之间查找就可以了。

cmd_tbl_t在include/command.h中定义如下:

  1. struct cmd_tbl_s {
  2.     char        *name;        /* Command Name            */
  3.     int          maxargs;    /* maximum number of arguments    */
  4.     int          repeatable;    /* autorepeat allowed?        */
  5.                     /* Implementation function    */
  6.     int        (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
  7.     char        *usage;        /* Usage message    (short)    */
  8. #ifdef    CONFIG_SYS_LONGHELP
  9.     char        *help;        /* Help  message    (long)    */
  10. #endif
  11. #ifdef CONFIG_AUTO_COMPLETE
  12.     /* do auto completion on the arguments */
  13.     int        (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
  14. #endif
  15. };
  16.  
  17. typedef struct cmd_tbl_s    cmd_tbl_t;

一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。

以“boot”命令的定义经过宏展开后如下:

    cmd_tbl_t __u_boot_cmd_boot __attribute__ ((unused,section (".u_boot_cmd"))) = {boot, 1, 1, do_bootd, "boot    - boot default, i.e., run 'bootcmd'\n", " NULL"}
  1. int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  2. {
  3. int rcode = 0;
  4.  
  5. if (run_command(getenv("bootcmd"), flag) < 0)
  6. rcode = 1;
  7. return rcode;
  8. }
     
     

    在2013-10-rc3的最新u-boot版本中,对此有改动,我们先看定义(include/command.h)

    #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_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)
    #define ll_entry_declare(_type, _name, _list)      _type _u_boot_list_2_##_list##_2_##_name __aligned(4)      __attribute__((unused,      section(".u_boot_list_2_"#_list"_2_"#_name)))

    看上去挺复杂的,其实就是将cmd放在了u_boot_list这类开头的section里,所以我们只需在u-boot.lds里添加:

    . = ALIGN(4);
    .u_boot_list : {
     KEEP(*(SORT(.u_boot_list*)));
    }
     

run_command 命令分析:

u-boot启动第二阶段最后跳到main_loop函数中循环

  1. s = getenv ("bootcmd");
  2. if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
  3. ......
  4. run_command (s, 0);
  5. ......
  6. }

从main_loop中我们知道,如果bootdelay时间内未按下按键则启动Linux内核,按下按键则进入uboot命令行等待用户输入命令。

用户输入命令则调取run_command函数,在该函数中有下面几个比较重要的点:

1. 从注释我们很容易知道这段代码是在对命令进行分离,并且u-boot支持’;’分离命令。

  1. /*
  2. * Find separator, or string end
  3. * Allow simple escape of ';' by writing "\;"
  4. */
  5. for (inquotes = 0, sep = str; *sep; sep++) {
  6. if ((*sep=='\'') &&
  7. (*(sep-1) != '\\'))
  8. inquotes=!inquotes;
  9.  
  10. if (!inquotes &&
  11. (*sep == ';') && /* separator */
  12. ( sep != str) && /* past string start */
  13. (*(sep-1) != '\\')) /* and NOT escaped */
  14. break;
  15. }

2. 分离参数

  1. /* Extract arguments */
  2. if ((argc = parse_line (finaltoken, argv)) == 0) {
  3. rc = -1; /* no command at all */
  4. continue;
  5. }

3. 用第一个参数argv[0]在命令列表中寻找对应的命令,并返回一个cmd_tbl_t类型的实例。我们可以猜到这个结构体应该保函了有关命令的一系列内容。

  1. /* Look up command in command table */
  2. if ((cmdtp = find_cmd(argv[0])) == NULL) {
  3. printf ("Unknown command '%s' - try 'help'\n", argv[0]);
  4. rc = -1; /* give up after bad command */
  5. continue;
  6. }

在common/command.c 中查看find_cmd函数

  1. /*__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回cmdtp->name命令的cmd_tbl_t结构。*/
  2. cmd_tbl_t *cmdtp;
  3. cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; /*Init value */
  4. const char *p;
  5. int len;
  6. int n_found = 0;
  7.  
  8. /*
  9. * Some commands allow length modifiers (like "cp.b");
  10. * compare command name only until first dot.
  11. */
  12. len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
  13.  
  14. for (cmdtp = &__u_boot_cmd_start;
  15. cmdtp != &__u_boot_cmd_end;
  16. cmdtp++) {
  17. if (strncmp (cmd, cmdtp->name, len) == 0) {
  18. if (len == strlen (cmdtp->name))
  19. return cmdtp; /* full match */
  20.  
  21. cmdtp_temp = cmdtp; /* abbreviated command ? */
  22. n_found++;
  23. }
  24. }

五、总结命令执行过程

① 在u-boot控制台中输入“boot”命令执行时,u-boot控制台接收输入的字符串“boot”,传递给run_command函数。

② run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回boot命令的cmd_tbl_t结构。

③ 然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用boot命令的响应函数do_bootd,从而完成了命令的执行。

六、自制u-boot命令

__attribute__这个关键词是GNU编译器中的编译属性,ARM编译器也支持这个用法。__attribute__主要用于改变所声明或定义的函数或 数据的特性,它有很多子项,用于改变作用对象的特性。比如对函数,noline将禁止进行内联扩展、noreturn表示没有返回值、pure表明函数除 返回值外,不会通过其它(如全局变量、指针)对函数外部产生任何影响。当然,__attribute__肯定有很多的用法,今天就用到了section部分,所以就只针对这个做一些记录。

提到section,就得说RO
RI ZI了,在ARM编译器编译之后,代码被划分为不同的段,RO Section(ReadOnly)中存放代码段和常量,RW
Section(ReadWrite)中存放可读写静态变量和全局变量,ZI Section(ZeroInit)是存放在RW段中初始化为0的变量。

于是本文的大体意思就清晰了,__attribute__((section("section_name"))),其作用是将作用的函数或数据放入指定名为"section_name"对应的段中。

MDK给出的帮助文档如下,他将__attribute__的用法归类到编译器特性里,以变量和函数的两种用法做区分。

1.编译时为变量指定段

  1. __attribute__((section("name")))
  2. RealView Compilation Tools for µVision Compiler Reference Guide Version 4.0
  3. Home > Compiler-specific Features > Variable attributes > __attribute__((section("name")))
  4. 4.5.6. __attribute__((section("name")))
  5. Normally, the ARM compiler places the objects it generates in sections like data and bss. However, you might require additional data sections or you might want a variable to appear in a special section, for example, to map to special hardware. The section attribute specifies that a variable must be placed in a particular data section. If you use the section attribute, read-only variables are placed in RO data sections, read-write variables are placed in RW data sections unless you use the zero_init attribute. In this case, the variable is placed in a ZI section.
  6. Note
  7. This variable attribute is a GNU compiler extension supported by the ARM compiler.
  8. Example
  9. /* in RO section */
  10. const int descriptor[3] __attribute__ ((section ("descr"))) = { 1,2,3 };
  11. /* in RW section */
  12. long long rw[10] __attribute__ ((section ("RW")));
  13. /* in ZI section *
  14. long long altstack[10] __attribute__ ((section ("STACK"), zero_init));/

2.编译时为函数指定段

    1. __attribute__((section("name")))
    2. RealView Compilation Tools for µVision Compiler Reference Guide Version 4.0
    3. Home > Compiler-specific Features > Function attributes > __attribute__((section("name")))
    4. 4.3.13. __attribute__((section("name")))
    5. The section function attribute enables you to place code in different sections of the image.
    6. Note
    7. This function attribute is a GNU compiler extension that is supported by the ARM compiler.
    8. Example
    9. In the following example, Function_Attributes_section_0 is placed into the RO section new_section rather than .text.
    10. void Function_Attributes_section_0 (void)
    11. __attribute__ ((section ("new_section")));
    12. void Function_Attributes_section_0 (void)
    13. {
    14. static int aStatic =0;
    15. aStatic++;
    16. }
    17. In the following example, section function attribute overrides the #pragma arm section setting.
    18. #pragma arm section code="foo"
    19. int f2()
    20. {
    21. return 1;
    22. }                                  // into the 'foo' area
    23. __attribute__ ((section ("bar"))) int f3()
    24. {
    25. return 1;
    26. }                                  // into the 'bar' area
    27. int f4()
    28. {
    29. return 1;
    30. }                                  // into the 'foo' area
    31. #pragma arm section

Uboot命令U_BOOT_CMD分析的更多相关文章

  1. Uboot命令U_BOOT_CMD

    转载:http://blog.csdn.net/shengzhadon/article/details/52766263 U_BOOT_CMD是一个宏定义,具体功能是定义一个struct cmd_tb ...

  2. 多版本uboot命令行分析

    1.1.6 经典版本: 1.uboot第二阶段第一个函数void start_armboot (void),一路gd参数设置.设备初始化.控制台初始化.端口初始化,最后到main_loop ()命令行 ...

  3. uboot命令分析+实现【转】

    转自:http://xouou.iteye.com/blog/2150061 先贴一个重要结构,位于uboot/include/command.h,这个结构代表每个uboot命令 struct cmd ...

  4. u-boot源码分析之C语言段

    题外话: 最近一直在学习u-boot的源代码,从代码量到代码风格,都让我认识到什么才是真正的程序.以往我所学到的C语言知识和u-boot的源代码相比,实在不值一提.说到底,机器都是0和1控制的.感觉这 ...

  5. U-Boot GOT表分析和u-boot.lds解读

    转自:http://blog.sina.com.cn/s/blog_70dd16910100zab6.html u-boot-2010.09/arch/powerpc/cpu/mpc86xx/star ...

  6. (转载)U-boot启动完全分析

    1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 Ø 硬件设备初始化 Ø 加载U-Boot第二阶段代码到RAM空间 Ø 设置好栈 Ø ...

  7. uboot主Makefile分析(t配置和编译过程详解)

    1.编译uboot前需要三次make make distcleanmake x210_sd_configmake -j4 make distclean为清楚dist文件. make x210_sd_c ...

  8. u-boot链接脚本分析

    eclipse 64位下载地址:http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release ...

  9. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

随机推荐

  1. Wowza 4.5 修改 manager 端口号

    //编辑下面的文件, 搜索8088 有两处,改为想要的端口号即可 vim /usr/local/WowzaStreamingEngine/manager/bin/startmgr.sh // 重启服务 ...

  2. debugfs linux rm 删除 恢复 Attempt to read block from filesystem resulted in short read while opening filesystem

    w 删除具有空字符的文件 反斜杠来转义下一个字符 rm -R Samples\ -\ Copy well@well:/home/etc/project/apilinux/MarketplaceWebS ...

  3. docker 提高效率 network-bridging 桥接

    安装的时间顺序 bit3 192.168.107.128 wredis 192.168.107.129 wmysql 192.168.107.130 wslave 192.168.107.131 w ...

  4. MySQL与MongoDB的不同

    数据存放的巨大不同

  5. ps 等程序的选项的三种风格

    unix options bsd options gnu long options unix options, which may be grouped and must be preceded by ...

  6. accomplish、complete、finish、achieve和fulfill

    accomplish to succeed in doing something, especially after trying very hard vt. 完成:实现:达到 complete us ...

  7. VMware vSphere 虚拟化简介

    目录 目录 vSphere 简介 vSphere 提供的工具 vCenter vCenter 的功能 vCenter 管理界面上提供的操作功能 HOST CLUSTER TEMPLATE Virtua ...

  8. JMeterContext----上下文

    http://jmeter.apache.org/api/org/apache/jmeter/threads/JMeterContext.html org.apache.jmeter.threads ...

  9. django基于odm,简单的post和get封装

  10. 使用autofac的一些问题

    None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on ...