uboot 源码分析(2)uboot 环境变量实现简析

                                          uboot 环境变量实现简析

                                                         ----------基于u-boot-2010.03

 

             

         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, 0, sizeof (gd_t));
      gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
      memset (gd->bd, 0, 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 %p\n", __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 %p\n", __FUNCTION__,__LINE__,env_ptr);
         #endif

(2):

     if (gd->env_valid == 0)

   {
      #if defined(CONFIG_GTH) || defined(CONFIG_ENV_IS_NOWHERE) /* Environment not changable */
             puts ("Using default environment\n\n");
     #else
            puts ("*** Warning - bad CRC, using default environment\n\n");
         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 数据比较,就知道获取的数据是否合法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值