uboot中环境变量的实现
Posted 代二毛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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卡里起始块和块数量的信息。
以上是关于uboot中环境变量的实现的主要内容,如果未能解决你的问题,请参考以下文章