uboot中环境变量的实现

1、环境变量介绍

uboot中环境变量的作用类似于全局变量,需要某个环境变量的值时调用getenv函数就可以得到。环境变量会指导程序的运行,不必修改代码重新编译,通过修改环境变量就可以改变uboot的的数据和特性。在uboot启动时bootdelay时间内按下按键就可以打断uboot的启动进入控制台,输入printenv命令就可以查看当前的环境变量,setenv可以改变环境变量的值,如果没有saveenv则修改只有本次启动有效,因为修改的是DDR里的那一份环境变量。

2、环境变量在内存中的保存形式

	bootdelay=3`\0`bootcmd=movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000`\0`.........

环境变量是类似于数组,一个接着一个加载在某段内存中,其中该段内存的起始地址保存在全局变量gd->env_addr中。每个环境变量之间以反斜杠零(\0)作为分隔符(也就是字符串的末尾符),在遍历环境变量时去找反斜杠零(\0)就可以将每个环境变量分隔开,而每个环境变量的格式也是固定的,
格式:env=value,再根据等号作为分隔符区分环境变量的名字和数值。只要拿到保存环境变量的内存起始地址,就可以解析出所有的环境变量。

3、默认环境变量和SD卡中的环境变量

环境变量在代码里有一份默认的环境变量,是用数组来实现的;在SD卡/flash等外存也存在一份,uboot会加载到内存中。首先是调用env_init函数,暂时使用默认的环境变量;后续再调用env_relocate函数将SD卡中的那一份环境变量加载到内存中,如果CRC校验通过则将gd->env_addr指向内存中从SD卡处加载的环境变量。由此可见,SD卡中的环境变量优先级高于默认的环境变量。

//初始化默认环境变量,保存在default_environment数组中
int env_init(void)
{
    
    
	gd->env_addr  = (ulong)&default_environment[0];
	gd->env_valid = 1;

	return (0);
}

char * env_ptr;

//从SD卡中重定位环境变量
void env_relocate (void)
{
    
    
	/*
	 * We must allocate a buffer for the environment
	 */
	env_ptr = (env_t *)malloc (CFG_ENV_SIZE); //申请CFG_ENV_SIZE大小的内存,此时已经初始化了堆管理器,所以可以用malloc
	DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);

	if (gd->env_valid == 0) {
    
    

		puts ("*** Warning - bad CRC, using default environment\n\n");
		show_boot_progress (-60);

		set_default_env();
	}
	else {
    
    
		env_relocate_spec (); //把外存里的环境变量加载到内存,并将内存起始地址保存在env_ptr->data
	}
	gd->env_addr = (ulong)&(env_ptr->data);
}

void env_relocate_spec (void)
{
    
    
	uint *magic = (uint*)(PHYS_SDRAM_1);

	if ((0x24564236 != magic[0]) || (0x20764316 != magic[1]))
		movi_read_env(virt_to_phys((ulong)env_ptr)); //从外存读取环境变量

	if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) //crc校验不通过则用默认的环境变量
		return use_default();
}

4、关键函数的实现思路

前提:从gd->env_addr得到环境变量在内存中的地址,并且uboot中有变量记录当前所有环境变量所占内存大小
(1)printenv思路分析:
以反斜杠零为分隔符解析处各个环境变量,然后通过控制台输出;
(2)setenv思路分析
首先通过以反斜杠零和等号为分隔符,解析处各个环境变量的名字和数值,然后去匹配是否存在要查找的环境变量,如果存在则覆盖掉原来的值;
(3)saveenv思路分析
将内存中的那份环境变量写到SD卡中特定的分区里;
(4)getenv思路分析
首先通过以反斜杠零和等号为分隔符,解析处各个环境变量的名字和数值,然后去匹配是否存在要查找的环境变量,如果存在则返回该环境变量的值;

5、可重入版本和不可重入版本的函数

可重入版本函数的名字就是在不可重入版本函数的名字后面加"_r",因为可重入版本是在不可重入版本后面出现的。不可重入函数,简单理解就是函数不能同时多个地方调用或者同时调用不安全。
(1)char *getenv (char *name);
传入环境变量的名字返回环境变量字符串所在的内存地址;如果修改返回来的环境变量字符串内容,则内存中的环境变量也会被修改,这样不安全。
(2)int getenv_r (char *name, char *buf, unsigned len);
传入环境变量的名字name,缓冲区buf,buf的长度len,返回读取到的环境变量字符串的长度。环境变量的值是保存在buf中,无论你怎么修改buf中保存的环境变量字符串也不会影响内存中的环境变量。

6、补充

在SD卡中会预留出分区来保存环境变量,在uboot的配置文件里会用宏定义来指定环境变量保存在SD卡里起始块和块数量的信息。

Guess you like

Origin blog.csdn.net/weixin_42031299/article/details/121219047