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环境变量实现