UBOOT——环境变量的实现原理
uboot 环境变量实现原理:
⾸先我们先要搞清楚uboot中环境变量的作⽤,uboot中环境变量的作⽤就是在不改变源码、不⽤重新编译的情况下,可以使我们通过
设置环境变量的值来改变uboot的⼀些设置,如bootdelay时间、机器码的值等等。
下⾯我们来具体看⼀下uboot中环境变量如何实现
⾸先看⼀下环境变量的初始化函数:
  env_init定义在commen/env_movi.c中函数中实际执⾏的就是把default_environment的地址赋值给全局变量gd中的env_addr 和
env_valid两个值;
int env_init(void)
{
#if defined(ENV_IS_EMBEDDED)
#else /* ENV_IS_EMBEDDED */
gd->env_addr  = (ulong)&default_environment[0];
gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED */
return (0);
}
来看⼀下default_environment数组
在来看⼀下env_relocate  这个函数
重点是⼀下两句代码
      env_ptr = (env_t *)malloc (CFG_ENV_SIZE); 这句代码作⽤是给uboot环境变量开辟⼀块16k⼤⼩的内存
      env_relocate_spec ();  这句代码的作⽤是把sd卡中的uboot环境变量整个分区复制到开辟的这个内存地址处;     void env_relocate (void)
{
#ifdef ENV_IS_EMBEDDED
#else
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
if (gd->env_valid == 0) {
}
else {
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
}
通过movi_read_env函数把sd卡中的环境变量复制到内存中
主药⽤到的是  movi_read_env(virt_to_phys((ulong)env_ptr)); 函数   
        crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc 
        use_default()
三个函数: movi_read_env把sd卡中的uboot的环境变量分区复制到env_ptr中;
     crc32 计算内存中环境变量的crc的值,如果不相等则执⾏
     use_default函数 
void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
uint *magic = (uint*)(PHYS_SDRAM_1);
if ((0x24564236 != magic[0]) || (0x20764316 != magic[1]))
movi_read_env(virt_to_phys((ulong)env_ptr));
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}
下⾯看⼀下use_default函数的代码:
1:输出*** Warning - bad CRC or moviNAND, using default environment:
2:清0环境变量内存,把default_environment中的值复制到环境变量内存
3:计算crc,写⼊内存中的crc位
4:设置gd中的env_valid为1;
static void use_default()
{
puts ("*** Warning - bad CRC or moviNAND, 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;
}
这样环境变量初始化算是完成了;
我们在回顾⼀下环境变量初始化都做了哪些⼯作:
1:在uboot还没有初始化flash设备(nand movinand 等flash)的时候,先进⾏环境变量初级初始化,即把gd全局变量中的 env_valid = 1;env_addr 等于全局变量default_enviroment数组的⾸地址
2:初始化完flash设置以后就要进⾏环境变量的重定位⼯作了,即把环境变量从flash中copy到内存中,⽤⼀个全局变量env_ptr指向这段内存;复制⼯作时通过movi_read_env这个函数实现的,实际就是把sd卡中
环境变量分区全部复制到env_ptr指向的这段内存中,然后在对这段内存中的环境变量进⾏crc校验,如果失败的话,则把default_enviroment 中的环境变量复制到这⾥;
(这⾥对代码分析env_relocate函数中⽤malloc设置了⼀段内存来存放环境变量,⼀直没有⽤free来释放这段内存,这⾥是否存在安全隐患?)
------------------------------------------------------------------------------------------------------------
接下来我们在看⼀下uboot中关于环境变量的⼀些命令
第⼀个 printenv 实现函数为do_printfenv
代码如下:
1int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
2 {
3int i, j, k, nxt;
4int rcode = 0;
5
6if (argc == 1) {        /* Print all env variables    */
7for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
8for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)
9                ;
10for (k=i; k<nxt; ++k)
11                putc(env_get_char(k));
12            putc  ('\n');
13
14if (ctrlc()) {
15                puts ("\n ** Abort\n");
16return1;
17            }
18        }
19
20        printf("\nEnvironment size: %d/%ld bytes\n",
21            i, (ulong)ENV_SIZE);
22
23return0;
24    }
25
26for (i=1; i<argc; ++i) {    /* print single env variables    */
27char *name = argv[i];
28
29        k = -1;
30
31for (j=0; env_get_char(j) != '\0'; j=nxt+1) {
32
33for (nxt=j; env_get_char(nxt) != '\0'; ++nxt)
34                ;
35            k = envmatch((uchar *)name, j);
36if (k < 0) {
37continue;
38            }
39            puts (name);
40            putc ('=');
41while (k < nxt)
42                putc(env_get_char(k++));
43            putc ('\n');
44break;
45        }
46if (k < 0) {
47            printf ("## Error: \"%s\" not defined\n", name);
48            rcode ++;
49        }
50    }
51return rcode;
52 }
uchar env_get_char_memory (int index)
{
if (gd->env_valid) {
return ( *((uchar *)(gd->env_addr + index)) );
} else {
return ( default_environment[index] );
}
}
第⼆个命令:setenv命令对⽤的函数是do_setenv 命令,实际上调⽤的是_do_setenv函数下⾯我们来分析⼀下这个函数
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 */
return1;
name = argv[1];
if (strchr(name, '=')) {
printf ("## Error: illegal character '=' in variable name \"%s\"\n", name);
return1;
}
/*
* 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) {
/* 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);
return1;
}
/* Try assigning specified device */
if (console_assign (console, argv[2]) < 0)
return1;
#ifdef CONFIG_SERIAL_MULTI
if (serial_assign (argv[2]) < 0)
return1;
#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);
return1;
}
printf ("## Switch baudrate to %d bps and press ENTER ...\n",                baudrate);
udelay(50000);
printf函数是如何实现的gd->baudrate = baudrate;
serial_setbrg ();
udelay(50000);
for (;;) {
if (getc() == '\r')
break;
}
}
if (*++nxt == '\0') {
if (env > env_data) {
env--;
} else {
*env = '\0';