进入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”)))将所有定义的命令都存放在一块连续的存储空间中,这种存储方式的好处是减少了命令检索的时间。