Uboot命令U_BOOT_CMD分析


其中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.編譯時為變量指定段

[cpp]  view plain  copy
  1. __attribute__((section("name")))  
  2. RealView Compilation Tools for µVision Compiler Reference Guide Version 4.0   
  3.    
  4. Home > Compiler-specific Features > Variable attributes > __attribute__((section("name")))   
  5.   
  6. 4.5.6. __attribute__((section("name")))  
  7. 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.  
  8.   
  9. Note  
  10. This variable attribute is a GNU compiler extension supported by the ARM compiler.  
  11.   
  12. Example  
  13. /* in RO section */  
  14. const int descriptor[3] __attribute__ ((section ("descr"))) = { 1,2,3 };  
  15. /* in RW section */  
  16. long long rw[10] __attribute__ ((section ("RW")));  
  17. /* in ZI section *  
  18. long long altstack[10] __attribute__ ((section ("STACK"), zero_init));/  

 

2.編譯時為函數指定段

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM