免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1960 | 回复: 0
打印 上一主题 下一主题

U-BOOT环境变量实现 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-02-22 16:58 |只看该作者 |倒序浏览
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;
ta.h>
gd_t.env_addr 即指向 env_ptr->data。





3.ENV 的初始化

500)this.width=500;" border=0>
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_OFFSET, CFG_ENV_SIZE))
              return 1;

       puts ("Writing to Nand... ");
       total = CFG_ENV_SIZE;
       ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (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驱动的保存环境变量版本。
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, 0, 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], 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 */
}
此外,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_OFFSET, CFG_ENV_SIZE))
      //写入
total = CFG_ENV_SIZE;
      ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (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))
                     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 / pair
*
* s1 is either a simple 'name', or a ' 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’符号隔开,即是当遇到该符号时,则表示一个变量结束而另一个变量开始。
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/91445/showart_2184050.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP