转载自:https://www.cnblogs.com/fah936861121/articles/6929858.html
一.main_loop函数执行流程和命令解释器
run_main_loop是board_r中函数运行列表init_fnc_t init_sequence_r[]最后一个函数,它又调用了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),此处为空函数。
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构建系统自动产生,
run_preboot_environment_command函数从环境变量中获取"preboot"的定义,该变量包含了一些预启动命令,
一般环境变量中不包含该项配置。
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 != -1 && s && !abortboot(stored_bootdelay)) {
run_command_list(s, -1, 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内核,至此不再返回。
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 != -1 && !(flag & FLAG_EXIT_FROM_LOOP) && //#define FLAG_EXIT_FROM_LOOP 1
(inp->peek != static_peek || b_peek(inp)));
}
上面的do-while会循环命令解析器的"命令输入解析--执行"运行模式。
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最后一个参数_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,这里定义一个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定义为:
"1890747" snippet_file_name="blog_20160921_20_3676404" name="code" class="cpp">struct cmd_tbl_s {
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使用该结构体来描述一个完整的命令。
U_BOOT_CMD_COMPLETE宏用来定义一个cmd_tbl_t结构体变量,初始化该结构体中的相应成员,并把该结构体变量存放在4字节对齐的.u_boot_list_2_cmd_2_env符号段中。如前所述,宏U_BOOT_CMD将最后一个参数_comp置为NULL,对U_BOOT_CMD_COMPLETE做了进一步的封装。所有使用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);
定义一个包含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
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中的命令。
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为命令是否自动重复执行标志。这两个变量都将返回到上层的调用函数。
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;
}
三.u-boot中的子命令