本篇我们来学习uboot CMD 命令的原理,其实在上一篇中我们跳转执行内核入口函数时,就是使用如下的CMD的命令
bootm 0x30007FC0"
下面我们就以bootm命令来说明CMD的原理
1.首先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
);
#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}
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
从以上分析可知U_BOOT_CMD为宏定义展开如下:
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd")))=
其中我们重点看下__attribute__ ((unused,section (“.u_boot_cmd”))),
1. __attribute__是gcc的编译选项,section是子选项,用于表明当前的定义放置在单独的段中,比如上面就是__u_boot_cmd_bootm的定义放在”.u_boot_cmd”段中
2.unused 用于表示即使该变量没有在代码中显式的使用编译器也不产生警告信息
经过上面的定义,我们在u_boot_cmd段中定义了一个cmd_tbl_t的结构体__u_boot_cmd_bootm
下面我们看下链接脚本中的关于u_boot_cmd段的定义
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
从上面的链接脚本可以看出,所以的u_boot_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; /*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);
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 */
}
重点就是上面的for循环,cmdtp = &__u_boot_cmd_start定义为u_boot_cmd段的起始地址,依次以cmd_tbl_t为大小
在u_boot_cmd段查找
总结下:
就是通过宏定义来定义变量,准确地说应该是结构体变量。并且把这些同一种结构体的变量放在一个段中,充分的利用了连接器的作用。这种用法在内核启动的开始阶段也有,相信后面我们还会说到。