U-BOOT环境变量的获取和保存的实现分析

本文主要以U-boot(1.1.6)为例进行说明。
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 保存到永久性存储介质中。

在tools/env/fw_env.c中定义了表示env的数据结构
typedef struct environment_s {
    ulong crc;            /* CRC32 over data bytes    */
    uchar flags;            /* active or obsolete */
    uchar *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 相关的部分
这个结构体在U-Boot的include/asm-arm/global_data.h中定义如下:
typedef    struct    global_data {
    bd_t        *bd;   //与板子相关的结构
    unsigned long    flags;
    unsigned long    baudrate;
    unsigned long    have_console;    /* serial_init() was called */
    unsigned long    reloc_off;    /* Relocation Offset */
    unsigned long    env_addr;    /* Address  of Environment struct */
........................
} gd_t;
gd_t.env_addr 即指向 env_ptr->data。

3.ENV的初始化
1):env_init函数
我们来看一下env_init函数,env 存储在不同的存储介质中,有不同的实现函数,这里以先以norflash,为例,它在common/env_flash.c中
在common/env_flash.c中对env_ptr定义如下:
char * env_name_spec = "Flash";

#ifdef ENV_IS_EMBEDDED

extern uchar environment[];
env_t *env_ptr = (env_t *)(&environment[0]); //env_ptr指向默认环境参数存放地址

#ifdef CMD_SAVEENV
/* static env_t *flash_addr = (env_t *)(&environment[0]);-broken on ARM-wd-*/
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;
#endif

#else /* ! ENV_IS_EMBEDDED */

env_t *env_ptr = (env_t *)CFG_ENV_ADDR;   //env_ptr指向norflash环境参数存放地址
#ifdef CMD_SAVEENV
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;
#endif

#endif /* ENV_IS_EMBEDDED */
注:env_ptr在不同的存储介质中都有相应的定义

int  env_init(void)
{
#ifdef CONFIG_OMAP2420H4
    int flash_probe(void);

    if(flash_probe() == 0)
        goto bad_flash;
#endif
    if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {  //使用norflash中环境参数
        gd->env_addr  = (ulong)&(env_ptr->data);
        gd->env_valid = 1;   标识环境变量可用
        return(0);
    }
#ifdef CONFIG_OMAP2420H4
bad_flash:
#endif
    gd->env_addr  = (ulong)&default_environment[0];//否则使用默认的环境参数
    gd->env_valid = 0;  使用默认环境变量参数,gd->env_valid设置为0,标识环境变量不可用
    return (0);
}
实现 env的第一次初始化,对于nand env(非embedded方式)它在common/env_nand.c中:
char * env_name_spec = "NAND";


#ifdef ENV_IS_EMBEDDED
extern uchar environment[];
env_t *env_ptr = (env_t *)(&environment[0]); //env_ptr指向默认环境参数存放地址
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr = 0;   //env_ptr初始化为空指针
#endif /* ENV_IS_EMBEDDED */


/* local functions */
#if !defined(ENV_IS_EMBEDDED)
static void use_default(void);
#endif

int env_init(void)
{
#if defined(ENV_IS_EMBEDDED)  
    ulong total;
    int crc1_ok = 0, crc2_ok = 0;
    env_t *tmp_env1, *tmp_env2;

    total = CFG_ENV_SIZE;

    tmp_env1 = env_ptr;
    tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE);

    crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
    crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);

    if (!crc1_ok && !crc2_ok)
        gd->env_valid = 0;
    else if(crc1_ok && !crc2_ok)
        gd->env_valid = 1;
    else if(!crc1_ok && crc2_ok)
        gd->env_valid = 2;
    else {
        /* both ok - check serial */
        if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
            gd->env_valid = 2;
        else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
            gd->env_valid = 1;
        else if(tmp_env1->flags > tmp_env2->flags)
            gd->env_valid = 1;
        else if(tmp_env2->flags > tmp_env1->flags)
            gd->env_valid = 2;
        else /* flags are equal - almost impossible */
            gd->env_valid = 1;
    }

    if (gd->env_valid == 1)
        env_ptr = tmp_env1;
    else if (gd->env_valid == 2)
        env_ptr = tmp_env2;
#else /* ENV_IS_EMBEDDED */
    gd->env_addr  = (ulong)&default_environment[0];//非embedded方式
    gd->env_valid = 1; //env有效位置1,标识环境变量可用
#endif /* ENV_IS_EMBEDDED */

    return (0);
}
2)环境变量的初始化env_relocate
common/env_common.c
void env_relocate (void)
{
    DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
        gd->reloc_off);

#ifdef CONFIG_AMIGAONEG3SE  //未定义
    enable_nvram();
#endif

#ifdef ENV_IS_EMBEDDED  //未定义
    /*
     * The environment buffer is embedded with the text segment,
     * just relocate the environment pointer
     */
    env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
    DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
    /*
     * We must allocate a buffer for the environment
     */
    env_ptr = (env_t *)malloc (CFG_ENV_SIZE);  //env_ptr重定位到内存空间
    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif

    /*
     * After relocation to RAM, we can always use the "memory" functions
     */
    env_get_char = env_get_char_memory; //重新初始化函数指针,该函数指针原来在common/env_common.c文件中被初始化为env_get_char_init,现在改为env_get_char_memory。对于nand flash,这两个函数是一样的。

    if (gd->env_valid == 0) { //在 env_annd.c和env_flash.c : env_init 中已经将 gd->env_valid 置1

#if defined(CONFIG_GTH)    || defined(CFG_ENV_IS_NOWHERE)    /* Environment not changable */
        puts ("Using default environment\n\n");
#else
        puts ("*** Warning - bad CRC, using default environment\n\n");
        SHOW_BOOT_PROGRESS (-1);
#endif

        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 CFG_REDUNDAND_ENVIRONMENT
        env_ptr->flags = 0xFF;
#endif
        env_crc_update (); //更新crc32校验
        gd->env_valid = 1; //标识环境变量可用
    }
    else {
        env_relocate_spec ();//如果flash上有参数表可用,则从flash上加载,通过调用具体的env_relocate_spec函数来实现
    }
    gd->env_addr = (ulong)&(env_ptr->data); //最终完成将环境变量搬移到内存,即将环境变量的值赋值给全局变量gd->env_addr,这样只要通过这个全局变量就可以访问这些变量了。值得一提的是,字符串数组data里面的变量与变量之间是通过’\0’来分割的。

#ifdef CONFIG_AMIGAONEG3SE
    disable_nvram();
#endif
}
这里涉及到两个和环境变量有关的宏,都在include/configs/smdk2410.h配置文件中定义
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 中偏移地址
#define PHYS_FLASH_1            0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE          PHYS_FLASH_1

#define CONFIG_AMD_LV400        1       /* uncomment this if you have a LV400 flash */
#if 0
#define CONFIG_AMD_LV800        1       /* uncomment this if you have a LV800 flash */
#endif

#define CFG_MAX_FLASH_BANKS     1       /* max number of memory banks */
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE         0x00100000 /* 1MB */
#define CFG_MAX_FLASH_SECT      (19)    /* max number of sectors on one chip */
#define CFG_ENV_ADDR            (CFG_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE         0x00080000 /* 512KB */
#define CFG_MAX_FLASH_SECT      (11)    /* max number of sectors on one chip */
#define CFG_ENV_ADDR            (CFG_FLASH_BASE + 0x070000) /* addr of environment */
#endif

/* timeout values are in ticks */
#define CFG_FLASH_ERASE_TOUT    (5*CFG_HZ) /* Timeout for Flash Erase */
#define CFG_FLASH_WRITE_TOUT    (5*CFG_HZ) /* Timeout for Flash Write */

#define CFG_ENV_IS_IN_FLASH     1
#define CFG_ENV_SIZE            0x10000 /* Total Size of Environment Sector */

3、env_relocate_spec
env_relocate_spec针对不同的存储介质有不同的实现
env_relocate_spec在common/env_flash.c中的实现
void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED) || defined(CFG_ENV_ADDR_REDUND)
#ifdef CFG_ENV_ADDR_REDUND  //未定义,boot的参数表还支持一种被称为CFG_ENV_OFFSET_REDUND的冗余模式,它会在flash上保存两个参数表副本,这样在一个副本出错的时候,还可以从另一个副本中去读取,通过这种方式,提高了数据的安全性。
    if (gd->env_addr != (ulong)&(flash_addr->data)) {
        env_t * etmp = flash_addr;
        ulong ltmp = end_addr;

        flash_addr = flash_addr_new;
        flash_addr_new = etmp;

        end_addr = end_addr_new;
        end_addr_new = ltmp;
    }

    if (flash_addr_new->flags != OBSOLETE_FLAG &&
        crc32(0, flash_addr_new->data, ENV_SIZE) ==
        flash_addr_new->crc) {
        char flag = OBSOLETE_FLAG;

        gd->env_valid = 2;
        flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new);
        flash_write(&flag,
                (ulong)&(flash_addr_new->flags),
                sizeof(flash_addr_new->flags));
        flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new);
    }

    if (flash_addr->flags != ACTIVE_FLAG &&
        (flash_addr->flags & ACTIVE_FLAG) == ACTIVE_FLAG) {
        char flag = ACTIVE_FLAG;

        gd->env_valid = 2;
        flash_sect_protect (0, (ulong)flash_addr, end_addr);
        flash_write(&flag,
                (ulong)&(flash_addr->flags),
                sizeof(flash_addr->flags));
        flash_sect_protect (1, (ulong)flash_addr, end_addr);
    }

    if (gd->env_valid == 2)
        puts ("*** Warning - some problems detected "
              "reading environment; recovered successfully\n\n");
#endif /* CFG_ENV_ADDR_REDUND */
    memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE);  //读出操作,将环境参数重定向到RAM中
#endif /* ! ENV_IS_EMBEDDED || CFG_ENV_ADDR_REDUND */
}

env_relocate_spec在common/env_nand.c中的实现
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 */
}

static void use_default()
{
        puts ("*** Warning - bad CRC or NAND, using default environment\n\n");

        if (default_environment_size > CFG_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,
                        default_environment_size);
        env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);
        gd->env_valid = 1;  //设置环境参数有效

}

4、读取环境变量
Uboot中经常要读取环境变量,这是通过getenv来实现的:
它定义在common/cmd_nvedit.c文件中
char *getenv (char *name)
{
 int i, nxt;
 WATCHDOG_RESET();
 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;
  return ((char *)env_get_addr(val));
 }
 return (NULL);
}
这里重点理解env_get_char函数,它定义在common/env_common.c中:
static uchar env_get_char_init (int index);
uchar (*env_get_char)(int) = env_get_char_init;
/************************************************************************
 * Default settings to be used when no valid environment is found
 */
#define XMK_STR(x) #x
#define MK_STR(x) XMK_STR(x)
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
 "bootargs=" CONFIG_BOOTARGS   "\0"
#endif
.................
static uchar env_get_char_init (int index)
{
 uchar c;
 /* if crc was bad, use the default environment */
 if (gd->env_valid)
 {
  c = env_get_char_spec(index);
 } else {
  c = default_environment[index];
 }
 return (c);
}
注意:环境变量的初始化函数env_relocate中,将env_get_char函数指针由原来在common/env_common.c文件中被初始化为env_get_char_init,又重新初始化为env_get_char_memory了。对于nor和nand flash,这两个函数是一样的。
common/env_common.c
uchar env_get_char_memory (int index)
{
    if (gd->env_valid) {
        return ( *((uchar *)(gd->env_addr + index)) );
    } else {
        return ( default_environment[index] );
    }
}  该函数获取环境变量数组中下标为index的字符。
这里 gd->env_valid参数在start_armboot函数中的初始化函数例表中的env_init函数中设置,如果配置参数保存在 flash中,gd->env_valid被设置为1,这里就通过env_get_char_spec函数从flash中取参数,否则 gd->env_valid设置为0,使用默认环境变量参数,默认环境变量参数定义在u-boot的common/env_common.c文件 uchar default_environment[] ,也就是include/configs/smdk2410.h配置文件中配置的参数。这里针对不同的flash存储芯片有不同的 env_get_char_spec定义
common/env_flash.c
uchar env_get_char_spec (int index)
{
 return ( *((uchar *)(gd->env_addr + index)) );
}
common/env_nand.c
DECLARE_GLOBAL_DATA_PTR;
uchar env_get_char_spec (int index)
{
 return ( *((uchar *)(gd->env_addr + index)) );
}
common/env_nvram.c
#ifdef CONFIG_AMIGAONEG3SE
uchar env_get_char_spec (int index)
{
#ifdef CFG_NVRAM_ACCESS_ROUTINE
 uchar c;
 nvram_read(&c, CFG_ENV_ADDR+index, 1);
 return c;
#else
 uchar retval;
 enable_nvram();
 retval = *((uchar *)(gd->env_addr + index));
 disable_nvram();
 return retval;
#endif
}
#else
uchar env_get_char_spec (int index)
{
#ifdef CFG_NVRAM_ACCESS_ROUTINE
 uchar c;
 nvram_read(&c, CFG_ENV_ADDR+index, 1);
 return c;
#else
 return *((uchar *)(gd->env_addr + index));
#endif
}
#endif
env_get_char_init函数功能就是如果保存了参数到flsah中就调用env_get_char_spec从指定的flash地址中读取参数字符,否则就从默认环境变量参数中读取参数字符。
理解完env_get_char_init函数后,再来看envmatch函数,定义在common/cmd_nvedit.c
/************************************************************************
 * 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’符号隔开,即是当遇到该符号时,则表示一个变量结束而另一个变量开始。
common/env_common.c
uchar *env_get_addr (int index)
{
 if (gd->env_valid) {
  return ( ((uchar *)(gd->env_addr + index)) );
 } else {
  return (&default_environment[index]);
 }
}
这个函数功能是返回找到的环境变量字符串数组地址。

5、环境变量的设置过程
commom/cmd_nvedit.c
void setenv (char *varname, char *varvalue)
{
 char *argv[4] = { "setenv", varname, varvalue, NULL };
 _do_setenv (0, 3, argv);
}
int do_setenv ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
 if (argc < 2) {
  printf ("Usage:\n%s\n", cmdtp->usage);
  return 1;
 }
 return _do_setenv (flag, argc, argv);
}
int _do_setenv (int flag, int argc, char *argv[])
{
    int   i, len, oldval;
    int   console = -1;
    uchar *env, *nxt = NULL;
    char *name;
    bd_t *bd = gd->bd;

    uchar *env_data = env_get_addr(0);

    if (!env_data)    /* need copy in RAM */
        return 1;

    name = argv[1];

    if (strchr(name, '=')) {
        printf ("## Error: illegal character '=' in variable name \"%s\"\n", name);
        return 1;
    }

    /*
     * search if variable with this name already exists
     */
    oldval = -1;
    for (env=env_data; *env; env=nxt+1) {
        for (nxt=env; *nxt; ++nxt)
            ;
        if ((oldval = envmatch((uchar *)name, env-env_data)) >= 0)
            break;
    }

    /*
     * Delete any existing definition
     */
    if (oldval >= 0) {
#ifndef CONFIG_ENV_OVERWRITE

        /*
         * Ethernet Address and serial# can be set only once,
         * ver is readonly.
         */
        if ( (strcmp (name, "serial#") == 0) ||
            ((strcmp (name, "ethaddr") == 0)
#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR)
             && (strcmp ((char *)env_get_addr(oldval),MK_STR(CONFIG_ETHADDR)) != 0)
#endif    /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */
            ) ) {
            printf ("Can't overwrite \"%s\"\n", name);
            return 1;
        }
#endif

        /* Check for console redirection */
        if (strcmp(name,"stdin") == 0) {
            console = stdin;
        } else if (strcmp(name,"stdout") == 0) {
            console = stdout;
        } else if (strcmp(name,"stderr") == 0) {
            console = stderr;
        }

        if (console != -1) {
            if (argc < 3) {        /* Cannot delete it! */
                printf("Can't delete \"%s\"\n", name);
                return 1;
            }

            /* Try assigning specified device */
            if (console_assign (console, argv[2]) < 0)
                return 1;

#ifdef CONFIG_SERIAL_MULTI
            if (serial_assign (argv[2]) < 0)
                return 1;
#endif
        }

        /*
         * Switch to new baudrate if new baudrate is supported
         */
        if (strcmp(argv[1],"baudrate") == 0) {
            int baudrate = simple_strtoul(argv[2], NULL, 10);
            int i;
            for (i=0; i<N_BAUDRATES; ++i) {
                if (baudrate == baudrate_table[i])
                    break;
            }
            if (i == N_BAUDRATES) {
                printf ("## Baudrate %d bps not supported\n",
                    baudrate);
                return 1;
            }
            printf ("## Switch baudrate to %d bps and press ENTER ...\n",
                baudrate);
            udelay(50000);
            gd->baudrate = baudrate;
#ifdef CONFIG_PPC
            gd->bd->bi_baudrate = baudrate;
#endif

            serial_setbrg ();
            udelay(50000);
            for (;;) {
                if (getc() == '\r')
                      break;
            }
        }

        if (*++nxt == '\0') {
            if (env > env_data) {
                env--;
            } else {
                *env = '\0';
            }
        } else {
            for (;;) {
                *env = *nxt++;
                if ((*env == '\0') && (*nxt == '\0'))
                    break;
                ++env;
            }
        }
        *++env = '\0';
    }

#ifdef CONFIG_NET_MULTI
    if (strncmp(name, "eth", 3) == 0) {
        char *end;
        int   num = simple_strtoul(name+3, &end, 10);

        if (strcmp(end, "addr") == 0) {
            eth_set_enetaddr(num, argv[2]);
        }
    }
#endif


    /* Delete only ? */
    if ((argc < 3) || argv[2] == NULL) {
        env_crc_update ();
        return 0;
    }

    /*
     * Append new definition at the end
     */
    for (env=env_data; *env || *(env+1); ++env)
        ;
    if (env > env_data)
        ++env;
    /*
     * Overflow when:
     * "name" + "=" + "val" +"\0\0"  > ENV_SIZE - (env-env_data)
     */
    len = strlen(name) + 2;
    /* add '=' for first arg, ' ' for all others */
    for (i=2; i<argc; ++i) {
        len += strlen(argv[i]) + 1;
    }
    if (len > (&env_data[ENV_SIZE]-env)) {
        printf ("## Error: environment overflow, \"%s\" deleted\n", name);
        return 1;
    }
    while ((*env = *name++) != '\0')
        env++;
    for (i=2; i<argc; ++i) {
        char *val = argv[i];

        *env = (i==2) ? '=' : ' ';
        while ((*++env = *val++) != '\0')
            ;
    }

    /* end is marked with double '\0' */
    *++env = '\0';

    /* Update CRC */
    env_crc_update ();

    /*
     * Some variables should be updated when the corresponding
     * entry in the enviornment is changed
     */

    if (strcmp(argv[1],"ethaddr") == 0) {
        char *s = argv[2];    /* always use only one arg */
        char *e;
        for (i=0; i<6; ++i) {
            bd->bi_enetaddr[i] = s ? simple_strtoul(s, &e, 16) : 0;
            if (s) s = (*e) ? e+1 : e;
        }
#ifdef CONFIG_NET_MULTI
        eth_set_enetaddr(0, argv[2]);
#endif
        return 0;
    }

    if (strcmp(argv[1],"ipaddr") == 0) {
        char *s = argv[2];    /* always use only one arg */
        char *e;
        unsigned long addr;
        bd->bi_ip_addr = 0;
        for (addr=0, i=0; i<4; ++i) {
            ulong val = s ? simple_strtoul(s, &e, 10) : 0;
            addr <<= 8;
            addr  |= (val & 0xFF);
            if (s) s = (*e) ? e+1 : e;
        }
        bd->bi_ip_addr = htonl(addr);
        return 0;
    }
    if (strcmp(argv[1],"loadaddr") == 0) {
        load_addr = simple_strtoul(argv[2], NULL, 16);
        return 0;
    }
#if (CONFIG_COMMANDS & CFG_CMD_NET)
    if (strcmp(argv[1],"bootfile") == 0) {
        copy_filename (BootFile, argv[2], sizeof(BootFile));
        return 0;
    }
#endif    /* CFG_CMD_NET */

#ifdef CONFIG_AMIGAONEG3SE
    if (strcmp(argv[1], "vga_fg_color") == 0 ||
        strcmp(argv[1], "vga_bg_color") == 0 ) {
        extern void video_set_color(unsigned char attr);
        extern unsigned char video_get_attr(void);

        video_set_color(video_get_attr());
        return 0;
    }
#endif    /* CONFIG_AMIGAONEG3SE */

    return 0;
}

6、环境变量的保存,保存是读取的反过程,所以跟上面的过程相似,如下:
common/env_nand.c
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;
}
common/env_flash.c
int saveenv(void)
{
    char *saved_data = NULL;
    int rc = 1;
    char flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG;
#if CFG_ENV_SECT_SIZE > CFG_ENV_SIZE
    ulong up_data = 0;
#endif

    debug ("Protect off %08lX ... %08lX\n",
        (ulong)flash_addr, end_addr);

    if (flash_sect_protect (0, (ulong)flash_addr, end_addr)) {
        goto Done;
    }

    debug ("Protect off %08lX ... %08lX\n",
        (ulong)flash_addr_new, end_addr_new);

    if (flash_sect_protect (0, (ulong)flash_addr_new, end_addr_new)) {
        goto Done;
    }

#if CFG_ENV_SECT_SIZE > CFG_ENV_SIZE
    up_data = (end_addr_new + 1 - ((long)flash_addr_new + CFG_ENV_SIZE));
    debug ("Data to save 0x%x\n", up_data);
    if (up_data) {
        if ((saved_data = malloc(up_data)) == NULL) {
            printf("Unable to save the rest of sector (%ld)\n",
                up_data);
            goto Done;
        }
        memcpy(saved_data,
            (void *)((long)flash_addr_new + CFG_ENV_SIZE), up_data);
        debug ("Data (start 0x%x, len 0x%x) saved at 0x%x\n",
               (long)flash_addr_new + CFG_ENV_SIZE,
                up_data, saved_data);
    }
#endif
    puts ("Erasing Flash...");
    debug (" %08lX ... %08lX ...",
        (ulong)flash_addr_new, end_addr_new);

    if (flash_sect_erase ((ulong)flash_addr_new, end_addr_new)) {
        goto Done;
    }

    puts ("Writing to Flash... ");
    debug (" %08lX ... %08lX ...",
        (ulong)&(flash_addr_new->data),
        sizeof(env_ptr->data)+(ulong)&(flash_addr_new->data));
    if ((rc = flash_write((char *)env_ptr->data,
            (ulong)&(flash_addr_new->data),
            sizeof(env_ptr->data))) ||
        (rc = flash_write((char *)&(env_ptr->crc),
            (ulong)&(flash_addr_new->crc),
            sizeof(env_ptr->crc))) ||
        (rc = flash_write(&flag,
            (ulong)&(flash_addr->flags),
            sizeof(flash_addr->flags))) ||
        (rc = flash_write(&new_flag,
            (ulong)&(flash_addr_new->flags),
            sizeof(flash_addr_new->flags))))
    {
        flash_perror (rc);
        goto Done;
    }
    puts ("done\n");

#if CFG_ENV_SECT_SIZE > CFG_ENV_SIZE
    if (up_data) { /* restore the rest of sector */
        debug ("Restoring the rest of data to 0x%x len 0x%x\n",
               (long)flash_addr_new + CFG_ENV_SIZE, up_data);
        if (flash_write(saved_data,
                (long)flash_addr_new + CFG_ENV_SIZE,
                up_data)) {
            flash_perror(rc);
            goto Done;
        }
    }
#endif
    {
        env_t * etmp = flash_addr;
        ulong ltmp = end_addr;

        flash_addr = flash_addr_new;
        flash_addr_new = etmp;

        end_addr = end_addr_new;
        end_addr_new = ltmp;
    }

    rc = 0;
Done:

    if (saved_data)
        free (saved_data);
    /* try to re-protect */
    (void) flash_sect_protect (1, (ulong)flash_addr, end_addr);
    (void) flash_sect_protect (1, (ulong)flash_addr_new, end_addr_new);

    return rc;
}

你可能感兴趣的:(U-BOOT环境变量的获取和保存的实现分析)