以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。
参考资料:http://www.cnblogs.com/biaohc/p/6398515.html。
一、uboot的环境变量基础
1、环境变量的作用
- 在不改变源码、不用重新编译的情况下,可以通过设置环境变量的值来改变uboot的一些设置,如bootdelay时间、机器码的值等等。
2、环境变量的优先级
(1)uboot代码当中有一个值,SD卡中环境变量分区也有一个值。
- uboot程序实际运行时规则是:如果环境变量分区为空则使用uboot代码中的值;如果环境变量分区不为空则优先使用环境变量分区对应的值。
(2)譬如machid(机器码)。
- uboot中在x210_sd.h中定义了一个机器码2456,写死在程序中的不能更改(如果要修改,则要重新编译烧录);
- SD卡的环境变量分区中也有machid;
- 由于SD卡的环境变量分区中也有machid,系统启动时会优先使用环境变量的machid,这就是优先级问题。
3、环境变量在uboot中工作方式
(1)默认环境变量
- 在uboot/common/env_common.c的default_environment中;
- 本质是一个字符数组,大小为CFG_ENV_SIZE(16kb);
- 很多个环境变量连续分布组成的,每个环境变量最末端以'\0'结束。
(2)SD卡中环境变量分区
- 在uboot的raw分区中,SD卡中有专门的分区用来存储;
- 存储时,把DDR中的环境变量整体写入SD卡的环境变量分区。
- 因此saveenv时,是所有的环境变量都被保存了一遍,而不是只保存更改了的。
(3)DDR中环境变量
- 在default_environment中,实质是字符数组。
- 在uboot中其实是一个全局变量,链接时在数据段,重定位时default_environment就被重定位到DDR中一个内存地址处了。
- 这个地址处的这个全局字符数组就是uboot运行时的DDR中的环境变量。
(4)总结
- SD卡的环境变量分区一开始是空白的,uboot第一次运行时加载的是uboot代码中的那份环境变量,叫默认环境变量。
- 在saveenv时,DDR中的环境变量会被更新到SD卡的环境变量分区中。下次开机在环境变量relocate时,会将SD卡中的环境变量加载到DDR中去。
- default_environment中的内容虽然被uboot源代码初始化为一定的值,但是在env_relocate时,代码会把环境变量从SD卡中拷贝到内存中,然后去判断SD卡中的env分区的crc是否通过,如果不通过则使用default_environment字符数组,通过了使用SD卡内的环境变量。
4、环境变量的初始化
(1)没有初始化flash设备前,进行环境变量初级初始化
- 函数env_init定义在commen/env_movi.c中;
- 把gd全局变量中的 env_valid = 1; env_addr = 全局变量default_enviroment数组的首地址。
(2)初始化flash设备之后
- 初始化flash后,进行环境变量的重定位工作,即把环境变量从flash中copy到内存中,用一个全局变量env_ptr指向这段内存;
- 复制是通过movi_read_env这个函数实现的,实际就是把sd卡中环境变量分区全部复制到env_ptr指向的这段内存中;
- 然后在对这段内存中的环境变量进行crc校验,如果失败的话,则把default_enviroment中的环境变量复制到这里。
- env_ptr = (env_t *)malloc (CFG_ENV_SIZE); 这句代码作用是给uboot环境变量开辟一块16k大小的内存;
- env_relocate_spec ();这句代码的作用是把sd卡中的uboot环境变量整个分区复制到开辟的这个内存地址处;
- 见博客http://blog.csdn.net/oqqhutu12345678/article/details/69365347的(四2)。
代码跟踪:env_relocate_spec ()函数:
- 输出*** Warning - bad CRC or moviNAND, using default environment:;
- 清0环境变量内存,把default_environment中的值复制到环境变量内存;
- 计算crc,写入内存中的crc位;
- 设置gd中的env_valid为1;
二、环境变量相关命令源码解析(举例)
1、printenv,实现函数为do_printfenv
- do_printenv函数首先区分是否argc=1,若argc=1则循环打印所有的环境变量出来,否则后面的参数就是要打印的环境变量,给哪个就打印哪个。
- argc=1时,用双重for循环来依次处理所有的环境变量的打印。第一重for循环就是处理各个环境变量,所以有多少个环境变量则第一重就执行循环多少圈。
- 要明白整个环境变量在内存中如何存储的问题。
2、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 */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) {/* 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_MULTIif (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;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_MULTIif (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_MULTIeth_set_enetaddr(0, argv[2]);
#endifreturn 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 defined(CONFIG_CMD_NET)if (strcmp(argv[1],"bootfile") == 0) {copy_filename (BootFile, argv[2], sizeof(BootFile));return 0;}
#endif#ifdef CONFIG_AMIGAONEG3SEif (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;
}