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 數據比較,就知道獲取的數據是否合法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM