cortex-a8 uboot系列:第十四章 uboot环境变量
0赞一、 Uboot的环境变量基础
1. 环境变量的作用
让我们可以不用修改uboot的源代码,而是通过修改环境变量来影响uboot运行时的一些数据和特性。如修改bootdelay环境变量就可以更改系统开机自动启动时倒数的秒数。
2. 环境变量的优先级
Uboot代码中有一个值,环境变量中也有一个值。Uboot程序实际运行时规则是:如果环境变量为空则使用代码中的值;如果环境变量不为空则优先使用环境变量对应的值。
3. 环境变量在uboot中工作方式
默认环境变量,在common/env_common.c中default_environment中。default_environment是一个字符数组,大小为CFG_ENV_SIZE(16K),里面内容就是很多个环境变量连续分布组成的,每个环境变量最末端以’\0’分隔。
Gcc编译器会自动将多个字符串连接成一个字符串。也就是"bootargs="CONFIG_BOOTARGS"\0",gcc会将之变成” bootargs= console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3\0”
SD卡中环境变量分区,在uboot的raw分区中。SD卡中其实就是给了个分区,专门用来存储而已。存储时其实是把DDR中的环境变量整体的写入到SD卡中分区里。当saveenv时其实整个所有的环境变量都被保存了一遍,而不是只保存更改的。
DDR中环境变量,在default_environment中,实质是字符数组。在uboot中其实是一个全局变量,链接时在数据段,重定位时default_environment就被重定位到DDR中一个内存地址处。这个地址处这个全局字符数组就是uboot运行时的DDR中的环境变量了。
4. 总结:
刚烧录的系统环境变量是空白的,uboot第一次运行时加载的是uboot代码中自带的一份环境变量,叫默认环境变量。在saveenv时DDR中的环境变量会被更新到SD卡中的环境变量中,就可以被保存下来,下次开机会在环境变量relocate时会被SD卡中的环境变量会被加载到DDR中。
default_environment中的内容虽然被uboot源代码初始化为一定的值,但是在uboot启动的第二阶段,env_relocate时代码会去判断SD卡中的env分区的CRC是否通过。如果CRC通过说明SD卡中有正确的环境变量存储,则relocate函数会将SD卡中读取环境变量来覆盖default_environment字符数组,从而每次开机可以保持上一次更改过的环境变量。
二、 环境变量相关命令
1. printenv
Uboot中输入printenv后,其实是执行函数do_printenv,common/cmd_nvedit.c中。
先判断输入的参数,如果为1个,说明是打印所有的环境变量。就循环打印所有的环境变量出来。
用双重for循环,来依次处理所有环境变量的打印。第一层for循环,就是处理各个环境变量。所以有多少个环境变量就会循环多少次。第二层for循环,就处理单个环境变量的打印。
因此default_environment数组是很长的一个字符数组,中间以\0作为环境变量的间隔。
第一层循环,用来循环各个环境变量。第二个循环,用来处理每个环境变量。当for (nxt=i; env_get_char(nxt) != '\0'; ++nxt);执行完毕后,nxt就指向了当前处理环境变量在数组中\0的索引处。而环境变量的开始索引保存在i中。
然后在第三个循环,将i和nxt之间的数组内容打印出来,也就是环境变量的内容。打印完毕后,判断是否有按下ctrl+c,有的话,就退出。否则就处理下一个环境变量。
最后i的值就是环境变量的个数。
env_get_char函数(common/env_common.c),获取default_environment数组下标为index的值。
判断目前状态是否已经是重定位了,重定位后,就从内存保存的环境变量中读取。如果没有重定位,就从默认的环境变量保存处读取。
如果argc不为1,说明是打印特定环境变量的值,就打印参数的环境变量。
如: printenv bootdelay serverip gateway
那么 argc=3, argv[0] = bootdelay
argv[1] = serverip
argv[2] = gateway
第一层循环,处理输入要打印的各个环境变量,argv[i]就保存了第i个要打印环境变量的值。
首先通过通过两层循环,获取到各个环境变量在default_environment数组中的起始索引和结束索引。然后去判断得到的环境变量和需要打印的环境变量是否一致,是的话,就打印,否则就处理下一个要打印的命令。
2. setenv
设置环境变量的值。
对应的函数do_setenv(common/cmd_nvedit.c中)
函数判断输入参数,输入参数必须要大于2个。大于的话,就调用_do_setenv函数(common/cmd_nvedit.c中)。
通过env_get_addr(0)函数(common/env_common.c),获取到环境变量数组中第一个环境变量的地址。
如果返回地址为0,说明环境变量还没有拷贝到内存中,直接返回1。
argv[1]为输入setenv的第一个参数,也就是要设置的环境变量名,保存在name中。如果在参数中有=号,提示错误信息,并返回。
strchr函数,从字符串s中,寻找字符c,找到,返回字符c的地址,没有找到,返回NULL。
env_data是环境变量数组的首地址,env在每个循环后指向下一个环境变量的起始地址。env - env_data的值是上一个环境变量在数组中所占的空间大小。
Envmatch函数(common/cmd_nvedit.c)在环境变量数组中从索引i2开始查找在=号前的内容是不是和字符串s1匹配,匹配的话,返回i2的值,否则返回-1。
如果找到,需要将原来定义的环境变量给删除掉,也就是将原来环境变量的内容都给清成\0。这里先判断输入的环境变量是不是serial#和ethaddr这两个变量,是的话,提示错误信息,然后返回1。
判断是不是修改stdin、stdout、stderr这三个环境变量。
setenv思路:
先去DDR中的环境变量处找有没有这个环境变量,如果有则需要覆盖原来的环境变量,如果没有则在最后新增一个环境变量即可。
第一步:遍历DDR中环境变量的数组,找到环境变量对应的地址
第二步:擦除原来的环境变量
第三步:写入新的环境变量
还需要考虑其他一些问题:
问题1:环境变量太多超过DDR中的字符数组,溢出的解决方法
问题2:有些环境变量如baudrate、ipaddr等,在gd中有对应的全局变量。这种环境变量在set更新的时候要同时去更新对应的全局变量,否则就会出现在本次运行中环境变量和全局变量不一致的情况。
3. saveenv
定义在common/cmd_nvedit.c中。
使用的方法是do_saveenv
env_name_spec在common/env_auto.c中定义
最后调用saveenv函数(common/env_auto.c),判断启动方式,然后在不同的启动介质中保存环境变量。
INF_REG寄存器是s5pv210提供给用户的寄存器。这里是用的REG3。
在之前的启动中,uboot将启动介质信息保存到了INFORM3寄存器中,这里读取这个寄存器,就知道了板子从什么启动介质启动。因此就可以确定将环境变量保存到什么地方去。
对于九鼎x210开发板,使用的是SD卡/iNand卡启动,属于movinand,因此执行的是saveenv_movinand函数(common/env_auto.c中)。
调用movi_write_env函数(cpu/s5pc11x/movi.c中),调用movi_write函数将环境变量写入到外部NAND中。
raw_area_control是uboot中规划iNand/SD卡的原始分区表,这个里面记录了对iNand的分区,env分区也在这里,下标是2。
movi_write函数(drivers/mmc/mmc.c中),三个参数,第一个参数是外部存储设备的起始扇区,第二个参数是要写的扇区块数,第三个是保存写的数据的内存首地址。
最终调用mmc_bwrite函数(drivers/mmc/mmc.c中)执行写数据操作。4个参数,第一个参数是设备号,第二个是写的起始扇区,第三个参数是写的扇区块数,第四个参数是写的数据的内存首地址。
对于九鼎x210开发板,默认使用外部的iNand启动,所以第一个参数为0,表示写入到外部的iNand中。
4. getenv
获取环境变量的内容。
依然使用两个for循环,来依次去查找环境变量数组中的各个环境变量,然后进行匹配,匹配成功的话,返回环境变量的首地址。
5. gentenv_r
获取环境变量的内容
使用两个for循环,去遍历环境变量数组中的各个环境变量,匹配的话,就将环境变量写入到buf中。
两个函数的区别:
getenv函数是直接返回这个环境变量在DDR中环境变量处的地址,而getenv_r函数的做法是找到了DDR中环境变量地址后,将这个环境变量复制一份到提供的buf中,而不动原来DDR中环境变量。
所以getenv中返回的地址只能读不能随便写,而getenv_r中返回的环境变量是在自己提供的buf中,可以随意改写。
总结:
功能是一样的,但是getenv_r会比较安全一些,建议使用。
有关于环境变量的所有操作,主要理解了环境变量在DDR中的存储方法,理解了环境变量和gd全局变量的关联和优先级,理解了环境变量在存储介质中的存储方式(专用raw分区),整个环境变量相关的都比较容易理解了。