u-boot的环境变量是使用u-boot的关键,它可以由你自己定义的,但是其中有一些也是大家经常使用,约定熟成的,有一些是u-boot自己定义的,更改这些名字会出现错误,下面的表中我们列出了一些常用的环境变量:
???? bootdelay??? 执行自动启动的等候秒数
???? baudrate???? 串口控制台的波特率
???? netmask???? 以太网接口的掩码
???? ethaddr?????? 以太网卡的网卡物理地址
???? bootfile??????? 缺省的下载文件
???? bootargs???? 传递给内核的启动参数
???? bootcmd???? 自动启动时执行的命令
???? serverip?????? 服务器端的ip地址
???? ipaddr???????? 本地ip 地址
???? stdin?????????? 标准输入设备
???? stdout??????? 标准输出设备
???? stderr???????? 标准出错设备
????? 上面只是一些最基本的环境变量,请注意,板子里原本是没有环境变量的,u-boot的缺省情况下会有一些基本的环境变量,在你执行了saveenv之后,环境变量会第一次保存到flash或者eeprom中,之后你对环境变量的修改,保存都是基于保存在flash中的环境变量的操作。
??? 环境变量可以通过printenv命令查看环境变量的设置描述,通过setenv 命令进行重新设置,设置完成后可以通过saveenv将新的设置保存在非易失的存储设备中(nor flash 、nand flash 、eeprom)。例如:
??? setenv? bootcmd "nand read 0x30008000 0x80000 0x500000;bootm 0x30008000"
??? saveenv
??? 通过这两条命令就完成了环境变量bootcmd的重新设置,并讲其保存在固态存储器中。
??? 下面简单分析下uboot中环境变量的实现流程。???
????? uboot启动后,执行玩start.S中的汇编程序,将跳入board.c 中定义的start_arm_boot()函数中,在该函数中,uboot讲完成板子外设和相关系统环境的初始化,然后进入main_loop循环中进行系统启动或者等待与用户交互,这其中就包括环境变量的初始化和重定位。主要代码如下:
???? void start_armboot (void)
??? {
?????? init_fnc_t **init_fnc_ptr;
?????? .....................................
?????
?????? /* Pointer is writable since we allocated a register for it */
????? gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
????? /* compiler optimization barrier needed for GCC >= 3.4 */
????? __asm__ __volatile__("": : :"memory");
????? memset ((void*)gd,sizeof (gd_t));
????? gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
????? memset (gd->bd,sizeof (bd_t));
????? gd->flags |= GD_FLG_RELOC;
????? monitor_flash_len = _bss_start - _armboot_start;
(1):
????? for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
???? {
????????? if ((*init_fnc_ptr)() != 0)
??????? {
?????????? hang ();
?????? }
??? }
??? /* armboot_start is defined in the board-specific linker script */
??? mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
??? CONFIG_SYS_MALLOC_LEN);
(2)
?? /* initialize environment */
?? env_relocate ();
?? for (;;) {
????? main_loop ();
?? }
?/* NOTREACHED - no way out of command loop except booting */
}
????? 在代码段(1)中,会通过for循环调用init_sequence中的系统初始化函数,其中一个就是环境变量的初始化函数env_init(),uboot在编译的时候,会根据配置文件比如(mini2440.h)中的定义环境变量存储设备类型,编译对应存储设备的环境变量存储驱动文件,具体可参考u-boot/common/Makefile。比如在mini2440.h中定义了#define CONFIG_ENV_IS_IN_NAND 1 ,表明系统中环境变量存储在nand flash中,uboot在编译的时候会编译env_nand.c,在该文件中,env_ini()定义和实现如下:
?????
/* this is called before nand_init()
?* so we can't read Nand to validate env data.
?* Mark it OK for now. env_relocate() in env_common.c
?* will call our relocate function which does the real
?* validation.
?*
?* When using a NAND boot image (like sequoia_nand),the environment
?* can be embedded or attached to the U-Boot image in NAND flash. This way
?* the SPL loads not only the U-Boot image from NAND but also the
?* environment.
?*/
int env_init(void)
{
????? #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
???????? int crc1_ok = 0,crc2_ok = 0;
???????? env_t *tmp_env1;
??????? #ifdef CONFIG_ENV_OFFSET_REDUND
?????????????? env_t *tmp_env2;
?????????????? tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
?????????????? crc2_ok = (crc32(0,tmp_env2->data,ENV_SIZE) == tmp_env2->crc);
??????? #endif
??????? tmp_env1 = env_ptr;
??????? crc1_ok = (crc32(0,tmp_env1->data,ENV_SIZE) == tmp_env1->crc);
?????? if (!crc1_ok && !crc2_ok)
????? {
??????????? gd->env_addr? = 0;
??????????? gd->env_valid = 0;
??????????? return 0;
?????? }
????? else if (crc1_ok && !crc2_ok) {
??????????? gd->env_valid = 1;
????? }
???? #ifdef CONFIG_ENV_OFFSET_REDUND
?????????? else if (!crc1_ok && crc2_ok) {
??????????? gd->env_valid = 2;
???????? } else {
???????? /* both ok - check serial */
??????? if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
?????? gd->env_valid = 2;
????? else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
?? gd->env_valid = 1;
? else if(tmp_env1->flags > tmp_env2->flags)
?? gd->env_valid = 1;
? else if(tmp_env2->flags > tmp_env1->flags)
?? gd->env_valid = 2;
? else /* flags are equal - almost impossible */
?? gd->env_valid = 1;
?}
?if (gd->env_valid == 2)
? env_ptr = tmp_env2;
?else
#endif
?if (gd->env_valid == 1)
? env_ptr = tmp_env1;
?gd->env_addr = (ulong)env_ptr->data;
#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
?gd->env_addr? = (ulong)&default_environment[0];
?gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
?return (0);
}
???? 其实这段代码,只看注释就能明白个大概,注释中说,这段代码将在nand_init()之前调用,所以在这段函数中是无法从nand里面读取到存储到的环境变量的,这里先假设环境变量是可用的,设置gd—>env_valid =1;然后在env_relocate ()会进行真正的判断。这也说明在拜读别人的代码的时候,仔细阅读下别人的注释也是多么的重要啊。
??? 在看代码段(2),在这里调用了env_relocate()函数。该函数主要代码如下:
???
void env_relocate (void)
{
?????????? ...............................................
????????? #ifdef ENV_IS_EMBEDDED
?????????????????? /*
??????????????????? * The environment buffer is embedded with the text segment,
??????????????????? * just relocate the environment pointer
????????????????? */
??????????????? #ifndef CONFIG_RELOC_FIXUP_WORKS
?????????????????????? env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
?????????????? #endif
???????????????????? DEBUGF ("%s[%d] embedded ENV at %pn",__FUNCTION__,__LINE__,env_ptr);
????????? #else
(1):????????? /*
????????????? * We must allocate a buffer for the environment
???????????? */
??????????? env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);
??????????? DEBUGF ("%s[%d] malloced ENV at %pn",env_ptr);
???????? #endif
(2):
???? if (gd->env_valid == 0)
?? {
????? #if defined(CONFIG_GTH) || defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */
???????????? puts ("Using default environmentnn");
???? #else
??????????? puts ("*** Warning - bad CRC,using default environmentnn");
???????? show_boot_progress (-60);
??? #endif
? set_default_env();
? }
?else
?{
? env_relocate_spec ();
?}
?gd->env_addr = (ulong)&(env_ptr->data);
............................................................
}
?????????? 这段代码先在代码段(1)中从内存中为环境变量分配空间,然后在代码段(2)中,犹豫我们在env_init()中,已经强制将gd->env_valid设置为1,所以这里将调用env_relocate_spec()函数。在env_nand.c中,evn_relocate_spec()实现如下:
/*
?* The legacy NAND code saved the environment in the first NAND device i.e.,
?* nand_dev_desc + 0. This is also the behaviour using the new NAND code.
?*/
void env_relocate_spec (void)
{
???? #if !defined(ENV_IS_EMBEDDED)
???????? int ret;
??????? ret = readenv(CONFIG_ENV_OFFSET,(u_char *) env_ptr);
?????? if (ret)
????????? return use_default();
????? if (crc32(0,env_ptr->data,ENV_SIZE) != env_ptr->crc)
????????? return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}
????? 这段代码用意很明确,先从nand中evn变量的存储区将env参数读取到内存中为env变量分配的区域,然后对这些env数据进行crc校验以验证数据的有效性。如果读取失败,或者crc校验失败,就会调用use_default()函数,使用系统默认的环境变量,这时候我们将在串口等终端上看到“*** Warning - bad CRC or NAND,using default environment”
????? 这里再说下env_ptr指向的环境变量的数据结构,相关定义如下:
? typedef struct environment_s {
?uint32_t crc;? /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
?unsigned char flags;? /* active/obsolete flags */
#endif
?unsigned char data[ENV_SIZE]; /* Environment data? */
} env_t;
env_t *env_ptr=0;
???? 在用户执行setenv命令的时候, 会调用env_crc_update ()函数,更新env数据中对应的crc,然后用户执行saveenv命令的时候,env数据和crc都被存储到固态存储设备中,下一次从固态设备中读取env数据的时候,根据读取到的数据计算出对应的crc校验和,然后与读取到的crc 数据比较,就知道获取的数据是否合法。