http://www.360doc.com/content/11/0826/12/7473909_143404764.shtml
uboot命令解释与运行分析
http://blog.chinaunix.net/uid-20665047-id-3485444.html
添加命令 U_BOOT_CMD
http://blog.sina.com.cn/s/blog_500bd63c0101bnvk.html
http://blog.sina.com.cn/s/blog_6947718c010171hg.html
uboot第三阶段启动内核等命令实现
思路是命令是一个结构体,结构体中有name 和function,name表示command_name,function表示如何执行和处理这个command_name。具体的结构体形式是这样的:
struct cmd_tbl_s {
char *name;
int maxargs;
int repeatable; 这里指的是执行完命令之后如果再次摁下回车能不能自动执行。
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);这个是实现函数的函数指针
char *usage; 指的是短的帮助信息,例如输入help命令后,出现的所有命令的简单的介绍
#ifdef CFG_LONGHELP
char *help; 指的是长的帮助信息,例如输入help md就会显示出来md命令详细的信息。
#endif
#ifdef CONFIG_AUTO_COMPLETE
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
下面分析run_command如何实现:
run_command函数是在command/main.c中
---------------------------run_command函数开始-------------------------------------
int run_command (const char *cmd, int flag)
{
cmd_tbl_t *cmdtp;
char cmdbuf[CFG_CBSIZE];
char *token;
char *sep;
char finaltoken[CFG_CBSIZE];
char *str = cmdbuf;
char *argv[CFG_MAXARGS + 1];
int argc, inquotes;
int repeatable = 1;
int rc = 0;
#ifdef DEBUG_PARSER
printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
puts (cmd ? cmd : "NULL");
puts ("\"\n");
#endif
clear_ctrlc();
if (!cmd || !*cmd) {
return -1;
}
if (strlen(cmd) >= CFG_CBSIZE) {
puts ("## Command too long!\n");
return -1;
}
strcpy (cmdbuf, cmd);
#ifdef DEBUG_PARSER
printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
while (*str) {这里实现把命令解析出来,一个一个提取出来
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && 这里分号表示如果有两个命令的话用分号隔开,也就是说处理了输入多个命令的情况。
( sep != str) &&
(*(sep-1) != '\\'))
break;
}
token = str;
if (*sep) {
str = sep + 1;
*sep = '\0';
}
else
str = sep;
#ifdef DEBUG_PARSER
printf ("token: \"%s\"\n", token);
#endif
process_macros (token, finaltoken);处理宏,例如当我们用网卡或者usb下载文件到内存中时候会临时生成一些环境变量或者宏,
if ((argc = parse_line (finaltoken, argv)) == 0) {解析输入的命令文字,比如md.w 0,成为一个字符数组如argc[0]="md.w" argc[1]="0",英文是extract arguments,提取参数。
rc = -1;
continue;
}
if ((cmdtp = find_cmd(argv[0])) == NULL) {这里是查找命令,一般argv[0]中存放的是命令,后面的几个是命令的参数,
printf ("Unknown command '%s' - try 'help'\n", argv[0]);
rc = -1;
continue;
}
if (argc > cmdtp->maxargs) {
printf ("Usage:\n%s\n", cmdtp->usage);
rc = -1;
continue;
}
#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("'bootd' recursion detected\n");
rc = -1;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}
repeatable &= cmdtp->repeatable;
if (had_ctrlc ())
return 0;
}
return rc ? rc : repeatable;
}
---------------------------run_command函数结束-------------------------------------
---------------------------find_cmd函数开始---------------------------------------
* find command table entry for a command
*/
cmd_tbl_t *find_cmd (const char *cmd)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start;
const char *p;
int len;
int n_found = 0;
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
for (cmdtp = &__u_boot_cmd_start;这两个参数在文件中是没有的,但是查看链接脚本有这样几句话_u_boot_cmd_start = .;
.u_boot_cmd:{ *(.u_boot_cmd) }
_u_boot_cmd_end = .;
是一个地址,这个循环实现从命令开始存放地址到结束地址遍历命令进行查找。所有的命令全都会被集中到u_boot_cmd这个区域里面来,具体的实现办法是:#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd"))),这句话,同理可以找到如何将所有文件的代码段放到.text的,关键的是结构体cmd_tbl_t,也即cmd_table,t表示结构体,有这个属性attribute。
cmdtp != &__u_boot_cmd_end;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) {
if (len == strlen (cmdtp->name))
return cmdtp;
cmdtp_temp = cmdtp;
n_found++;
}
}
if (n_found == 1) {
return cmdtp_temp;
}
return NULL;
}
---------------------------find_cmd函数开始---------------------------------------
例如U_BOOT_CMD命令的定义
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
);按照结构体中去拆开就是 bootm是name,最大参数个数,可以repeat,具体实现函数指针,简要帮助信息,详细帮助信息。
uboot下用do_run()函数来执行U_BOOT_CMD定义的命令
今天研究了一下uboot中怎么用do_run()函数来执行定义好的一些命令,发现其实也挺简单的,关键是得把do_run函数的几个参数吃透,这几个参数都是由uboot commmod 的cmd_tbl_s结构体决定的,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}
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 CONFIG_SYS_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
}
对比两者后很容易就明白了怎样去定义一个uboot下的命令。
接下来看看do_run函数的定义(在../boot/common/main.c下):
#if defined(CONFIG_CMD_RUN)
int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
int i;
if (argc < 2) { //参数个数小于2则提示重新输入
cmd_usage(cmdtp);
return 1;
}
for (i=1; i
char *arg;
if ((arg = getenv (argv[i])) == NULL) {
printf ("## Error: \"%s\" not defined\n", argv[i]);
return 1;
}
#ifndef CONFIG_SYS_HUSH_PARSER
if (run_command (arg, flag) == -1){ //
return 1;
}
#else
if (parse_string_outer(arg,
FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP) != 0){
return 1;
}
#endif
}
return 0;
}
其中第一个参数cmd_tbl_t * cmdtp为Command Table,即命令表参数,该结构体的定义在common.h下,一般设为NULL。
第二个参数int flag为Command Flag,用来标志命令来自哪里,跟它相关的有两个宏:
#define CMD_FLAG_REPEAT 0x0001 /* repeat last command */
#define CMD_FLAG_BOOTD 0x0002 /* command is from bootd */
它被传给run_command (const char *cmd, int flag),一般设为0,表示bootd,设为1则表示repeat(递归式)。
第三个int argc表示输入参数的个数,小于2则提示重新输入。第三个char *argv[]是输入命令字符的指针,用来传递定义好的指针数组。下面通过一个简单的例子来说明怎么使用do_run()函数,yaffs2ram的作用是实现从yaffs文件系统中拷贝logo.bmp到指定的RAM地址上:
int yaffs2ram(void)
{
char *argv[5]; //定义5个指针数组,用来存放各个字符命令的指针
char buf[100];
char *name_buf = "/logo/logo.bmp";
int ret;
if(!yaffs_file_exist(name_buf))//检查文件系统中是否存在logo.bmp
{
dprintf_line("%s NOT Exist!",name_buf);
return -1;
}
//把yrdm /logo/logo.bmp 0x8000_0000命令传给buf(loadaddr为环境变量)
sprintf(buf,"yrdm %s ${loadaddr}",name_buf);
setenv("upgradetemp", buf);//设置临时环境变量upgradetemp的值
argv[0] = "run";
argv[1] = "upgradetemp";
argv[2] = NULL;
ret = do_run(NULL, 0, 2, argv); //运行yrdm /logo/logo.bmp 0x8000_0000命令,把logo.bmp拷贝到0x8000_000地址上
if ( ret == 1)
{
printf("do_run error!")
return -1;
}
setenv("upgradetemp", NULL); //清空临时环境变量的值
return 0;
}
注:由于do_yrdm函数规定输入命令的个数必须为3(如下所示),所以我们加了一个argv[2] = NULL命令,该命令什么都不做。
U_BOOT_CMD(
yrdm, 3, 0, do_yrdm,
"read file to memory from yaffs",
"filename offset"
);
uboot命令解释与运行分析
题记: 省略200字
这一回来分析一下uboot中命令行的解释, 所以我们直接从main_loop开始分析.
1. 从汇编阶段进入c阶段的第一个函数是start_xxx, 如/lib_unicore/board.c中的start_unicoreboot. 前半部分调用了若干初始化函数来进行部分硬件的初始化, 并设置一下环境. 这里不是我们本回要讨论的所以一一跳过. 在start_xxx的最后调用了main_loop(), 而且还是被一个死循环死死圈住了;
for (;;) { |
2. 现在我们已经进入了这个圈套那么只能往里钻了. common/main.c文件中的main_loop().
//从环境变量里找bootdelay |
上面代码主要是对自启动部分的描述, 其中命令执行部分是在run_command中进行的, 这个等在后文分析. 如果我们没有bootcmd或者在延时中被打断, 那么代码会继续向下执行
//下面for循环是uboot处理终端命令的主循环, 这时等待我们从键盘中输入一行命令, 真正的人机交互从这里开始 |
3.read_line()读取到命令行后会调用common/main.c文件中的run_command(). 现在是分析run_command()的时候了, 不管是从环境变量还是终端获得命令, 都是由run_command()来处理的.
//下面if语句判断命令是否太长, 还是避免有些变态的家伙输入了超过CFG_CBSIZE个字符的命令 |
中场休息, 下面要进入处理cmdbuf的循环中了, 长征马上开始
//str就是指向cmdbuf的指针, 其实这些东西都是针对的刚才那行命令 |
4.就此打断一下, 我们要分析一下find_cmd了, 不能再跳过了. find_cmd()在.u_boot_cmd段中寻找该命令的cmd_tbl_t结构, 找到后返回该结构. 该命令的结构是通过定义在include/command.h中的宏定义U_BOOT_CMD登记进.u_boot_cmd段中的.
//先研究一下这个神奇的U_BOOT_CMD, 定义如下, 其中cmd_tbl_t是命令的结构体类型, 这里不罗列了,成员包括命令名字, 参数的最大个数, 使用说明等等. Struct_Section定义为 #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd"))) |
5. 刚才我们在长征的半路翻越了一座雪山, 现在继续回到while循环中
|