weiqi7777

cortex-a8 uboot系列:第十四章 uboot环境变量

0
阅读(3079)

一、            Uboot的环境变量基础

1.      环境变量的作用

让我们可以不用修改uboot的源代码,而是通过修改环境变量来影响uboot运行时的一些数据和特性。如修改bootdelay环境变量就可以更改系统开机自动启动时倒数的秒数。

2.      环境变量的优先级

Uboot代码中有一个值,环境变量中也有一个值。Uboot程序实际运行时规则是:如果环境变量为空则使用代码中的值;如果环境变量不为空则优先使用环境变量对应的值。

3.      环境变量在uboot中工作方式

默认环境变量,在common/env_common.cdefault_environment中。default_environment是一个字符数组,大小为CFG_ENV_SIZE16K),里面内容就是很多个环境变量连续分布组成的,每个环境变量最末端以’\0’分隔。

Gcc编译器会自动将多个字符串连接成一个字符串。也就是"bootargs="CONFIG_BOOTARGS"\0"gcc会将之变成” bootargs= console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3\0”

clip_image002

 

SD卡中环境变量分区,在ubootraw分区中。SD卡中其实就是给了个分区,专门用来存储而已。存储时其实是把DDR中的环境变量整体的写入到SD卡中分区里。当saveenv时其实整个所有的环境变量都被保存了一遍,而不是只保存更改的。

 

DDR中环境变量,在default_environment中,实质是字符数组。在uboot中其实是一个全局变量,链接时在数据段,重定位时default_environment就被重定位到DDR中一个内存地址处。这个地址处这个全局字符数组就是uboot运行时的DDR中的环境变量了。

 

4.      总结:

刚烧录的系统环境变量是空白的,uboot第一次运行时加载的是uboot代码中自带的一份环境变量,叫默认环境变量。在saveenvDDR中的环境变量会被更新到SD卡中的环境变量中,就可以被保存下来,下次开机会在环境变量relocate时会被SD卡中的环境变量会被加载到DDR中。

default_environment中的内容虽然被uboot源代码初始化为一定的值,但是在uboot启动的第二阶段,env_relocate时代码会去判断SD卡中的env分区的CRC是否通过。如果CRC通过说明SD卡中有正确的环境变量存储,则relocate函数会将SD卡中读取环境变量来覆盖default_environment字符数组,从而每次开机可以保持上一次更改过的环境变量。

二、            环境变量相关命令

1.      printenv

clip_image004

Uboot中输入printenv后,其实是执行函数do_printenvcommon/cmd_nvedit.c中。

 

clip_image006

         先判断输入的参数,如果为1个,说明是打印所有的环境变量。就循环打印所有的环境变量出来。

         用双重for循环,来依次处理所有环境变量的打印。第一层for循环,就是处理各个环境变量。所以有多少个环境变量就会循环多少次。第二层for循环,就处理单个环境变量的打印。

         clip_image008

         因此default_environment数组是很长的一个字符数组,中间以\0作为环境变量的间隔。

 

         第一层循环,用来循环各个环境变量。第二个循环,用来处理每个环境变量。当for (nxt=i; env_get_char(nxt) != '\0'; ++nxt);执行完毕后,nxt就指向了当前处理环境变量在数组中\0的索引处。而环境变量的开始索引保存在i中。

         然后在第三个循环,将inxt之间的数组内容打印出来,也就是环境变量的内容。打印完毕后,判断是否有按下ctrl+c,有的话,就退出。否则就处理下一个环境变量。

         最后i的值就是环境变量的个数。

 

clip_image010

         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

clip_image012

         第一层循环,处理输入要打印的各个环境变量,argv[i]就保存了第i个要打印环境变量的值。

         首先通过通过两层循环,获取到各个环境变量在default_environment数组中的起始索引和结束索引。然后去判断得到的环境变量和需要打印的环境变量是否一致,是的话,就打印,否则就处理下一个要打印的命令。

2.      setenv

设置环境变量的值。

clip_image014

         对应的函数do_setenvcommon/cmd_nvedit.c中)

 

clip_image016

         函数判断输入参数,输入参数必须要大于2个。大于的话,就调用_do_setenv函数(common/cmd_nvedit.c)

         clip_image018

通过env_get_addr0)函数(common/env_common.c),获取到环境变量数组中第一个环境变量的地址。

clip_image020

         clip_image022

         如果返回地址为0,说明环境变量还没有拷贝到内存中,直接返回1

 

clip_image024

         argv[1]为输入setenv的第一个参数,也就是要设置的环境变量名,保存在name中。如果在参数中有=号,提示错误信息,并返回。

strchr函数,从字符串s中,寻找字符c,找到,返回字符c的地址,没有找到,返回NULL

         clip_image026

 

clip_image028

         env_data是环境变量数组的首地址,env在每个循环后指向下一个环境变量的起始地址。env - env_data的值是上一个环境变量在数组中所占的空间大小。

        

clip_image030

         Envmatch函数(common/cmd_nvedit.c)在环境变量数组中从索引i2开始查找在=号前的内容是不是和字符串s1匹配,匹配的话,返回i2的值,否则返回-1

 

clip_image032

         如果找到,需要将原来定义的环境变量给删除掉,也就是将原来环境变量的内容都给清成\0。这里先判断输入的环境变量是不是serial#ethaddr这两个变量,是的话,提示错误信息,然后返回1

clip_image034

         判断是不是修改stdinstdoutstderr这三个环境变量。

 

         setenv思路:

         先去DDR中的环境变量处找有没有这个环境变量,如果有则需要覆盖原来的环境变量,如果没有则在最后新增一个环境变量即可。

         第一步:遍历DDR中环境变量的数组,找到环境变量对应的地址

         第二步:擦除原来的环境变量

         第三步:写入新的环境变量

 

         还需要考虑其他一些问题:

         问题1:环境变量太多超过DDR中的字符数组,溢出的解决方法

         问题2:有些环境变量如baudrateipaddr等,在gd中有对应的全局变量。这种环境变量在set更新的时候要同时去更新对应的全局变量,否则就会出现在本次运行中环境变量和全局变量不一致的情况。

3.      saveenv

定义在common/cmd_nvedit.c中。

clip_image036

使用的方法是do_saveenv

 

clip_image038

 

env_name_speccommon/env_auto.c中定义

clip_image040

 

最后调用saveenv函数(common/env_auto.c),判断启动方式,然后在不同的启动介质中保存环境变量。

clip_image042

 

clip_image044

INF_REG寄存器是s5pv210提供给用户的寄存器。这里是用的REG3

clip_image046

在之前的启动中,uboot将启动介质信息保存到了INFORM3寄存器中,这里读取这个寄存器,就知道了板子从什么启动介质启动。因此就可以确定将环境变量保存到什么地方去。

对于九鼎x210开发板,使用的是SD/iNand卡启动,属于movinand,因此执行的是saveenv_movinand函数(common/env_auto.c中)。

clip_image048

调用movi_write_env函数(cpu/s5pc11x/movi.c中),调用movi_write函数将环境变量写入到外部NAND中。

clip_image050

raw_area_controluboot中规划iNand/SD卡的原始分区表,这个里面记录了对iNand的分区,env分区也在这里,下标是2

movi_write函数(drivers/mmc/mmc.c中),三个参数,第一个参数是外部存储设备的起始扇区,第二个参数是要写的扇区块数,第三个是保存写的数据的内存首地址。

clip_image052

 

最终调用mmc_bwrite函数(drivers/mmc/mmc.c中)执行写数据操作。4个参数,第一个参数是设备号,第二个是写的起始扇区,第三个参数是写的扇区块数,第四个参数是写的数据的内存首地址。

         对于九鼎x210开发板,默认使用外部的iNand启动,所以第一个参数为0,表示写入到外部的iNand中。

clip_image054

4.      getenv

获取环境变量的内容。

clip_image056

依然使用两个for循环,来依次去查找环境变量数组中的各个环境变量,然后进行匹配,匹配成功的话,返回环境变量的首地址。

5.      gentenv_r

获取环境变量的内容

clip_image058

使用两个for循环,去遍历环境变量数组中的各个环境变量,匹配的话,就将环境变量写入到buf中。

 

 

两个函数的区别:

getenv函数是直接返回这个环境变量在DDR中环境变量处的地址,而getenv_r函数的做法是找到了DDR中环境变量地址后,将这个环境变量复制一份到提供的buf中,而不动原来DDR中环境变量。

所以getenv中返回的地址只能读不能随便写,而getenv_r中返回的环境变量是在自己提供的buf中,可以随意改写。

 

总结:

功能是一样的,但是getenv_r会比较安全一些,建议使用。

有关于环境变量的所有操作,主要理解了环境变量在DDR中的存储方法,理解了环境变量和gd全局变量的关联和优先级,理解了环境变量在存储介质中的存储方式(专用raw分区),整个环境变量相关的都比较容易理解了。