Chinaunix首页 | 论坛 | 博客
  • 博客访问: 503128
  • 博文数量: 119
  • 博客积分: 5054
  • 博客等级: 大校
  • 技术积分: 1305
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-03 13:13
文章分类

全部博文(119)

文章存档

2011年(4)

2010年(115)

我的朋友

分类: 嵌入式

2010-12-18 21:55:30

参考:http://blog.chinaunix.net/u2/70445/showart_1852111.html

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/cmd_nvedit.c

实现u-boot对环境变量的操作命令

environment.h

环境变量以及一些宏定义

env如果存储在Flash中还需要Flash的支持。

2.数据结构

env 在 u-boot 中通常有两种存在方式:在永久性存储介质中( Flash NVRAM等 ),在SDRAM。可以配置不使用 env 的永久存储方式,但这不常用。u-boot 在启动的时候会将存储在永久性存储介质中的 env 重新定位到 RAM 中,这样可以快速访问,同时可以通过saveenv将 RAM 中的 env 保存到永久性存储介质中。

在include/environment.h中定义了表示env的数据结构

#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;

这个结构就是在内存和flash上表示环境变量参数表的结构。
关于以上结构的说明:

ENV_HEADER_SIZE定义了env头部长度,为crc与flags之和,即为sizeof(long)+sizeof(char)。

crc是u-boot在保存env 的时候加上去的校验头,在第一次启动时一般 crc校验会出错,这很正常,因为这时 Flash中的数据无效。

flags是冗余标志。

data字段保存实际的环境变量。u-boot 的 env 按 name=value”\0”的方式存储,在所有env的最后以”\0\0”表示整个 env 的结束。新的name=value对总是被添加到 env 数据块的末尾,当删除一个name=value对时,后面的环境变量将前移,对一个已经存在的环境变量的修改实际上先删除再插入。

例如定义在env_common.c文件中的默认环境变量参数列表:

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

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;

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.1 env_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_nand.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出错了。

#if !defined(ENV_IS_EMBEDDED)
static void use_default()
{
	puts ("*** Warning - bad CRC or NAND, using default environment\n\n");
	set_default_env();
}
#endif

其中 set_default_env() 函数设置默认环境变量参数。

void set_default_env(void)
{
	if (sizeof(default_environment) > ENV_SIZE) {
		puts ("*** Error - default environment is too large\n\n");
		return;
	}

	memset(env_ptr, 0, sizeof(env_t));
	memcpy(env_ptr->data, default_environment,
	       sizeof(default_environment));
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
	env_ptr->flags = 0xFF;
#endif
	env_crc_update ();
	gd->env_valid = 1;
}

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驱动的保存环境变量版本。

5.Env 的读取

Uboot中经常要读取环境变量,这是通过getenv来实现的:
cmd_nvedit.c

/ * 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);
}

通过输入变量的名字,返回变量的值。

获取环境变量数组中下标为index的字符:

uchar env_get_char (int index)
{
	uchar c;

	/* if relocated to RAM */
	if (gd->flags & GD_FLG_RELOC)
		c = env_get_char_memory(index);
	else //对于nand flash,这两个env_get_char函数最终是一样的。
		c = env_get_char_init(index);

	return (c);
}

调用环境变量内存查找函数

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’符号隔开,即是当遇到该符号时,则表示一个变量结束而另一个变量开始。

阅读(1369) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~