一. uboot启动流程
上一篇文章大体分析了run_main_loop 到 cmd_process处理过程。地址如下:
uboot启动流程-run_main_loop 到 cmd_process处理说明一_凌肖战的博客-CSDN博客
本文具体分析涉及的几个函数执行过程。
二. cmd_process处理用到的 uboot命令
uboot
启动以后会进入
3
秒倒计时,如果在
3
秒倒计时结束之前按下按下回车键,就
会进入
uboot
的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动
Linux
内
核 , 这个功能
就 是 由 run_main_loop 函 数 来 完 成 的。
run_main_loop 函数主要是调用了 main_loop 函数。
main_loop函数做了很多事,主要调用了几个函数:
bootdelay_process 函数:此函数会读取环境变量 bootdelay 和 bootcmd 的内容, 然后将 bootdelay 的值赋值给全局变量 stored_bootdelay,返回值为环境变量 bootcmd 的值。
autoboot_command 函数:此函数就是检查倒计时是否结束?倒计时结束之前有没有被打断?
cli_loop 函数:是 uboot 的命令行处理函数,我们在 uboot 中输入各种命令,进行各种操作就是有 cli_loop 函数来处理的。该函数最终调用 cmd_process函数。
1. uboot中命令是如何定义?
在学习
cmd_process
之前先看一下
uboot
中命令是如何定义的。
uboot
使用宏
U_BOOT_CMD
来定义命令,宏
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)
可 以 看 出:U_BOOT_CMD
是
U_BOOT_CMD_COMPLETE
的 特 例 , 将
U_BOOT_CMD_COMPLETE
的最后一个参数设置成
NULL
就 是 U_BOOT_CMD。U_BOOT_CMD_COMPLETE
如下:
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
ll_entry_declare(cmd_tbl_t, _name, cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp);
宏 U_BOOT_CMD_COMPLETE
又 用 到了 :
ll_entry_declare
和
U_BOOT_CMD_MKENT_COMPLETE
。
宏
U_BOOT_CMD_MKENT_COMPLETE
定义在文件
include/command.h
中,内容如下:
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp) \
{ #_name, _maxargs, _rep, _cmd, _usage, \
_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
上 述 代 码 中 的 “
#
” 表 示 将
_name
传 递 过 来 的 值 字 符 串 化。
ll_entry_declar
定义在文件
include/linker_lists.h
中,定
义如下:
#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)))
_type
为
cmd_tbl_t
,因此
ll_entry_declare
就是定义了一个
cmd_tbl_t
变量,这里用到了
C
语
言中的“
##
”连接符符。其中的“
##_list
”表示用
_list
的值来替换,“
##_name
”就是用
_name
的
值来替换。
U_BOOT_CMD_MKENT_COMPLETE
又用到了宏
_CMD_HELP
和
_CMD_COMPLETE
,这两个
宏的定义如下:
#ifdef CONFIG_AUTO_COMPLETE
# define _CMD_COMPLETE(x) x,
#else
# define _CMD_COMPLETE(x)
#endif
#ifdef CONFIG_SYS_LONGHELP
# define _CMD_HELP(x) x,
#else
# define _CMD_HELP(x)
#endif
综上所述,看一下 U_BOOT_CMD 经过展开以后究竟是个什么模样的。
所有与命令有关的,即命令的实现,在 uboot根目录下的 cmd目录的文件下。
以命令 dhcp 为例,dhcp 为网络命令,所以在 uboot根目录下的/cmd/net.c文件中。dhcp 命令定义如下:
U_BOOT_CMD(
dhcp, 3, 1, do_dhcp,
"boot image via network using DHCP/TFTP protocol",
"[loadAddress] [[hostIPaddr:]bootfilename]"
);
将其展开,结果如下:
1、将 U_BOOT_CMD 展开后为:
U_BOOT_CMD_COMPLETE(dhcp, 3, 1, do_dhcp,
"boot image via network using DHCP/TFTP protocol",
"[loadAddress] [[hostIPaddr:]bootfilename]",
NULL)
2、将 U_BOOT_CMD_COMPLETE 展开后为:
ll_entry_declare(cmd_tbl_t, dhcp, cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(dhcp, 3, 1, do_dhcp, \
"boot image via network using DHCP/TFTP protocol", \
"[loadAddress] [[hostIPaddr:]bootfilename]", \
NULL);
3、将 ll_entry_declare 和 U_BOOT_CMD_MKENT_COMPLETE 展开后为:
cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned(4) \
__attribute__((unused,section(.u_boot_list_2_cmd_2_dhcp))) \
{ "dhcp", 3, 1, do_dhcp, \
"boot image via network using DHCP/TFTP protocol", \
"[loadAddress] [[hostIPaddr:]bootfilename]",\
NULL}
dhcp 命令最终展开结果为:
cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned(4) \
__attribute__((unused,section(.u_boot_list_2_cmd_2_dhcp))) \
{ "dhcp", 3, 1, do_dhcp, \
"boot image via network using DHCP/TFTP protocol", \
"[loadAddress] [[hostIPaddr:]bootfilename]",\
NULL}
第
1
行定义了一个
cmd_tbl_t
类型的变量,变量名为
_u_boot_list_2_cmd_2_dhcp
,此变量
4字节对齐。
第
2
行,使用
__attribute__
关 键 字 设 置 变 量
_u_boot_list_2_cmd_2_dhcp
存 储
在
.u_boot_list_2_cmd_2_dhcp 段中。第
2
行就是设置变量
_u_boot_list_2_cmd_2_dhcp
的存储位置。
u-boot.lds 链接脚本中有一个名为“
.u_boot_list
” 的段,所
有
.u_boot_list
开头的段都存放到
.u_boot.list 中。.u_boot.list 段如下:
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
第
3~6
行,
cmd_tbl_t
是个结构体,因此第
3-6
行是初始化
cmd_tbl_t
这个结构体的各个成员变量。
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 * 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_list_2_cmd_2_dhcp
的各个成员的值如下所示:
_u_boot_list_2_cmd_2_dhcp.name = "dhcp"
_u_boot_list_2_cmd_2_dhcp.maxargs = 3
_u_boot_list_2_cmd_2_dhcp.repeatable = 1
_u_boot_list_2_cmd_2_dhcp.cmd = do_dhcp
_u_boot_list_2_cmd_2_dhcp.usage = "boot image via network using DHCP/TFTP protocol"
_u_boot_list_2_cmd_2_dhcp.help = "[loadAddress] [[hostIPaddr:]bootfilename]"
_u_boot_list_2_cmd_2_dhcp.complete = NULL
三. 总结
当我们在
uboot
的命令行中输入“
dhcp
”这个命令的时候,最终执行的是
do_dhcp
这个函数。
总结一下,
uboot
中使用
U_BOOT_CMD
来定义一个命令,最终的目的就是为了定义一个
cmd_tbl_t
类型的变量,并初始化这个变量的各个成员。
uboot
中的每个命令都存储在
.u_boot_list
段中,每个命令都有一个名为
do_xxx(xxx
为具体的命令名
)
的函数,这个
do_xxx
函数就是具体
的命令处理函数。