在u-boot中,我们可以输入命令来进行操作,如print命令打印内核信息,bootm命令来启动内核等等,那这些命令是如何被u-boot组织起来的呢?为了说明这个问题,我们以bootm命令为例来找到问题的答案。
u-boot源码中搜索”bootm“,可以在common\cmd_bootm.c中找到bootm命令对应的do_bootm()函数,那这个函数是如何被调用起来的?注意在这个源文件中存在一个U_BOOT_CMD宏,如下代码所示,虽然现在看不懂这个宏的含义,但是从这个宏的参数可以看出将bootm命令和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"
);
那么现在我们就需要知道这个宏的定义,继续在源码中搜索”U_BOOT_CMD“,当然会搜索出一大堆眼花缭乱的东西,宏的定义必然涉及“define”字段,在结果中搜索“define”可以找到定义了,在include\command.h中定义,同时这个宏中的Struct_Section也是一个宏。
#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}
那么现在根据宏的定义将上面定义bootm的宏进行展开,看这个宏到底进行了什么工作,展开结果是
cmd_tbl_t __u_boot_cmd_bootm _attribute__((unused,section (".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"
}
从展开的结果来看,定义了一个cmd_tbl_t的结构体,并设置这个结构体的段属性为“.u_boot_cmd”,先来看看cmd_tb_t这个结构体的内容
struct cmd_tbl_s {
char *name; //命令的名称,“bootm”
int maxargs; //命令运行携带的最大参数个数,CFG_MAXARGS=16个
int repeatable; //是否允许通过敲入回车键来重复上一次输入的命令 1为允许,0为不允许
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); //对于命令的实现函数 do_bootm,此处就将bootm命令和命令的实现关联起立了</span>
char *usage; //短的帮助信息,"bootm - boot application image from memory\n"
char *help; //长的帮助信息
};
从这个结构来看,显然是构造了一个命令的名称,命令的实现函数,及其帮助信息。那么段属性为“.u_boot_cmd”在哪定义,对于这种属性的定义,一般都在链接脚本中定义的,链接脚本内容如下
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
board/100ask24x0/boot_init.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .; //此处定了(.u_boot_cmd)段的起始地址__u_boot_cmd_start
.u_boot_cmd : { *(.u_boot_cmd) } //此处存放了所有文件的(.u_boot_cmd)
__u_boot_cmd_end = .; //定义了(.u_boot_cmd)段的结束地址__u_boot_cmd_end<pre name="code" class="plain">
. = ALIGN(4);__bss_start = .;.bss : { *(.bss) }_end = .;
从链接脚本来看,它将所有命令的cmd_tbl_t结构体都集中到(.u_boot_cmd)段进行存放。那么现在关键在于命令的调用了。我们可以假象一下,在u-boot的菜单中,当我们输入一个bootm命令的时候,我们一般的处理思路是将这个”bootm“字符串跟所有的命令结构体中name字段进行匹配,如果匹配成功那么就取出这个命令的cmd_tbl_t结构体,那么自然就可以调用这个命令的实现函数了。因此,既然涉及到所有命令结构的遍历,那么自然是从__u_boot_cmd_start遍历到__u_boot_cmd_end,那么我们可以继续在源码中搜索“__u_boot_cmd_start”,可以在common\command.c中找到find_cmd()函数对这个功能的实现。
cmd_tbl_t *find_cmd (const char *cmd)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; /*Init value */ //先找到(.u_boot_cmd段的起始地址)
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_cmd段中进行遍历与匹配)
for (cmdtp = &__u_boot_cmd_start;
cmdtp != &__u_boot_cmd_end;
cmdtp++) {
if (strncmp (cmd, cmdtp->name, len) == 0) { //如果输入的命令字符串和cmdtp中name字段完全匹配则返回cmdtp结构体
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中命令的组织架构了,那么我们也可以仿照bootm命令来自己实现一个u-boot的命令,比如输入一个hello命令,然后打印出hello world,步骤如下:
1、类似于bootm,在common下创建一个名为cmd_hello.c的源文件,内容如下
#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <image.h>
#include <malloc.h>
#include <zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <asm/byteorder.h>
int do_bhello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf("hello world\n");
return 1;
}
U_BOOT_CMD(
hello, CFG_MAXARGS, 1,do_hello,
"hello -new command\n",
""
);
2、在common目录下的Makefile文件中COBJ后加上 cmd_hello.o,这样就将这个命令编译进u-boot了。