老规矩有道云地址:http://note.youdao.com/noteshare?id=3645af9d084b3aad1d13858fe5614528&sub=FC582BCEB2994BF09BB3291BF0DE40FA
u-boot的缺省情况下会有一些基本的环境变量,当执行saveenv时,环境变量会保存到flash存储设备中。
如果环境变量的值为空,则uboot会使用uboot代码中的值;如果环境变量不为空,则优先使用环境变量的值。
默认环境变量在uboot源码中common/Env_common.c文件中。
所有的环境变量存储在一个16KB大小的一维数组中,每个环境变量以”\0”结束。
uboot在启动时,在遍历调用执行init_sequence函数指针数组中的env_init函数时已经对环境变量进行校验,通过调用env_relocate函数,将环境变量从Flash启动设备重定位到SDRAM中。uboot启动时环境变量的初始化如下:
(具体参考八.linux开发之uboot移植(八)——uboot源码分析2-启动第二阶段之start_armboot函数分析)
环境变量有2份,一份在Flash中,另一份在DDR中。uboot开机时一次性从Flash中读取全部环境变量到DDR中作为环境变量的初始化值,然后使用过程中都是用DDR中这一份
bootdelay | 执行自动启动(bootcmd中的命令)的等候秒数 |
---|---|
baudrate | 串口控制台的波特率 |
netmask | 以太网的网络掩码 |
ethaddr | 以太网的MAC地址 |
bootfile | 默认的下载文件名 |
bootargs | 传递给Linux内核的启动参数 |
bootcmd | 自动启动时执行命令 |
serverip | TFTP服务器端的IP地址 |
ipaddr | 本地的IP地址 |
stdin | 标准输入设备,一般是串口 |
stdout | 标准输出,一般是串口,也可是LCD(VGA) |
stderr | 标准出错,一般是串口,也可是LCD(VGA) |
1、printenv环境变量对应函数(打印出环境变量的功能)
(1)找到printenv命令所对应的函数。通过printenv的help可以看出,这个命令有2种使用方法。
第一种直接使用不加参数则打印所有的环境变量;第二种是printenv name则只打印出name这个环境变量的值。
(2)分析do_printenv函数。
(3)argc=1时用双重for循环来依次处理所有的环境变量的打印。第一重for循环就是处理各个环境变量。所以有多少个环境变量则第一重就执行循环多少圈。
int do_printenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
int i, j, k, nxt;
int rcode = 0;
if (argc == 1) {//printenv命令没有参数时打印出所有变量信息
for (i=0; env_get_char(i) != '\0'; i=nxt+1) {//遍历环境变量数组中的变量
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)//遍历变量获取字符数量
;
for (k=i; k'\n');//变量信息输出完毕打印换行
if (ctrlc()) { //打印中使用ctrl+c中止过程,则打印如下信息
puts ("\n ** Abort\n");
return 1;
}
}
printf("\nEnvironment size: %d/%ld bytes\n",
i, (ulong)ENV_SIZE);
return 0;
}
//argc不等于1,则后面的参数就是要打印的环境变量,遍历打印单个或多个变量的信息,即print xx xx 则只打印出xx xx这个环境变量的值。
for (i=1; ichar *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;
}
if (k < 0) {
printf ("## Error: \"%s\" not defined\n", name);
rcode ++;
}
}
return rcode;
}
(4)总结:do_printenv函数首先区分argc=1还是不等于1的情况,若argc=1那么就循环打印所有的环境变量出来;如果argc不等于1,则后面的参数就是要打印的环境变量,给哪个就打印哪个。
1、setenv环境变量对应函数(改变或增加环境变量的功能)
(1)命令定义和对应的函数在uboot/common/cmd_nvedit.c中,对应的函数为do_setenv。分析里面**,最重要的是int
_do_setenv函数**
(2)setenv的思路就是:先去DDR中的环境变量处寻找原来有没有这个环境变量,如果原来就有则需要覆盖原来的环境变量,如果原来没有则在最后新增一个环境变量即可。
第1步:遍历DDR中环境变量的数组,找到原来就有的那个环境变量对应的地址。168-174行。
第2步:擦除原来的环境变量,259-265行
第3步:写入新的环境变量,266-273行。
(3)本来setenv做完上面的就完了,但是还要考虑一些附加的问题。
问题一:环境变量太多超出DDR中的字符数组,溢出的解决方法。
问题二:有些环境变量如baudrate、ipaddr等,在gd中有对应的全局变量。这种环境变量在set更新的时候要同时去更新对应的全局变量,否则就会出现在本次运行中环境变量和全局变量不一致的情况。
2、saveenv环境变量(保存更改命令后的功能)
(1)在uboot/common/cmd_nvedit.c中,对应函数为do_saveenv函数
(2)从uboot实际执行saveenv命令的输出,和x210_sd.h中的配置(#define
CFG_ENV_IS_IN_AUTO)可以分析出:我们实际使用的是env_auto.c中相关的内容。没有一种芯片叫auto的,env_auto.c中是使用宏定义的方式去条件编译了各种常见的flash芯片(如movinand、norflash、nand等)。然后在程序中读取INF_REG(OMpin内部对应的寄存器)从而知道我们的启动介质,然后调用这种启动介质对应的操作函数来操作。
(3)do_saveenv内部调用env_auto.c中的saveenv函数来执行实际的环境变量保存操作。
(4)寄存器地址:E010F000+0C=E010_F00C,含义是用户自定义数据。我们在start.S中判断启动介质后将#BOOT_MMCSD(就是3,定义在x210_sd.h)写入了这个寄存器,所以这里读出的肯定是3,经过判断就是movinand。所以实际执行的函数是:saveenv_movinand
**\#define INF_REG3_REG \__REG(INF_REG_BASE+INF_REG3_OFFSET)**
**\#define INF_REG_BASE0xE010F**
**\#define INF_REG3_OFFSET0x0c**
(5)真正执行保存环境变量操作的是:cpu/s5pc11x/movi.c中的movi_write_env函数,这个函数肯定是写sd卡,将DDR中的环境变量数组(其实就是default_environment这个数组,大小16kb,刚好32个扇区)写入iNand中的ENV分区中。
(6)raw_area_control是uboot中规划iNnad/SD卡的原始分区表,这个里面记录了我们对iNand的分区,env分区也在这里,下标是2.追到这一层就够了,再里面就是调用驱动部分的写SD卡/iNand的底层函数了。
1、getenv环境变量对应函数(功 能: 从环境中取字符串,获取环境变量的值)
(1)应该是不可重入的。
(2)实现方式就是去遍历default_environment数组,挨个拿出所有的环境变量比对name,找到相等的直接返回这个环境变量的首地址即可。
(3)getenv()用来取得参数name环境变量的内容。参数name为环境变量的名称,如果该变量存在则会返回指向该内容的指针。环境变量的格式为name=value。getenv函数的返回值存储在一个全局二维数组里,当你再次使用getenv函数时不用担心会覆盖上次的调用结果。
一旦查找到匹配的字符串*name,则返回*name的地址。
2、getenv_r
(1)可重入版本。(可自行搜索补充可重入函数的概念)
(2)getenv函数是直接返回这个找到的环境变量在DDR中环境变量处的地址,而getenv_r函数的做法是找到了DDR中环境变量地址后,将这个环境变量复制一份到提供的buf中,而不动原来DDR中环境变量。
所以差别就是:getenv中返回的地址只能读不能随便乱写,而getenv_r中返回的环境变量是在自己提供的buf中,是可以随便改写加工的。
3、字符串的解析
(转载:http://blog.51cto.com/9291927/1792952)
uboot中对环境变量的操作充分利用了对环境变量字符串的解析。本部分将字符串的解析提取出来进行分析。
环境变量存储在default_environment[CFG_ENV_SIZE]数组中,每个环境变量以”\0”结束。通过对环境变量数default_environment[CFG_ENV_SIZE]进行解析,将环境变量分别打印出来。
实验源码:
#include
unsigned char environment[] =
{
"bootdelay=" "10" "\0"
"ipaddr=" "192.168.1.210" "\0"
"serverip=" "192.168.1.200" "\0"
"bootcmd=" "tftp 0x20008000 uImage;bootm 0x20008000" "\0"
"baudrate=" "115200" "\0"
};
int main(int argc, char **argv)
{
int i, s,k;
for(i = 0; *(environment+i) != '\0'; i = s + 1) //按照变量遍历环境变量数组
{ //
for(s = i; *(environment + s) != '\0'; s++)//计算出变量的字符数量
;
for(k = i; k < s; k++)//打印出变量的信息
putc( *(environment + k), stdout);
putc('\n', stdout);
}
return 0;
}
编译运行结果: