U-BOOT环境变量实现
1.相关文件common/env_common.c 供u-boot调用的通用函数接口,它们隐藏了env的不同实现方式,比如dataflash,epprom,flash等
common/env_dataflash.c env?存储在dataflash中的实现 common/env_epprom.c env?存储在epprom中的实现 common/env_flash.c env?存储在flash中的实现
common/env_nand.c env?存储在nand中的实现
common/env_nvedit.c 实现u-boot对环境变量的操作命令
environment.c 环境变量以及一些宏定义 env如果存储在Flash中还需要Flash的支持。 2.数据结构env?在?u-boot?中通常有两种存在方式,在永久性存储介质中(?Flash NVRAM等?)在SDRAM,可以配置不使用?env?的永久存储方式,但这不常用。u-boot?在启动的时候会将存储在永久性存储介质中的?env?重新定位到?RAM?中,这样可以快速访问,同时可以通过saveenv将?RAM?中的?env?保存到永久性存储介质中。 在include/environment.h中定义了表示env的数据结构 typedef struct environment_s { ???????unsigned long crc;???/* CRC32 over data bytes */ #ifdef CFG_REDUNDAND_ENVIRONMENT ???????unsigned char flags;??/* active/obsolete flags */ #endif ???????unsigned char data[ENV_SIZE]; /* Environment data */ } env_t; 关于以上结构的说明: crc是u-boot在保存env?的时候加上去的校验头,在第一次启动时一般?crc校验会出错,这很正常,因为这时?Flash中的数据无效。 data字段保存实际的环境变量。u-boot?的?env?按?name=value”/0”的方式存储,在所有env的最后以”/0/0”表示整个?env?的结束。新的name=value对总是被添加到?env?数据块的末尾,当删除一个name=value对时,后面的环境变量将前移,对一个已经存在的环境变量的修改实际上先删除再插入。 env?可以保存在?u-boot?的?TEXT?段中,这样?env?就可以同?u-boot?一同加载入RAM中,这种方法没有测试过。 ???????上文提到u-boot会将?env?从?flash?等存储设备重定位到?RAM?中,在?env?的不同实现版本(?env_xxx.c?)中定义了?env_ptr,?它指向?env在RAM中的位置。u-boot在重定位?env后对环境变量的操作都是针对?env_ptr。 ???????env_t?中除了数据之外还包含校验头,u-boot?把env_t?的数据指针有保存在了另外一个地方,这就是?gd_t?结构(?不同平台有不同的?gd_t结构?),这里以ARM为例仅列出和?env?相关的部分 typedef struct global_data { ???????… ???????unsigned long env_off;?????????/* Relocation Offset */ ???????unsigned long env_addr;????????/* Address of Environment struct ??? */ ???????unsigned long env_valid????????/* Checksum of Environment valid */ ???????… } gd_t; <include/asm-arm/Global_data.h> gd_t.env_addr?即指向?env_ptr->data。 3.ENV?的初始化? start_armboot :?(?lib_arm/board.c?) *env_init : env_xxx.c(?xxx = nand | flash | epprom …?) env_relocate : env_common.c *env_relocate_spec : env_xxx.c(?xxx=nand | flash | eporom…?) 3.1env_init实现?env?的第一次初始化,对于nand env?(非embedded方式): Env_nand.c : env_init gd->env_addr = (ulong)&default_environment[0]; //先使gd->env_addr指向默认的环境变量 gd->env_valid = 1;// env?有效位置1 3.2 env_relocate#ifdefine ENV_IS_EMBEDDED …(略) #else env_ptr = (env_t *)malloc (CFG_ENV_SIZE); #endif if( gd->env_valid == 0) //?在?Env_annd.c : env_init?中已经将?gd->env_valid?置1 { ???????… } else ???????env_relocate_spec ();//?调用具体的?env_relocate_spec?函数 gd->env_addr = (ulong)&(env_ptr->data);//?最终完成将环境变量搬移到内存 这里涉及到两个和环境变量有关的宏 ENV_IS_EMBEDDED : env?是否存在于?u-boot TEXT?段中 CFG_ENV_SIZE : env?块的大小 实际上还需要几个宏来控制u-boot?对环境变量的处理 CFG_ENV_IS_IN_NAND : env?块是否存在于Nand Flash?中 CFG_ENV_OFFSET : env?块在?Flash?中偏移地址 3.3*env_relocate_spec这里仅分析?Nand Flash?的?env_relocate_spec?实现 如果未设置?CFG_ENV_OFFSET_REDUND,env_relocate_spec的实现如下?: void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED) ???????ulong total; ???????int ret; ???????total = CFG_ENV_SIZE; ???????ret = nand_read(&nand_info[0],CFG_ENV_OFFSET,&total,(u_char*)env_ptr); ??????if (ret || total != CFG_ENV_SIZE) ??????????????return use_default(); ???????if (crc32(0,env_ptr->data,ENV_SIZE) != env_ptr->crc) ??????????????return use_default(); #endif /* ! ENV_IS_EMBEDDED */ } 上面的代码很清楚的表明了?env_relocate_spec?的意图,调用?nand_read?将环境变量从?CFG_ENV_OFFSET?处读出,环境变量的大小为CFG_ENV_SIZE?注意?CFG_ENV_OFFSET和?CFG_ENV_SIZE?要和?Nand Flash?的块/页边界对齐。读出数据后再调用crc32?对env_ptr->data进行校验并与保存在?env_ptr->crc?的校验码对比,看数据是否出错,从这里也可以看出在系统第一次启动时,Nand Flash?里面没有存储任何环境变量,crc校验肯定回出错,当我们保存环境变量后,接下来再启动板子u-boot就不会再报crc32出错了。 4. ENV?的保存由上问的论述得知,env?将从永久性存储介质中搬到RAM里面,以后对env?的操作,比如修改环境变量的值,删除环境变量的值都是对这个?env?在RAM中的拷贝进行操作,由于RAM的特性,下次启动时所做的修改将全部消失,u-boot提供了将env?写回?永久性存储介质的命令支持?: saveenv,不同版本的?env?(?nand flash,flash …?)实现方式不同,以Nand Flash?的实现(未定义CFG_ENV_OFFSET_REDUND)为例 Env_nand.c : saveenv int saveenv(void) { ???????ulong total; ???????int ret = 0; ???????puts ("Erasing Nand..."); ???????if (nand_erase(&nand_info[0],CFG_ENV_SIZE)) ??????????????return 1; ???????puts ("Writing to Nand... "); ???????total = CFG_ENV_SIZE; ???????ret = nand_write(&nand_info[0],(u_char*)env_ptr); ???????if (ret || total != CFG_ENV_SIZE) ??????????????return 1; ???????puts ("done/n"); ???????return ret; } Nand Flash?的?saveenv?命令实现很简单,调用nand_erase?和nand_write进行Nand Flash的?erase,write。nand_write/erase使用的是u-boot?的nand驱动框架,我在做开发的过程中使用的是nand_legacy驱动,所以可以把nand_erase和nand_write改成nand_legacy_erase和nand_legacy_rw就可实现nand_legacy驱动的保存环境变量版本。 原文:http://blog.chinaunix.net/u2/70445/showart_1852111.html 1、参数表的结构定义在environment.c中,如下: #ifdef CFG_REDUNDAND_ENVIRONMENT # define ENV_HEADER_SIZE???????(sizeof(unsigned long) + 1) #else # define ENV_HEADER_SIZE???????(sizeof(unsigned long)) #endif ? //除去参数表头后参数的长度最值 #define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE) ? typedef?????struct environment_s { unsigned long?crc;????????/* CRC32 over data bytes??????*/ #ifdef CFG_REDUNDAND_ENVIRONMENT unsigned char?flags;?????????????/* active/obsolete flags???*/ #endif unsigned char?data[ENV_SIZE]; /* Environment data?????????*/ }?env_t; ? 结构env_t参数表的结构非常简单,第一个成员就是crc,用于crc32校验,第二个参数是冗余的标志,最后一个就是参数数组了。所以参数头的长度ENV_HEADER_SIZE就是crc与flags之和,即为sizeof(long)+sizeof(char)。这个结构就是在内存和flash上表示参数表的结构。 ? 参数表的最后一个成员data数组中存放所有的环境变量值,每个变量和值用‘=’号连接,而两个变量之间则通过’/0’分开,如下: ? uchar default_environment[] = { #ifdef?CONFIG_BOOTARGS "bootargs="????CONFIG_BOOTARGS????????????????"/0" #endif #ifdef?CONFIG_BOOTCOMMAND "bootcmd="????CONFIG_BOOTCOMMAND??????????????"/0" #endif …… "/0" }; ? 2、环境变量的初始化env_relocate() ???????Uboot在完成汇编部分的初始化之后,将跳到start_armboot()去执行,其中便会执行env_relocate()初始化环境变量。 ? 去除了一些不执行的代码后,这个函数如下: ? void env_relocate (void) { ??????/* ??????* We must allocate a buffer for the environment ??????*/ ??????env_ptr = (env_t *)malloc (CFG_ENV_SIZE);?// 1 ? ??????/* ??????* After relocation to RAM,we can always use the "memory" functions ??????*/ ??????env_get_char = env_get_char_memory;?// 2 ? ??????if (gd->env_valid==0) ?????????????default_env();???????????// 3 ??????else { ?????????????env_relocate_spec ();???// 4 ??????} ??????gd->env_addr = (ulong)&(env_ptr->data);?// 5 ? } ? 第一步,初始化一个全局指针,它被定义为: env_t *env_ptr = 0; 第二步,重新初始化函数指针, static uchar env_get_char_init (int index); uchar (*env_get_char)(int) =?env_get_char_init; 该函数指针原来被初始化为env_get_char_init,现在改为env_get_char_memory。对于nand flash,这两个函数是一样的。 第三步,如果flash没有参数表,则使用默认参数,这里是通过default_env()来加载。 ? void default_env(void) { ???????memset (env_ptr,sizeof(env_t)); ???????memcpy (env_ptr->data, ??????????????default_environment, ??????????????sizeof(default_environment));?//拷贝环境变量 #ifdef CFG_REDUNDAND_ENVIRONMENT ???????env_ptr->flags = 0xFF; #endif ???????env_crc_update ();???//更新crc32校验 ???????gd->env_valid = 1;?//标识环境变量可用 } 第四步,如果flash上有参数表可用,则从flash上加载,通过env_relocate_spec()来实现: ? void env_relocate_spec (void) { #if !defined(ENV_IS_EMBEDDED) //如果不是使用嵌入参数的形式,即为参数表的形式 ???????ulong total; ???????int ret; ? ???????total = CFG_ENV_SIZE;?//参数表大小,包括参数表头部 ???????//读出操作,flash设备为nand_info,偏移为CFG_ENV_OFFSET,读出的大小为total,目标地址由env_ptr所指。 ???????ret = nand_read(&nand_info[0],(u_char*)env_ptr); ? ???????//如果读出的长度不对或出错,则使用默认值 ????if (ret || total != CFG_ENV_SIZE) ??????????????return use_default(); ? ???????//如果校验出错,使用默认值 ???????if (crc32(0,ENV_SIZE) != env_ptr->crc) ??????????????return use_default(); #endif /* ! ENV_IS_EMBEDDED */ } ? 此外,uboot的参数表还支持一种被称为CFG_ENV_OFFSET_REDUND的冗余模式,它会在flash上保存两个参数表副本,这样在一个副本出错的时候,还可以从另一个副本中去读取,通过这种方式,提高了数据的安全性。 ? 第五步,gd->env_addr = (ulong)&(env_ptr->data) 即将环境变量的值赋值给全局变量gd->env_addr,这样只要通过这个全局变量就可以访问这些变量了。 ???????值得一提的是,字符串数组data里面的变量与变量之间是通过’/0’来分割的。 ? 3、环境变量的保存,保存是读取的反过程,所以跟上面的过程相似,如下: int saveenv(void) { ??????ulong total; ??????int ret = 0; ??????//先擦除 ??????if (nand_erase(&nand_info[0],CFG_ENV_SIZE)) ??????//写入 total = CFG_ENV_SIZE; ??????ret = nand_write(&nand_info[0],(u_char*)env_ptr); ??????if (ret || total != CFG_ENV_SIZE) ?????????????return 1; ??????puts ("done/n"); ??????return ret; } ? 4、读取环境变量 Uboot中经常要读取环境变量,这是通过getenv来实现的: ? / * Look up variable from environment, * return address of storage for that variable, * or NULL if not found */ ? char *getenv (char *name) { ???????int i,nxt; ? ???????for (i=0; env_get_char(i) != '/0'; i=nxt+1) { ??????????????int val; ? ??????????????for (nxt=i; env_get_char(nxt) != '/0'; ++nxt) { ?????????????????????if (nxt >= CFG_ENV_SIZE) { ????????????????????????????return (NULL); ?????????????????????} ??????????????} ??????????????if ((val=envmatch((uchar *)name,i)) < 0) ?????????????????????continue; ??????????????//通过所得的下标返回变量值的指针,由于是字符串指针,所以它在碰到’/0’符合时结束,即为该变量的值。 ??????????????return ((char *)env_get_addr(val)); ???????} ? ???????return (NULL); } ? 通过输入变量的名字,返回变量的值。 前面已经提到,函数指针env_get_char已经被初始化为env_get_char_memory: ???????该函数获取环境变量数组中下标为index的字符。 uchar env_get_char_memory (int index) { if (gd->env_valid) { ?????????return ( *((uchar *)(gd->env_addr + index)) ); } else { ?????????return ( default_environment[index] ); } } ? ? ? /************************************************************************ * Match a name / name=value pair * * s1 is either a simple 'name',or a 'name=value' pair. * i2 is the environment index for a 'name2=value2' pair. * If the names match,return the index for the value2,else NULL. */ ? 查找符号变量,如果找到则返回等号后面的字符串指针,即为变量的值。 static int envmatch (uchar *s1,int i2) { ? ???????while (*s1 == env_get_char(i2++)) ??????????????if (*s1++ == '=') ?????????????????????return(i2); ???????if (*s1 == '/0' && env_get_char(i2-1) == '=') ??????????????return(i2); ???????return(-1); } ? 如前所述,环境变量表是一个字符串数组,而其中的变量之间通过’/0’符号隔开,即是当遇到该符号时,则表示一个变量结束而另一个变量开始。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |