接下来看/common/main.c中的main_loop ();
你会发现,这个函数相当长,但是其中有不少的编译条件,很多宏是没有定义的,也就是说,最后被编译的只是其中一部分。
在预编译的时候使用 #warning “你想要打印的字符串”,这种形式可以在编译的时候确定哪些宏是被定义的,哪些是没有定义的。
可以将没有编译的部分去掉,得到有效的代码就比较少了:
但是看代码的时候总是有种强迫症,恨不得把所有的相关代码都理解,被去掉的代码,虽然没有被编译,但是我们想了解他到底做了什么:
#ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif
这个宏比较重要,最后再说。
#ifdef CONFIG_PREBOOT char *p; #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(1); /* disable Control C checking */ # endif # ifndef CONFIG_SYS_HUSH_PARSER run_command (p, 0); # 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 */
CONFIG_PREBOOT
“预引导命令:
CONFIG_PREBOOT
如果定义了该选项,则在进行引导延时的计时前或者运行自动引导命令前,检查环境变量"preboot"是否存在,如果存在则进入交互模式。
该功能在"preboot"是由程序自动生成或修改的情况下比较有用。比如,LWMON单板的代码:当引导系统时,如果用户按下特定组合键,preboot会被修改。 ”
“http://linux.chinaunix.net/techdoc/desktop/2009/01/14/1058572.shtml”
#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); sprintf (bcs_set, "%lu", bootcount); setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */
CONFIG_BOOTCOUNT_LIMIT
“启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。”
“http://www.cnblogs.com/amanlikethis/p/3555516.html”
#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) ulong bmp = 0; /* default bitmap */ extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT if (do_mdm_init) bmp = 1; /* alternate bitmap */ #endif trab_vfd (bmp); #endif /* CONFIG_VFD && VFD_TEST_LOGO */
CONFIG_VFD宏,应该是VFD屏幕方面的,我们没有用。
CONFIG_MODEM_SUPPORT宏,如果系统中有Modem,打开该功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用。
#if defined(CONFIG_UPDATE_TFTP) update_tftp (); #endif /* CONFIG_UPDATE_TFTP */
这边是启动TFTP功能,但是这里有一个疑问:CONFIG_UPDATE_TFTP并没有参与编译,但是最终build出来的uImage,还是带有tftp client的功能的。
#ifdef CONFIG_VERSION_VARIABLE { extern char version_string[]; setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */
初始化命令自动完成功能等。动态版本号功能支持代码,version_string变量是在其他文件定义的一个字符串变量,当用户改变U-Boot版本的时候会更新该变量。打开动态版本支持功能后,U-Boot在启动的时候会显示最新的版本号。
最后说一下CONFIG_SYS_HUSH_PARSER这个宏,在uboot刚启动的时候hit任意一个键,将进入命令行模式,此时执行循环代码,有两种方式:
第一种是下面代码的红字部分,采用“hush”方式的主循环。
什么是hush?可以理解成一种shell工具,用于命令的接收和解析。集成在busybox中。
第二种为一般的循环方式。
如果定义了CONFIG_SYS_HUSH_PARSER,那么将采用第一种循环方式,否则用第二种。
其实这里说的不准确,不仅是在命令行模式下,其实只要是涉及到执行命令、解析命令的语句,都是分Hush和”一般”的两种情况,随后会讲到。
我参考的uboot代码中是采用hush的方式,相对一般的方法更为复杂,hush的代码比较复杂,研究了一下没看懂,没有相关的资料,hush简略提一下,着重分析一般的方法。
下面来具体分析main_loop:
/common/main.cvoid main_loop (void) { #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) //defined char *s; int bootdelay; #endif #ifdef CONFIG_SYS_HUSH_PARSER //defined u_boot_hush_start (); #endif #ifdef CONFIG_AUTO_COMPLETE //defined install_auto_complete(); #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) //defined s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); s = getenv ("bootcmd"); debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= 0 && s && !abortboot (bootdelay)) { # ifndef CONFIG_SYS_HUSH_PARSER run_command (s, 0); # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); # endif } #endif /* CONFIG_BOOTDELAY */ /* * Main Loop for Monitor Command Processing */ #ifdef CONFIG_SYS_HUSH_PARSER parse_file_outer(); /* This point is never reached */ for (;;); #else for (;;) { len = readline (CONFIG_SYS_PROMPT); 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; } } #endif /*CONFIG_SYS_HUSH_PARSER*/ }
#ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif /common/hush.c int u_boot_hush_start(void) { if (top_vars == NULL) { top_vars = malloc(sizeof(struct variables)); top_vars->name = "HUSH_VERSION"; top_vars->value = "0.01"; top_vars->next = 0; top_vars->flg_export = 0; top_vars->flg_read_only = 1; #ifndef CONFIG_RELOC_FIXUP_WORKS u_boot_hush_reloc(); #endif } return 0; }
这里是一些简单的赋值操作。
看一下top_vars这个变量:
struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; struct variables *top_vars = &shell_ver;
看一下struct variables结构体
struct variables { char *name; char *value; int flg_export; int flg_read_only; struct variables *next; };
接下来安装命令自动补全函数
#ifdef CONFIG_AUTO_COMPLETE install_auto_complete(); #endif /common/command.c void install_auto_complete(void) { 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 }
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; }
来看一下如何安装自动补全函数。
首先在install_auto_complete函数中调用了三次install_auto_complete_handler,实参分别为"printenv"、"setenv"、"run"、var_complete。
install_auto_complete_handler函数中,首先调用find_cmd函数找到该字符串对应的命令结构体,命令结构体的定义如下:
/include/command.h
/* * Monitor Command Table */ 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 *[]); 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 *argv[], char last_char, int maxv, char *cmdv[]); #endif }; typedef struct cmd_tbl_s cmd_tbl_t;
让后将结构体中的complete函数指针设置为complete,即var_complete。
所以install_auto_complete_handler做了两件事
寻找命令结构体
对结构体的函数指针赋值。
来看一下如何寻找结构体,即函数find_cmd。
/common/command.c
cmd_tbl_t *find_cmd (const char *cmd) { int len = &__u_boot_cmd_end - &__u_boot_cmd_start; return find_cmd_tbl(cmd, &__u_boot_cmd_start, len); }
/*************************************************************************** * find command table entry for a command */ 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; /* * Some commands allow length modifiers (like "cp.b"); * compare command name only until first dot. */ 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 */ }
find_cmd函数的返回值是一个cmd_tbl_t *指针。
定义了一个int len变量,它的值为 &__u_boot_cmd_end - &__u_boot_cmd_start;
/u-boot.lds文件中有
. = .; __u_boot_cmd_start = .; .u_boot_cmd : { *(.u_boot_cmd) } __u_boot_cmd_end = .;
所以len是整个section的长度,我们查找的字符串所对应的命令结构体,就是在这个section中。
然后运行find_cmd_tbl函数,它的实参cmd对应字符串"printenv"、"setenv"、
uboot源码分析——main_loop
最新推荐文章于 2024-04-21 10:08:41 发布
本文详细分析了U-Boot源码中的main_loop函数,包括预编译技巧、预引导命令、启动次数限制、VFD与Modem支持等功能。通过对CONFIG系列宏的解释,揭示了U-Boot在启动过程中的关键步骤,如TFTP功能、命令自动完成功能的安装以及启动模式判断。此外,文章还探讨了默认启动和命令行模式下的不同启动方式,如hush工具和一般方式,并重点解析了run_command和parse_string_outer函数的工作原理。
摘要由CSDN通过智能技术生成