uboot源码分析——main_loop

本文详细分析了U-Boot源码中的main_loop函数,包括预编译技巧、预引导命令、启动次数限制、VFD与Modem支持等功能。通过对CONFIG系列宏的解释,揭示了U-Boot在启动过程中的关键步骤,如TFTP功能、命令自动完成功能的安装以及启动模式判断。此外,文章还探讨了默认启动和命令行模式下的不同启动方式,如hush工具和一般方式,并重点解析了run_command和parse_string_outer函数的工作原理。
摘要由CSDN通过智能技术生成

接下来看/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

void 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*/
}
/common/main.c

#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"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值