u-boot-1.1.6
在u-boot命令行模式的时候,输入printenv的时候出现很多很多环境变量,这是怎么实现的?使用setenv修改一个环境变量后在printenv显示修改成功,但是关机重启后显示没有改变,这是怎么回事?我看了几天源码和参考网上一个仁兄的文章,今天就在这里好好八一八它的老底!
一、环境变量概述
1.1 调用的次序
1.2 相关源码文件
common/env_common.c 供u-boot调用的通用函数接口,它们隐藏了env的不同实现方式:默认的环境变量定义,环境变量底层操作env_get_char_memory
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对环境变量的操作命令:getenv函数和printenv,setenv,saveenv命令
environment.c 环境变量以及一些宏定义
1.3 相关结构体
/include/environment.h中定义了表示env的数据结构
typedef struct environment_s
{
unsigned long crc;
#ifdef CFG_REDUNDAND_ENVIRONMENT
unsigned char flags;
#endif
unsigned char data[ENV_SIZE];
} env_t;
crc是u-boot在保存env 的时候加上去的校验头,在第一次启动时一般 crc校验会出错,这很正常,因为这时 Flash中的数据无效。data字段保存实际的环境变量。u-boot 的 env 按 name=value”\0”的方式存储,在所有env的最后以”\0\0”表示整个 env 的结束。新的name=value对总是被添加到 env 数据块的末尾,当删除一个name=value对时,后面的环境变量将前移,对一个已经存在的环境变量的修改实际上先删除再插入。
u-boot会将 env 从 nand flash等存储设备重定位到 RAM 中,在 env 的不同实现版本(如env_nand.c)中定义了 env_ptr, 它指向 env 在RAM中的位置。u-boot在重定位 env后对环境变量的操作都是针对 env_ptr。u-boot 把env_t 的数据指针有保存在了另外一个地方,这就是 gd_t 结构,这里以ARM为例仅列出和 env 相关的部分。
<include/asm-arm/Global_data.h>
typedef struct global_data
{
…
unsigned long env_off;
unsigned long env_addr;
unsigned long env_valid
…
} gd_t
gd_t->env_addr = env_ptr->data;
二、环境变量实现
2.1 默认环境变量定义
/common/env_common.c default_environment部分内容如下:
uchar default_environment[] =
#ifdef CONFIG_BOOTARGS
"bootargs="CONFIG_BOOTARGS"\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd="CONFIG_BOOTCOMMAND"\0"
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot="CONFIG_RAMBOOTCOMMAND"\0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND"\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
"bootdelay="MK_STR(CONFIG_BOOTDELAY)"\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"baudrate="MK_STR(CONFIG_BAUDRATE)"\0"
#endif
可以从上面得到,
default_environment的初始化就是根据include/configs/smdk2410.h中的宏决定的
2.2 env_init
/common.c/env_nand.c 当ENV_IS_EMBEDDED为假时env_init为:
env_init
gd->env_addr = (ulong)&default_environment[0];//gd中env_addr指向默认的环境变量
gd->env_valid = 1; //说明env_addr指向的数据有效
2.3 env_relocate
common/env_common.c
void env_relocate (void)
env_ptr = (env_t *)malloc (CFG_ENV_SIZE); //环境变量重定位:先给它给配空间大小
env_get_char = env_get_char_memory; //环境变量底层操作函数指针初始化
env_relocate_spec(); //环境变量重定位,env_nand.c中定义的env_ptr指针指向重定位后的环境变量
gd->env_addr = (ulong)&(env_ptr->data); //gd中的env_addr指向重定位后的环境变量
2.4 env_relocate_spec
/common/env_nand.c
void env_relocate_spec (void)
ulong total = CFG_ENV_SIZE;
int ret;
ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);//将nand中环境变量读到内存的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();
三、环境变量操作
3.1 env_get_xxx
/common/env_common.c
uchar env_get_char_memory (int index) //获得环境变量字符串中偏移值为index的那个字符
{
if (gd->env_valid)
return ( *((uchar *)(gd->env_addr + index)) );
else
return ( default_environment[index] );
}
uchar *env_get_addr (int index) //获得环境变量字符串中偏移值为index的那个字符的地址
{
if (gd->env_valid)
return ( ((uchar *)(gd->env_addr + index)) );
else
return (&default_environment[index]);
}
3.2 printenv打印环境变量
/common/cmd_nvedit.c
int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])//将内存中的环境变量打印出来
{
int i, j, k, nxt;
int rcode = 0;
if (argc == 1) { //不带任何参数则打印出所有的变量
for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) ; //先获得某一个环境变量长度
for (k=i; k<nxt; ++k)
putc(env_get_char(k)); //打印出这个环境变量
putc ('\n');//换行
if (ctrlc()) {
puts ("\n ** Abort\n");
return 1;
}
}//end for
printf("\nEnvironment size: %d/%d bytes\n", i, ENV_SIZE);
return 0;
}//end if
for (i=1; i<argc; ++i) {//循环打印命令后出现的单个环境变量
char *name = argv[i]; //获得某个环境变量名字
k = -1;
for (j=0; env_get_char(j) != '\0'; j=nxt+1) {
for (nxt=j; env_get_char(nxt) != '\0'; ++nxt) ;//获得该个环境变量的长度
k = envmatch((uchar *)name, j); //根据名字找到它在环境变量中的偏移量
if (k < 0) {continue;} //没找到就跳过剩下的部分继续循环
puts (name); //找到就打印名字
putc ('='); //打印等号
while (k < nxt)
putc(env_get_char(k++)); //打印内容
putc ('\n'); //换行
break; //结束循环
}//end for2
if (k < 0) {
printf ("## Error: \"%s\" not defined\n", name);
rcode ++; }
}//end for1
return rcode;}
3.3 saveenv保存环境变量
/common/cmd_nvedit.c
int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])//将环境变量保存在永久性储存设备中
{
extern char * env_name_spec;
printf ("Saving Environment to %s...\n", env_name_spec);
return (saveenv() ? 1 : 0);
}
//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))//擦除nand中所有的环境变量
return 1;
puts ("Writing to Nand... ");
total = CFG_ENV_SIZE;
ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);//将env_ptr指向的环境变量烧写到nand中
if (ret || total != CFG_ENV_SIZE)
return 1;
puts ("done\n");
return ret;
}
3.4 setenv(太复杂,略)
3.5 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);}
四、配置环境变量
要支持环境变量保存在nand中必须在/include/configs/smdk2410.h设置下某些宏开关,并且要设置些宏为默认环境变量来赋值。
#define CFG_ENV_IS_IN_NAND 1
#define CFG_ENV_OFFSET 0x40000
#define CFG_ENV_SIZE 0x20000 //支持环境变量保存在nand中需要设置的宏定义
/***************************************************************************************/
#define MTDIDS_DEFAULT "nand0=nandflash0" //常用的环境变量
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bios)," \
"128k(params)," \
"128k(toc)," \
"512k(eboot)," \
"1024k(logo)," \
"3m(kernel)," \
"-(root)"
#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \
CFG_CMD_CACHE | \
CFG_CMD_PING | \
CFG_CMD_JFFS2 | \
CFG_CMD_NAND | \
CFG_CMD_REGINFO | \
CFG_CMD_ELF | \
CFG_CMD_DATE)
#include <cmd_confdefs.h>
#define CONFIG_BOOTDELAY 10
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0"
#define CONFIG_ETHADDR 0a:1b:2c:3d:4e:5f
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.1.6
#define CONFIG_SERVERIP 192.168.1.8
#define CONFIG_BOOTCOMMAND "boot_zImage"
参考文献: U-BOOT环境变量实现