函数main_loop和u-boot命令执行
来源:程序员人生 发布时间:2016-12-20 16:36:10 阅读次数:3632次
1.main_loop函数履行流程和命令解释器
run_main_loop是board_r中函数运行列表init_fnc_t init_sequence_r[]最后1个函数,它又调用了main_loop,且run_main_loop永不返回。
static int run_main_loop(void)
{
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
return 0;
}
main_loop定义在common/main.c中:
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
modem_init();
#ifdef CONFIG_VERSION_VARIABLE
setenv("ver", version_string); /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */
cli_init();
run_preboot_environment_command();
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
}
bootstage_mark_name函数调用了show_boot_progress,利用它显示启动进程(progress),此处为空函数。
setenv设置环境变量ver为version_string,后者在common/cmd_version.c中定义为:
const char __weak version_string[] = U_BOOT_VERSION_STRING;
U_BOOT_VERSION_STRING在version.h中定义为:
#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \
U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING
其中U_BOOT_VERSION ,U_BOOT_DATE,U_BOOT_TIME,U_BOOT_TZ均由u-boot构建系统自动产生,
它们分别代表u-boot版本号,编译日期和时间,和时间区。
如果定义了CONFIG_SYS_HUSH_PARSER,那末配置u-boot使用hush shell来作为履行器。hush shell是1种轻量型的shell。
cli_init用来初始化hush shell使用的1些变量。hush shell的实现机制比较复杂,以下的hush shell相干实现代码都不做详实跟踪分析。
有兴趣的可参见源代码和相干的网络文章。
run_preboot_environment_command函数从环境变量中获得"preboot"的定义,该变量包括了1些预启动命令,
1般环境变量中不包括该项配置。
bootdelay_process从环境变量中取出"bootdelay"和"bootcmd"的配置值,将取出的"bootdelay"配置值转换成整数,
赋值给全局变量stored_bootdelay,最后返回"bootcmd"的配置值。bootdelay为u-boot的启动延时计数值,计数期间内
如无用户按键输入干预,那末将履行"bootcmd"配置中的命令。
由于没有定义CONFIG_OF_CONTROL,函数cli_process_fdt返回false,接下来履行autoboot_command,
该函数在common/autoboot.c中实现:
void autoboot_command(const char *s)
{
if (stored_bootdelay != ⑴ && s && !abortboot(stored_bootdelay)) {
run_command_list(s, ⑴, 0);
}
}
全局变量stored_bootdelay在上面已做说明。静态函数abortboot中包括了CONFIG_AUTOBOOT_KEYED宏预处理分支,该宏定义用来使能用户名密码登录,这里它没有定义,而后调用了abortboot_normal,在履行的时间stored_bootdelay(秒)内,如无用户按键输入干预,那末abortboot_normal函数将返回0,否则返回1。 当无用户按键干预时,接下来将调用run_command_list履行上述从环境变量中读取的"bootcmd"配置值。注意该函数的参数s。run_command_list中调用了hush
shell的命令解释器(parse_stream_outer函数),解释bootcmd中的启动命令。环境变量bootcmd中的启动命令,用来设置linux必要的启动环境,然后加载和启动linux内核。u-boot启动linux内核后,将控制权交给linux内核,至此不再返回。
如用户在设定的bootdelay内无按键输入,那末将运行cli_loop履行hush shell命令解释器:
void cli_loop(void)
{
parse_file_outer();
/* This point is never reached */
for (;;);
}
parse_file_outer进行必要的初始化后,也将调用hush shell的命令解释器,即parse_stream_outer函数:
static int parse_stream_outer(structin_str*inp,intflag)
{
do {
...
...
run_list(...);
} while (rcode != ⑴ && !(flag & FLAG_EXIT_FROM_LOOP) && //#define FLAG_EXIT_FROM_LOOP 1
(inp->peek != static_peek || b_peek(inp)));
}
上面的do-while会循环命令解析器的"命令输入解析--履行"运行模式。
其中的函数run_list履行以下的函数调用流程:
run_list-->run_list_real-->run_pipe_real
最后在函数run_pipe_real中有:
return cmd_process(...);
函数cmd_process最后完成u-boot命令的定位和履行。
2.u-boot命令履行
命令处理函数均在common/command.c中实现,上述函数cmd_process定义以下:
enum command_ret_t cmd_process(int flag, int argc, char * const argv[], int *repeatable, ulong *ticks)
{
enum command_ret_t rc = CMD_RET_SUCCESS;
cmd_tbl_t *cmdtp;
/* Look up command in command table */
cmdtp = find_cmd(argv[0]);
if (cmdtp == NULL) {
printf("Unknown command '%s' - try 'help'\n", argv[0]);
return 1;
}
/* found - check max args */
if (argc > cmdtp->maxargs)
rc = CMD_RET_USAGE;
/* If OK so far, then do the command */
if (!rc) {
if (ticks)
*ticks = get_timer(0);
rc = cmd_call(cmdtp, flag, argc, argv);
if (ticks)
*ticks = get_timer(*ticks);
*repeatable &= cmdtp->repeatable;
}
if (rc == CMD_RET_USAGE)
rc = cmd_usage(cmdtp);
return rc;
}
u-boot中使用宏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最后1个参数_comp为NULL的特例,_comp表示变量是不是自动完成:
#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);
其中包括的宏U_BOOT_CMD_MKENT_COMPLETE定义为:
#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \
_usage, _help, _comp) \
{ #_name, _maxargs, _rep, _cmd, _usage, \
_CMD_HELP(_help) _CMD_COMPLETE(_comp) }
上面的_CMD_HELP根据配置可选为使用完全或简短帮助说明。_CMD_COMPLETE则根据配置决定是不是使用自动完成函数。U_BOOT_CMD_MKENT_COMPLETE宏实际上是组织输入参数,对ll_entry_declare进行数据填充。ll_entry_declare在文件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,这里定义1个cmd_tbl_t结构体,并把它放在符号段.u_boot_list_2_"#_list"_2_"#_name中,其中的_list和_name根据宏参数进行字符串替换。
下面,我们举例说明上述宏的实现机制。比如有以下的定义:
U_BOOT_CMD(
env, CONFIG_SYS_MAXARGS, 1, do_env,
"environment handling commands", env_help_text
);
即
U_BOOT_CMD_COMPLETE (
env, CONFIG_SYS_MAXARGS, 1, do_env,
"environment handling commands", env_help_text,NULL
);
带入宏参及其展开为 :
U_BOOT_CMD_COMPLETE(env, CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text , NULL ) \
ll_entry_declare(cmd_tbl_t, env , cmd) = \
U_BOOT_CMD_MKENT_COMPLETE(env , CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text , NULL);
其中的ll_entry_declare带入宏参及其展开为 :
ll_entry_declare(cmd_tbl_t , env , cmd ) \
cmd_tbl_t _u_boot_list_2_cmd_2_env __aligned(4) \
__attribute__((unused, \
section(".u_boot_list_2_cmd_2_env )))
其中的U_BOOT_CMD_MKENT_COMPLETE带入宏参及其展开为:
U_BOOT_CMD_MKENT_COMPLETE(env , CONFIG_SYS_MAXARGS , 1, do_env , _usage, _help, _comp) \
{ "env", CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text ,NULL}
那末上述U_BOOT_CMD_COMPLETE终究展开为:
cmd_tbl_t _u_boot_list_2_cmd_2_env __aligned(4) \
__attribute__((unused, section(".u_boot_list_2_cmd_2_env ))) =
{ "env", CONFIG_SYS_MAXARGS , 1, do_env , "environment handling commands" , env_help_text ,NULL}
其中的cmd_tbl_t定义为:
<pre code_snippet_id="1890747" snippet_file_name="blog_20160921_20_3676404" name="code" class="cpp">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
};
typedef struct cmd_tbl_s cmd_tbl_t;
该结构体包括了命令名,命令实现函数,命令使用简短说明usage的输出字符串,帮助回调函数,参数变量自动完成函数等。u-boot使用该结构体来描写1个完全的命令。
U_BOOT_CMD_COMPLETE宏用来定义1个cmd_tbl_t结构体变量,初始化该结构体中的相应成员,并把该结构体变量寄存在4字节对齐的.u_boot_list_2_cmd_2_env符号段中。如前所述,宏U_BOOT_CMD将最后1个参数_comp置为NULL,对U_BOOT_CMD_COMPLETE做了进1步的封装。所有使用U_BOOT_CMD和U_BOOT_CMD_COMPLETE定义的命令最后都集中放在以.u_boot_list_2_cmd_2开头的符号段中。即.u_boot_list_2_cmd_2_##name,这里的name是命令名。
我们回到函数上述的命令处理函数cmd_process,被其调用的find_cmd将根据命令名查找相应的cmd_tbl_t变量符号段,其实现以下:
cmd_tbl_t *find_cmd(const char *cmd)
{
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
const int len = ll_entry_count(cmd_tbl_t, cmd);
return find_cmd_tbl(cmd, start, len);
}
ll_entry_start定义以下:
#define ll_entry_start(_type, _list) \
({ \
static char start[0] __aligned(4) __attribute__((unused, \
section(".u_boot_list_2_"#_list"_1"))); \
(_type *)&start; \
})
那末,在上述函数find_cmd中,语句
cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);
定义1个包括0个字节的数组start[0],且把它放在.u_boot_cmd_2_list_1段中,该段属性为unsued。注意在u-boot.lds中有:
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
.u_boot_list中所有符号是按字符表的前后顺序排列的,.u_boot_list_2_list_1会放在所有使用U_BOOT_CMD和U_BOOT_CMD_COMPLETE
定义的符号段的最前面,即.u_boot_cmd_2_list_1为以.u_boot_list_2_cmd_2开头的符号段中的第1个。它定义为0个字节的数组start[0],
编译器其实不为它分配存储空间,那末它将指向以.u_boot_list_2_cmd_2开头的符号段中的第1个符号。
同理ll_entry_end用end[0]来标识.u_boot_list_2_cmd_2_xxx段的结尾,接下来的函数ll_entry_count返回的就是start - end的值,
即符号段.u_boot_list_2_cmd_2_xxx总字节长度。然后调用find_cmd_tbl,根据传入的.u_boot_list_2_cmd_2_xxx段的首地址和
函数ll_entry_count 返回的长度,根据命令名查找相应的符号段,即相命令对应的cmd_tbl_t结构体变量,然后返回该结构体指针。
find_cmd_tbl的实现以下:
cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len)
{
cmd_tbl_t *cmdtp;
cmd_tbl_t *cmdtp_temp = table; /* Init value */
const char *p;
int len;
int n_found = 0;
if (!cmd)
return NULL;
len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
for (cmdtp = table; cmdtp != table + table_len; 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 */
}
查找到命令名对应的cmd_tbl_t结构体变量后,cmd_process接下来将调用函数cmd_call履行cmd_tbl_t中的命令。
cmd_process中相应的代码段以下:
if (!rc) {
if (ticks)
*ticks = get_timer(0);
rc = cmd_call(cmdtp, flag, argc, argv);
if (ticks)
*ticks = get_timer(*ticks);
*repeatable &= cmdtp->repeatable;
}
if (rc == CMD_RET_USAGE)
rc = cmd_usage(cmdtp);
变量ticks用来记录命令的履行时间,repeatable为命令是不是自动重复履行标志。这两个变量都将返回到上层的调用函数。
函数cmd_call利用传入的参数,直接调用cmdtp->cmd,即:
(cmdtp->cmd)(cmdtp, flag, argc, argv);
最后,如果命令履行的返回值为CMD_RET_USAGE,代表命令履行出错,且置标CMD_RET_USAGE ,那末将调用cmd_usage,
输出简短的命令使用帮助信息。cmd_usage实现以下:
int cmd_usage(const cmd_tbl_t *cmdtp)
{
printf("%s - %s\n\n", cmdtp->name, cmdtp->usage);
#ifdef CONFIG_SYS_LONGHELP
printf("Usage:\n%s ", cmdtp->name);
if (!cmdtp->help) {
puts ("- No additional help available.\n");
return 1;
}
puts(cmdtp->help);
putc('\n');
#endif /* CONFIG_SYS_LONGHELP */
return 1;
}
3.u-boot中的子命令
部份u-boot的命令包括子命令,如env命令,它由子命令save,set,edit等组成。类似的还有sf命令。这些主命令履行时必须指定子命令。
u-boot中子命令的实现不再使用上面的gcc关键字section来指定段,只是直接定义了1个cmd_tbl_t表,并使用子命令及其运行参数初始化该表。对上述讨论中使用U_BOOT_CMD定义的命令,它们是散放在文件各处的,很难用1个全局的cmd_tbl_t表将这些命令统1定义初始化。而子命令不同,它只定义在1个或少许文件中,该cmd_tbl_t表为static类型,可以在定义时直接填充。固然,主命令还是由宏U_BOOT_CMD来定义引入。
生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠