代码分析
看u-boot
源码我们知道,在u-boot
中的每一个命令都是通过U_BOOT_CMD
或U_BOOT_CMD_COMPLETE
来定义的。下面来分析分析他们的具体实现逻辑。
U_BOOT_CMD
是一个宏定义,代码在 include/command.h
中。
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \
U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
#ifdef CONFIG_CMDLINE
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
ll_entry_declare(cmd_tbl_t, _name, cmd) = \ //此处需要注意的时这里赋值的是cmd 而非上层参数 _cmd
U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp);
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp) \
{ #_name, _maxargs, \
_rep ? cmd_always_repeatable : cmd_never_repeatable, \
_cmd, _usage, _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
#else
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, \
_comp) \
_CMD_REMOVE(sub_ ## _name, _cmd)
#define _CMD_REMOVE(_name, _cmd) \
int __remove_ ## _name(void) \
{ \
if (0) \
_cmd(NULL, 0, 0, NULL); \
return 0; \
}
#endif /* CONFIG_CMDLINE */
ll_entry_declare
代码在include/linker_lists.h
中。
/**
* ll_entry_declare() - Declare linker-generated array entry
* @_type: Data type of the entry
* @_name: Name of the entry
* @_list: name of the list. Should contain only characters allowed
* in a C variable name!
*
* This macro declares a variable that is placed into a linker-generated
* array. This is a basic building block for more advanced use of linker-
* generated arrays. The user is expected to build their own macro wrapper
* around this one.
*
* A variable declared using this macro must be compile-time initialized.
*
* Special precaution must be made when using this macro:
*
* 1) The _type must not contain the "static" keyword, otherwise the
* entry is generated and can be iterated but is listed in the map
* file and cannot be retrieved by name.
*
* 2) In case a section is declared that contains some array elements AND
* a subsection of this section is declared and contains some elements,
* it is imperative that the elements are of the same type.
*
* 3) In case an outer section is declared that contains some array elements
* AND an inner subsection of this section is declared and contains some
* elements, then when traversing the outer section, even the elements of
* the inner sections are present in the array.
*
* Example:
*
* ::
*
* ll_entry_declare(struct my_sub_cmd, my_sub_cmd, cmd_sub) = {
* .x = 3,
* .y = 4,
* };
*/
#define ll_entry_declare(_type, _name, _list) \
_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \
__attribute__((unused, \
section(".u_boot_list_2_"#_list"_2_"#_name)))
通过上面的代码可知U_BOOT_CMD
就是定义了一个类型为cmd_tbl_t
的结构体数据并赋值。并且U_BOOT_CMD
是U_BOOT_CMD_COMPLETE
的精减版,实现上U_BOOT_CMD
是调用U_BOOT_CMD_COMPLETE
。区别在于U_BOOT_CMD
对cmd_tbl_t
的结构体中的complete
成员赋了空值。U_BOOT_CMD_COMPLETE
则是对cmd_tbl_t
的结构体的所有成员都有赋值。
cmd_tbl_t
在include/command.h
中定义
typedef struct cmd_tbl_s cmd_tbl_t;
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
/*
* Same as ->cmd() except the command
* tells us if it can be repeated.
* Replaces the old ->repeatable field
* which was not able to make
* repeatable property different for
* the main command and sub-commands.
*/
int (*cmd_rep)(struct cmd_tbl_s *cmd, int flags, int argc,
char * const argv[], int *repeatable);
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char * const []);
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 * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
U_BOOT_CMD
中的各个参数,其实对应的就是cmd_tbl_s
结构体里面成员变量。
U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)
_name :命令名称
_maxargs :接受最大的参数个数
_rep :重复
_cmd :命令实现函数
_usage :使用说明(短说明)
_help :使用说明
具体命令分析
以env
和printenv
为例, 这两个命令为别通过U_BOOT_CMD
是U_BOOT_CMD_COMPLETE
来定义。其代码在cmd/nvedit.c
中,
U_BOOT_CMD(
env, CONFIG_SYS_MAXARGS, 1, do_env,
"environment handling commands", env_help_text
);
通过宏展开后可知U_BOOT_CMD
就是定义了一个名为 _u_boot_list_cmd_2_env
类型为cmd_tbl_t
的结构体数据并赋值。
cmd_tbl_t _u_boot_list_cmd_env __aligned(4) \
__attribute__((unused,section(".u_boot_list_2_cmd_2_env))) = \
{ env, 16, \
1 ? cmd_always_repeatable : cmd_never_repeatable, \
do_env, "environment handling commands", env_help_text }
U_BOOT_CMD_COMPLETE(
printenv, CONFIG_SYS_MAXARGS, 1, do_env_print,
"print environment variables",
"[-a]\n - print [all] values of all environment variables\n"
#if defined(CONFIG_CMD_NVEDIT_EFI)
"printenv -e [name ...]\n"
" - print UEFI variable 'name' or all the variables\n"
#endif
"printenv name ...\n"
" - print value of environment variable 'name'",
var_complete
);
通过宏展开后可知U_BOOT_CMD_COMPLETE
就是定义了一个名为 _u_boot_list_2_cmd_2_printenv
类型为cmd_tbl_t
的结构体数据并赋值。
cmd_tbl_t _u_boot_list_2_cmd_2_printenv __aligned(4) \
__attribute__((unused, section(".u_boot_list_2_cmd_2_printenv))) = \
{ printenv, 16, \
1 ? cmd_always_repeatable : cmd_never_repeatable, \
do_env_print,
"print environment variables",
"[-a]\n - print [all] values of all environment variables\n"
#if defined(CONFIG_CMD_NVEDIT_EFI)
"printenv -e [name ...]\n"
" - print UEFI variable 'name' or all the variables\n"
#endif
"printenv name ...\n"
" - print value of environment variable 'name'",
var_complete}
通过nm
或 readelf
查看相应的符号表
$ nm u-boot | grep _u_boot_list_2_cmd_2_env
9fca6818 D _u_boot_list_2_cmd_2_env
$ readelf -s u-boot | grep _u_boot_list_2_cmd_2_env
4671: 9fca6818 28 OBJECT GLOBAL DEFAULT 8 _u_boot_list_2_cmd_2_env
命令调用流程
run_command common/cli.c
cli_simple_run_command common/cli_simple.c
cli_simple_parse_line 分析参数
cmd_process common/command.c
find_cmd 根据name查找命令,在section .u_boot_list_2_cmd_1和.u_boot_list_2_cmd_3 之间查找
cmd_call 执行命令
在u-boot中添加命令
- 在
common
建立cmd_xx.c
文件,参考其他文件如 cmd_bootm.c 来添加头文件 - 用
U_BOOT_CMD
或U_BOOT_CMD_COMPLETE
来定义命令 - 实现命令的操作
do_xx
函数 - 将
common/cmd_xx.c
加入common/Makefile
中COBJS += cmd_xx.o
- 重新配置编译
u-boot