该博客主要记录和总结我在工作中遇到的问题和积累的经验。如有错误之处,谢请指正。
共享资源,欢迎转载:http://blog.youkuaiyun.com/fzu_dianzi
一、环境
目标板:mini2440
u-boot版本:u-boot-2009.08
交叉编译器:arm-linux-gcc-4.3.2
操作系统:Linux(Ubuntu-11.10)
二、目的
1、剖析u-boot命令结构
2、通过代码修改,添加u-boot命令
三、
1、
u-boot支持命令行交互,几乎全部的命令都在common/里面定义。以cmd_开头的文件都是命令类文件。如下图所示:

2、执行命令入口
u-boot stage1执行完后,就跳入stage2 的入口start_armboot函数中。该函数主要进行一系列的初始化,然后进入等待用户命令的循环,main_loop()。
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;)
{
main_loop ();
}
在main_loop()里,u-boot获取用户控制台输入,然后调用run_command函数执行命令。
这里不详细的分析run_command[common/Main.c]的处理流程。
执行命令之前肯定有一个校验命令合法性的过程,u-boot是通过find_cmd来查找已注册的合法命令的。
该函数定义在common/command.c
cmd_tbl_t *find_cmd (const char *cmd)
{
int len = &__u_boot_cmd_end - &__u_boot_cmd_start; //计算命令个数
return find_cmd_tbl(cmd, &__u_boot_cmd_start, len); //查找命令表
}
cmd_tbl_t
其定义是在include/command.h
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
};
typedef struct cmd_tbl_s cmd_tbl_t;
cmd_tbl_t定义了u-boot命令的结构信息,包含命令名、参数最多个数及命令的回调函数等
__u_boot_cmd_ start和__u_boot_cmd_end
其定义是在cpu/larm920t/u-boot.lds(不同类型的cpu,位置不同)
//u_boot_cmd的起始地址__u_boot_cmd_start,止于__u_boot_cmd_end
//这段区域内存放u-boot所有命令
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
我们可以查看u-boot根目录下system.map,便一目了然。

链接文件里u_boot_cmd段合并在一起生成一个大的.u_boot_cmd段,并且用__u_boot_cmd_start与__u_boot_cmd_end两个符号标识这块内存的首尾边界。这样的机制使得用户添加代码不用去修改其core代码。
3、查看u-boot的命令文件内容
如果我们要自定义出一个命令,那么必须了解其他命令是如何构建的。在每个cmd_XXX.c文件里都可以看到类似下面定义的语句。
U_BOOT_CMD(
go, CONFIG_SYS_MAXARGS, 1, do_go,
"start application at address 'addr'",
"addr [arg ...]\n - start application at address 'addr'\n"
" passing 'arg' as arguments"
);
U_BOOT_CMD
U-Boot中每个命令都通过U_BOOT_CMD宏来定义,其定义是在common/command.h
/*
凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
*/
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#ifdef CFG_LONGHELP
#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}
//“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串
#else /* no long help info */
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
参数意义如下:
name:命令名字;
maxargs:最大的参数个数;
rep:命令是否可重复,可重复是指运行一个命令后,下次敲回车即可再次运行;
cmd:对应的函数指针,一般都是do_name;
usage:简短的使用说明;
help:较详细的使用说明;
go命令的定义经过宏展开后如下:
cmd_tbl_t __u_boot_cmd_go __attribute__ ((unused,section (".u_boot_cmd"))) =
{
go, CONFIG_SYS_MAXARGS, 1, do_go,
"start application at address 'addr'",
"addr [arg ...]\n - start application at address 'addr'\n"
" passing 'arg' as arguments"
}
实质上就是用U_BOOT_CMD宏定义的信息构造了一个cmd_tbl_t类型的结构体。编译器将该结构体放在“u_boot_cmd”段,执行命令时就可以在“u_boot_cmd”段查找到对应的cmd_tbl_t类型结构体。
4、添加自定义命令
1) 在common/创建一个cmd_care.c
#include <common.h>
#include <command.h>
int do_care (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf("Hello world!\n");
}
U_BOOT_CMD(
care, 2, 1, do_care,
"u-boot command test by care",
""
);
1) 在common/Makefile添加COBJS-y += cmd_care.o
2) 对u-boot进行编译
3) 结果
编译成功后,查看System.map

证明该命令已经成功添加。
将u-boot.bin烧写到flash,输入我们自定义的命令care
