上一篇找到了我的关键点–main_loop函数,这一篇来好好看一下。
uboot中的main_loop函数是怎么工作的。
1、main_loop函数是做什么的?
start_armboot最后进入死循环调用了main_loop 函数;
uboot的目的是启动内核,那么main_loop一定会有设置启动参数和启动内核的实现;
main_loop()函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。
2、main_loop()函数内容
void main_loop(void) { const char *s; bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); if (IS_ENABLED(CONFIG_VERSION_VARIABLE)) env_set("ver", version_string); /* set version variable */ cli_init(); if (IS_ENABLED(CONFIG_USE_PREBOOT)) run_preboot_environment_command(); if (IS_ENABLED(CONFIG_UPDATE_TFTP)) update_tftp(0UL, NULL, NULL); s = bootdelay_process(); if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); autoboot_command(s); cli_loop(); panic("No CLI available"); }
env_set:设置环境变量,两个参数分别为name和value
cli_init:用于初始化hash shell的一些变量
run_preboot_environment_command:执行预定义的环境变量的命令
bootdelay_process:加载延时处理,一般用于Uboot启动后,有几秒的倒计时,用于进入命令行模式。
cli_loop:命令行模式,主要作用于Uboot的命令行交互。
bootdelay_process
const char *bootdelay_process(void) { char *s; int bootdelay; bootcount_inc(); s = env_get("bootdelay"); //先判断是否有bootdelay环境变量,如果没有,就使用menuconfig中配置的CONFIG_BOOTDELAY时间 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; if (IS_ENABLED(CONFIG_OF_CONTROL)) //是否使用设备树进行配置 bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", bootdelay); debug("### main_loop entered: bootdelay=%d\n\n", bootdelay); if (IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) bootdelay = menu_show(bootdelay); bootretry_init_cmd_timeout(); #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = env_get("failbootcmd"); } else #endif /* CONFIG_POST */ if (bootcount_error()) s = env_get("altbootcmd"); else s = env_get("bootcmd"); //获取bootcmd环境变量,用于后续的命令执行 if (IS_ENABLED(CONFIG_OF_CONTROL)) process_fdt_options(gd->fdt_blob); stored_bootdelay = bootdelay; return s; }
autoboot_command
void autoboot_command(const char *s) { debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) { bool lock; int prev; lock = IS_ENABLED(CONFIG_AUTOBOOT_KEYED) && !IS_ENABLED(CONFIG_AUTOBOOT_KEYED_CTRLC); if (lock) prev = disable_ctrlc(1); /* disable Ctrl-C checking */ run_command_list(s, -1, 0); if (lock) disable_ctrlc(prev); /* restore Ctrl-C checking */ } if (IS_ENABLED(CONFIG_USE_AUTOBOOT_MENUKEY) && menukey == AUTOBOOT_MENUKEY) { s = env_get("menucmd"); if (s) run_command_list(s, -1, 0); } }
我们看一下判断条件stored_bootdelay != -1 && s && !abortboot(stored_bootdelay
- stored_bootdelay:为环境变量的值,或者menuconfig设置的值
- s:为环境变量bootcmd的值,为后续运行的指令
- abortboot(stored_bootdelay):主要用于判断是否有按键按下。如果按下,则不执行bootcmd命令,进入cli_loop命令行模式;如果不按下,则执行bootcmd命令,跳转到加载Linux启动。
cli_loop
void cli_loop(void) { bootstage_mark(BOOTSTAGE_ID_ENTER_CLI_LOOP); #ifdef CONFIG_HUSH_PARSER parse_file_outer(); /* This point is never reached */ for (;;); //死循环 #elif defined(CONFIG_CMDLINE) cli_simple_loop(); #else printf("## U-Boot command line is disabled. Please enable CONFIG_CMDLINE\n"); #endif /*CONFIG_HUSH_PARSER*/ }
如上代码,程序只执行parse_file_outer来处理用户的输入、输出信息。
最后付一个关于main_loop()较为丰富的函数,去掉了宏定义控制的代码
void main_loop (void) { static char lastcommand[CFG_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) //是否有bootdelay char *s; int bootdelay; #endif #ifdef CONFIG_BOOTCOUNT_LIMIT //启动次数的限制 unsigned long bootcount = 0; unsigned long bootlimit = 0; char *bcs; char bcs_set[16]; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load();//读取已经启动的次数 bootcount++; bootcount_store (bootcount);//将启动次数加1再写回去保存起来 sprintf (bcs_set, "%lu", bootcount); setenv ("bootcount", bcs_set); //设置已经启动的次数到环境变量bootcount bcs = getenv ("bootlimit");//从环境变量获取启动次数的上限,此时返回的是字符串还需要转换成整数 bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_VERSION_VARIABLE //设置ver环境变量,里面保存的是uboot的版本 { extern char version_string[]; setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_AUTO_COMPLETE //命令的自动补全功能 install_auto_complete(); #endif #ifdef CONFIG_FASTBOOT//支持fastboot刷机 if (fastboot_preboot()) run_command("fastboot", 0); #endif /* 下面就是实现uboot启动延时机制bootdelay的代码 */ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); /* 从环境变量获取启动延时的秒数 */ bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); /* 检查启动次数是否超过上限*/ #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>"); /*abortboot函数是检测在bootdelay时间内是否有人按键:如果有人按键则返回1; 超过bootdelay的时间没有人按键则返回0,if条件满足则启动内核*/ if (bootdelay >= 0 && s && !abortboot (bootdelay)) { #ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* 禁止 ctrl+c 功能 */ #endif run_command (s, 0); //启动内核 #ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* 恢复 ctrl+c 功能 */ #endif } #endif /* CONFIG_BOOTDELAY */ /* * 下面是一个死循环,不停的从控制台读取命令解析,直到执行bootm命令去启动内核 */ for (;;) { len = readline (CFG_PROMPT); //从控制台读取一行指令,存放在console_buffer flag = 0; /* assume no special flags for now */ if (len > 0) strcpy (lastcommand, console_buffer); else if (len == 0) flag |= CMD_FLAG_REPEAT; if (len == -1) puts ("<INTERRUPT>\n"); else rc = run_command (lastcommand, flag); //解析并运行读取到的指令 if (rc <= 0) { /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } } }