U-Boot 启动过程和源码分析(第二阶段)-main_loop分析
1> main_loop common/main.c
/****************************************************************************/ void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { , };
int len;
int rc = ;
int flag;
#endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = ;
unsigned long bootlimit = ;
char *bcs;
char bcs_set[];
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
ulong bmp = ; /* default bitmap */
extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT
if (do_mdm_init)
bmp = ; /* alternate bitmap */
#endif
trab_vfd (bmp);
#endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMIT
bootcount = bootcount_load();
bootcount++;
bootcount_store (bootcount);
sprintf (bcs_set, "%lu", bootcount);
setenv ("bootcount", bcs_set);
bcs = getenv ("bootlimit");
bootlimit = bcs ? simple_strtoul (bcs, NULL, ) : ;
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_MODEM_SUPPORT
debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
if (do_mdm_init) {
char *str = strdup(getenv("mdm_cmd"));
setenv ("preboot", str); /* set or delete definition */
if (str != NULL)
free (str);
mdm_init(); /* wait for modem connection */
}
#endif /* CONFIG_MODEM_SUPPORT */ #ifdef CONFIG_VERSION_VARIABLE
{
extern char version_string[]; setenv ("ver", version_string); /* set version variable */
}
#endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start ();
#endif #if defined(CONFIG_HUSH_INIT_VAR)
hush_init_var ();
#endif #ifdef CONFIG_AUTO_COMPLETE
install_auto_complete(); //安装自动补全的函数,分析如下
#endif #ifdef CONFIG_PREBOOT
if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (p, );
# else
parse_string_outer(p, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
#endif /* CONFIG_PREBOOT */ #if defined(CONFIG_UPDATE_TFTP)
update_tftp ();
#endif /* CONFIG_UPDATE_TFTP */ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, ) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); # ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_POST
if (gd->flags & GD_FLG_POSTFAIL) {
s = getenv("failbootcmd");
}
else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd"); //获取引导命令。分析见下面。 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= && s && !abortboot (bootdelay)) {
//如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。abortboot函数的分析见下面。
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, );//运行引导内核的命令。这个命令是在配置头文件中定义的。run_command的分析在下面。
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
} # ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s) {
# ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, );
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
}
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */ #ifdef CONFIG_AMIGAONEG3SE
{
extern void video_banner(void);
video_banner();
}
#endif /*
* Main Loop for Monitor Command Processing
*/
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= ) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT); //CONFIG_SYS_PROMPT的意思是回显字符,一般是“>”。这是由配置头文件定义的
flag = ; /* assume no special flags for now */
if (len > )
strcpy (lastcommand, console_buffer); //保存输入的数据。
else if (len == )
flag |= CMD_FLAG_REPEAT; ;//如果输入数据为零,则重复执行上次的命令,如果上次输入的是一个命令的话
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -) {
/* -2 means timed out, retry autoboot
*/
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, , , NULL);
# else
return; /* retry autoboot */
# endif
}
#endif if (len == -)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag); //执行命令 if (rc <= ) {//执行失败,则清空记录
/* invalid command or not repeatable, forget it */
lastcommand[] = ;
}
}
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
2> 自动补全
int var_complete(int argc, char *argv[], char last_char, int maxv, char *cmdv[])
{
static char tmp_buf[];
int space; space = last_char == '\0' || last_char == ' ' || last_char == '\t'; if (space && argc == )
return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); if (!space && argc == )
return env_complete(argv[], maxv, cmdv, sizeof(tmp_buf), tmp_buf); return ;
} static void install_auto_complete_handler(const char *cmd,
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]))
{
cmd_tbl_t *cmdtp; cmdtp = find_cmd(cmd);
if (cmdtp == NULL)
return; cmdtp->complete = complete; //命令结构体的complete指针指向传入的函数。
} void install_auto_complete(void)
{
#if defined(CONFIG_CMD_EDITENV)
install_auto_complete_handler("editenv", var_complete);
#endif
install_auto_complete_handler("printenv", var_complete);
install_auto_complete_handler("setenv", var_complete);
#if defined(CONFIG_CMD_RUN)
install_auto_complete_handler("run", var_complete);
#endif
}
可以看到将editenv、printenv、setenv和run的自动补全函数安装为 var_complete。var_complete的功能是根据给出的前缀字符串,找出所有前缀相同的命令。
每个命令在内存中用一个cmd_tbl_t 表示。include/command.h
struct cmd_tbl_s {
char *name; /* 命令名,输入的就是它 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 允许自动重发,也就是在按下空格键之后执行最后一条命令。 */ int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 实现命令的参数 */
char *usage; /* 短的提示信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 详细的帮助信息。 */
#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; extern cmd_tbl_t __u_boot_cmd_start;
extern cmd_tbl_t __u_boot_cmd_end; #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} #define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) /
{#name, maxargs, rep, cmd, usage, help} /*uboot中的命令使用U_BOOT_CMD这个宏声明来注册进系统,链接脚本会把所有的cmd_tbl_t结构体放在相邻的地方。
链接脚本中的一些内容如下: */
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
/*可见,__u_boot_cmd_start 和__u_boot_cmd_end 分别对应命令结构体在内存中开始和结束的地址。 */
3> abortboot函数的分析 abortboot是uboot在引导期间的延时函数。期间可以按键进入uboot的命令行。common/main.c
static __inline__ int abortboot(int bootdelay)
{
int abort = ; #ifdef CONFIG_MENUPROMPT
printf(CONFIG_MENUPROMPT);
#else
printf("Hit any key to stop autoboot: %2d ", bootdelay);
#endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK
//如果定义了这个宏,即使定义延时为0,也会检查一次是否有按键按下。
//只要在这里执行之前按键,还是能进入uboot的命令行。
/*
* Check if key already pressed
* Don't check if bootdelay < 0
*/
if (bootdelay >= ) {
if (tstc()) { // 测试是否有按键按下
(void) getc(); //接收按键值
puts ("\b\b\b 0");
abort = ; //修改标记,停止自动引导
}
}
#endif while ((bootdelay > ) && (!abort)) {
//如果延时大于零并且停止标记没有赋值则进入延时循环,直到延时完或者接收到了按 键
int i; --bootdelay;
/* delay 100 * 10ms */
//每秒中测试按键100次,之后延时10ms。
for (i=; !abort && i<; ++i) {
if (tstc()) { /* we got a key press */
abort = ; //修改标记,停止自动引导
bootdelay = ; //延时归零
# ifdef CONFIG_MENUKEY
menukey = getc();
# else
(void) getc(); /* 获取按键*/
# endif
break;
}
udelay(); //延时10000us,也就是10ms
} printf("\b\b\b%2d ", bootdelay);//打印当前剩余时间
//可以看到uboot延时的单位是秒,如果想提高延时的精度,
//比如想进行10ms级的延时,将udelay(10000)改为udelay(100)就可以了 。 } putc('\n'); #ifdef CONFIG_SILENT_CONSOLE
if (abort)
gd->flags &= ~GD_FLG_SILENT;
#endif return abort;//返回结果:1-停止引导,进入命令行; 0-引导内核。
}
# endif /* CONFIG_AUTOBOOT_KEYED */
#endif /* CONFIG_BOOTDELAY >= 0 */
4> 引导命令
/****************************************************************************
* returns:
* 1 - command executed, repeatable
* 0 - command executed but not repeatable, interrupted commands are
* always considered not repeatable
* -1 - not executed (unrecognized, bootd recursion or too many args)
* (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is
* considered unrecognized)
*
* WARNING:
*
* We must create a temporary copy of the command since the command we get
* may be the result from getenv(), which returns a pointer directly to
* the environment data, which may change magicly when the command we run
* creates or modifies environment variables (like "bootp" does).
*/ int run_command (const char *cmd, int flag)
{
cmd_tbl_t *cmdtp;
char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char finaltoken[CONFIG_SYS_CBSIZE];
char *str = cmdbuf;
char *argv[CONFIG_SYS_MAXARGS + ]; /* NULL terminated */
int argc, inquotes;
int repeatable = ;
int rc = ; #ifdef DEBUG_PARSER
printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
puts ("\"\n");
#endif clear_ctrlc(); /* forget any previous Control C */ if (!cmd || !*cmd) {
return -; /* 空命令 */
} if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { //命令太长
puts ("## Command too long!\n");
return -;
} strcpy (cmdbuf, cmd);//将命令拷贝到临时命令缓冲cmdbuf /* Process separators and check for invalid
* repeatable commands
*/ #ifdef DEBUG_PARSER
printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
while (*str) { /*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
for (inquotes = , sep = str; *sep; sep++) { //寻找分割符或者命令尾部。相邻的句子之间是用;隔开的。每次处理一句命令
if ((*sep=='\'') &&
(*(sep-) != '\\'))
inquotes=!inquotes; if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-) != '\\')) /* and NOT escaped */
break;
} /*
* Limit the token to data between separators
*/
token = str; //token指向命令的开头
if (*sep) { //如果是分隔符的话,将分隔符替换为空字符
str = sep + ; /* str指向下一句的开头*/
*sep = '\0';
}
else
str = sep; /* 如果没有其它命令了,str指向命令尾部 */
#ifdef DEBUG_PARSER
printf ("token: \"%s\"\n", token);
#endif /* find macros in this token and replace them */
process_macros (token, finaltoken); //将token指向的命令中的宏替换掉,如把$(kernelsize)替换成内核的大小 /* Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == ) {
//将每一个参数用‘/0’隔开,argv中的每一个指针指向一个参数的起始地址。
//返回值为参数的个数
rc = -; /* no command at all */
continue;
} /* Look up command in command table */
if ((cmdtp = find_cmd(argv[])) == NULL) {
//第一个参数就是要运行的命令,首先在命令表中找到它的命令结构体的指针
printf ("Unknown command '%s' - try 'help'\n", argv[]);
rc = -; /* give up after bad command */
continue;
} /* found - check max args */
if (argc > cmdtp->maxargs) {//检查参数个数是否过多
cmd_usage(cmdtp);
rc = -;
continue;
} #if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion */
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("'bootd' recursion detected\n");
rc = -;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif /* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != ) {//调用命令执行函数。这是最重要的一步。
rc = -;
} repeatable &= cmdtp->repeatable;;//设置命令自动重复执行的标志。也就是只按下enter键是否可以执行最近执行的命令 . /* Did the user stop this? */
if (had_ctrlc ())
//检查是否有ctrl+c按键按下,如果有,结束当前命令。
//本函数并没有从中断接收字符,接收ctrl+c的是一些执行命令的函数。
return -; /* if stopped then not repeatable */
} return rc ? rc : repeatable;
}
U-Boot 启动过程和源码分析(第二阶段)-main_loop分析的更多相关文章
- [Spring Boot] Spring Boot启动过程源码分析
关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Spring-boot 1.5.6)的角度来看看Spring Boot的启动过程到底是怎么样的,为何以往纷繁复杂的配置到 ...
- Spring Boot启动过程源码分析--转
https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- Spring Boot启动过程(七):Connector初始化
Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...
- Spring Boot启动过程及回调接口汇总
Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...
- Spring Boot启动过程(三)
我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...
- Spring Boot启动过程(一)
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- 转:Spring Boot启动过程
之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...
- Spring Boot启动过程(二)
书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...
随机推荐
- 《Java程序员面试笔试宝典》之Java与C/C++有什么异同
Java与C++都是面向对象语言,都使用了面向对象思想(例如封装.继承.多态等),由于面向对象有许多非常好的特性(继承.组合等),使得二者都有很好的可重用性. 需要注意的是,二者并非完全一样,下面主要 ...
- 详细解读Jquery各Ajax函数:$.get(),$.post(),$.ajax(),$.getJSON() —(转)
一,$.get(url,[data],[callback]) 说明:url为请求地址,data为请求数据的列表(是可选的,也可以将要传的参数写在url里面),callback为请求成功后的回调函数,该 ...
- [think in java]知识点学习
java中 全部数值都有正负号,不存在无符号整数. java中的基本类型存储在堆栈中. 其它对象存储在堆中. java确保数组会被初始化,并且不能在它的范围之外被訪问. 下面代码在c和c++中是合法的 ...
- [Protractor] Use protractor to catch errors in the console
For any reason, there is an error in your code, maybe something like undefined error. Protractor sti ...
- Servlet实现Session
(1)首先看一下项目的结构 是在tomcat--webaps下的myWebSites项目 在myWebSites下有仅仅有WEB-INF目录 在WEB-INF目录中有 一下目录(在classes目录 ...
- mysql 5.6 General error: 1364 Field mysql 严格模式导致
问题:SQLSTATE[HY000]: General error: 1364 Field 解决方法:set global sql-mode=”NO_AUTO_CREATE_USER,NO_ENGIN ...
- 改变navigationbar的底部线条颜色
[[UINavigationBar appearance] setBackgroundImage:[UIImage new]forBarMetrics:UIBarMetricsDefault]; CG ...
- (转) Pointers
原地址 http://www.cplusplus.com/doc/tutorial/pointers/ Pointers In earlier chapters, variables have bee ...
- C++之类和对象——C++ primer plus学习(一)
一.类的构造函数和析构函数 1.构造函数: 1)试图将类成员名称用作构造函数的参数名是错误的. 2)构造函数的参数表示的不是类成员, 而是赋给类成员的值. 3)为了避免这种混乱,一种 ...
- tomcat解决乱码
今天遇到个问题,太尴尬了: 本来做好并测试号的项目提交到svn, 组员下了之后,又部分url传递的中文参数在另一个jsp接收出现乱码(只有我的不乱码). 改了之后更尴尬的事情发生了:组员的全部不乱码, ...