uboot之环境变量

一、环境变量基础知识

1、环境变量的作用

环境变量的最大作用就是在我们不需要改变源代码的情况下,改变程序的执行情况。比如我们的bootdelay时间,通过修改对应的环境变量的值,就可以改变uboot开机延时的时间,而不需要修改源代码,然后编译,烧录。省去了很多的麻烦。

uboot中两份环境变量,一份环境变量是程序中的,一份是存储在SD卡的环境变量分区中的。譬如我们的环境变量machid(机器码),uboot在x210_sd.h中定义了一个机器码2456,写死在程序中,不能更改的。如果想要改变uboot中配置的机器码,可以修改x210-sd.h中对应的机器码,但是比较麻烦。所以比较简单的方法,就是使用环境变量machid来修改uboot中的机器码。

2、环境变量的工作方式

在DDR中环境变量就是default_environment数组,在重定位的时候,它会被定位到DDR中的一个内存地址处,这个地址处,这个全局数组就是我们uboot我们uboot运行时的DDR中的环境变量。

但是我们SD卡中,也有一份环境变量,而且每次开机启动的时候,环境变量的数值,都是上一次关机时,保存在SD卡中的环境变量,怎么就是程序中的default_environment数组呢?原因是这样的,就是SD卡中那份环境变量重定位时,也会被加载到这个地址(就是我们default_environment数组的基地址,所以default_environment数组中的环境变量被SD卡中的环境变量覆盖了),所以我们看到的实际DDR中的环境变量,就是SD卡中的环境变量。

 

二、环境变量在uboot是如何存储的

1、default_environment数组

uchar default_environment[CFG_ENV_SIZE] = {
#ifdef	CONFIG_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			"\0"
#endif
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif
    .........
        ......
	"\0"
};

所有的把所有的环境变量当作字符串存储在数组中,比如bootargs的存储,即使这样的"bootargs="+"root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC2,115200"+'\0';然后后面接着其他的环境变量,不同的环境变量之间只用'\0'作为分隔符。了解了环境变量在uboot中存储方式,我们就知道如何查找一个一个环境变量了。

 

三、环境变量相关的命令源码解析

1、printenv命令:/uboot/common/cmd_nvedit.c中

(1)该命令对应的函数是do_printenv函数,这个函数有两种使用方法。一个是默认打印全部的环境变量,一个是打印我们输入的环境变量。

代码:

int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int i, j, k, nxt;
	int rcode = 0;

	if (argc == 1) {		/* Print all env variables	*/
		for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
			for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)
				;
			for (k=i; k<nxt; ++k)
				putc(env_get_char(k));
			putc  ('\n');

			if (ctrlc()) {
				puts ("\n ** Abort\n");
				return 1;
			}
		}

		printf("\nEnvironment size: %d/%ld bytes\n",
			i, (ulong)ENV_SIZE);

		return 0;
	}

	for (i=1; i<argc; ++i) {	/* print single env variables	*/
		char *name = argv[i];

		k = -1;

		for (j=0; env_get_char(j) != '\0'; j=nxt+1) {

			for (nxt=j; env_get_char(nxt) != '\0'; ++nxt)
				;
			k = envmatch((uchar *)name, j);
			if (k < 0) {
				continue;
			}
			puts (name);
			putc ('=');
			while (k < nxt)
				putc(env_get_char(k++));
			putc ('\n');
			break;
		}
		if (k < 0) {
			printf ("## Error: \"%s\" not defined\n", name);
			rcode ++;
		}
	}
	return rcode;
}

函数根据输入参数的个数,分为两个功能,但是两个分支的主要架构就是三个for循环结构体,而且作用都是相同的。第一、二个for循环用来寻找一个环境变量,第三个for循环处理这个找出来的环境变量。

"bootargs="	"root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC2,115200""\0"
"bootcmd="	"bootp; "								\
	"setenv bootargs root=/dev/nfs nfsroot=${serverip}:${rootpath} "	\
	"ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}::off; "	\
	"bootm""\0"
...

这里第一、二个for循环,就是为了找到bootargs这个环境变量(它的第一个字节在哪,最后一个字节在哪),然后第三个环境变量就是来处理这个环境变量,进行环境变量的打印输出。

这里第一、二个for循环的结果就是这样了的,表i和nxt的位置标记好,然后在使用一个循环来编译i和nxt之间的所有信息,然后打印出来即可。

 

2、setenv:/uboot/common/cmd_nvedit.c中

int setenv (char *varname, char *varvalue)
{
	char *argv[4] = { "setenv", varname, varvalue, NULL };
	if (varvalue == NULL)
		return _do_setenv (0, 2, argv);
	else
		return _do_setenv (0, 3, argv);
}

该命令有两种功能,一种是删除环境变量,一种是修改环境变量的值,如果传递进来的环境变量没有数值,就会删除这个环境变量,否则更改这个环境变量的数值为传递进来的数值。

 

3、saveenv命令:/uboot/common/cmd_nvedit.c中

(1)saveenv命令对应的函数do_saveenv,在该函数中调用了saveenv函数,这个函数有很多,为了找到正确的函数定义,我们必须从x210_sd.h中找到#define CFG_ENV_IS_IN_AUTO这个宏的定义,通过可能函数中,每个函数是否被编译链接,在文件的头部我们会找到#if defined(CFG_ENV_IS_IN_AUTO) /* Environment is in Non-Volatile Device */,会根据是否定义了相关的宏来判断是否将该函数参与编译链接,因为我们在x210_ssd.h中定义了CFG_ENV_IS_IN_AUTO,所以我们确定了函数是在env_auto.c文件中定义的。因为只有这个文件才会参与编译链接的过程。(auto函数中使用了宏定义的方法去条件编译各种常见的Flash芯片可以说是一个自动程序,它会根据我们的宏定义,判断我们Flash是哪个,我们的启动方式是什么,然后执行对应的函数)。

(2)这个saveenv函数中,程序会读取我们在start.S根据OMPin判断的启动方式,然后存储在INF_REG3_REG寄存器中,这里就读取该寄存器的数值,然后判断启动方式。INF_REG3_REG是一个用户自定义的寄存器。

(3)根据寄存器的数值,我们执行了saveenv_movinand()函数,将环境变量保存 ,使用函数movi_write_env(virt_to_phys((ulong)env_ptr));

  • virt_to_phys:将虚拟地址转换成物理地址,我们知道在uboot中只有0xC0000000-0xCFFFFFFF映射到了0x3000000--0x3FFFFFFF的地址处。
  • env_ptr:env_t *env_ptr = (env_t *)(&environment[0])是一个全局变量指针,指向的是DDR环境变量,大小为16KB,写入iNand的env分区。

(4)在movi_write_env调用了movi_write_env函数,该函数中使用了raw_area_control,raw_area_control是uboot规划iNand/SD卡的原始分区表,这里记录了我们对iNand/SD卡的分区,env分区也在这里,下标是2。

 

4、uboot获取环境变量的两个函数char *getenv (char *name)和int getenv_r (char *name, char *buf, unsigned len)

(1)两个函数的不同在于,getenv是不可重入的,getenv_r是可重入的,简单来说,我们从输入的参数就可以看出来,getenv传递的参数只有命令的名字,输出参数之后一个指针,这个指针指向就是这个命令所对应的实际地址,就是uboot中保存这个环境变量的地址。所以我们修改了这个传递回来的数值,就代表修改了uboot中的环境变量。但是第二个函数,是通过输出一个接收数组来接收的,也就说copy了一份代码到buf中,这样就可以在buf中随意处理这段字符,而不会修改uboot中的环境变量。但是两者的实现原理都是一样的。所以使用后者较为安全。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值