進入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”)))將所有定義的命令都存放在一塊連續的存儲空間中,這種存儲方式的好處是減少了命令檢索的時間。