一、環境變量概述
1、環境變量的概念
可以理解為用戶對軟件的全局配置信息,這部分信息應該可以從永久性存儲器上讀取,能被查詢,能被修改。
啟動過程中,應該首先把環境變量讀取到合適的內存區域,然后利用環境變量初始化硬件、啟動操作系統等等。
2、啟動過程中環境變量初始化過程涉及的問題
這里涉及到兩個問題:
環境變量在哪個地方存着(從哪個地方取)
將環境變量存儲到哪里(放到哪)
(1)環境變量位於存儲器(norflash、nandflash )
“CFG_ENV_IS_IN_XXX”(CFG_ENV_IS_IN_FLASH、CFG_ENV_IS_IN_NAND等等)定義了則這種情況有效,以在flash上為例。
ENV_IS_EMBEDDED定義了
詳細工作原理,見”ENV_IS_EMBEDDED“解惑以及相關的移植實驗。這種情況的環境變量在flash上存着(但是占了flash一個扇區),並且隨着代碼段(因為環境變量區嵌在代碼段內)在start.s重定位時一同載入內存。在環境變量初始化時候,如果這部分能通過校驗,就不需要先在堆區開辟空間然后搬移的工作,而是直接使用這部分環境變量(省了搬移工作)。倘若不能通過校驗,則使用默認環境變量放到重定位時環境變量所占的空間中。
ENV_IS_EMBEDDED沒有定義
這種情況會在堆區為環境變量區開辟空間,如果flash上存儲的是有效的(能通過校驗)環境變量,則需要把flash上的數據搬運到堆區指定的位置;如果flash上的存儲是錯誤的環境變量,那么使用默認的環境變量(default_environment)放到堆區。
(2)沒有存儲器上存儲有環境變量
“CFG_ENV_IS_NOWHERE”定義了則選擇這種模式,使用common/env_nowhere.c文件而不是用env_flash.c、env_nvram.c等等文件。
這種情況下,使用默認的環境變量(default_environment)。先在堆區為環境變量開辟空間,然后啟動搬運工作。
二、環境變量初始化流程
以環境變量位於NorFlash上,並且沒有使能“ENV_IS_EMBEDDED”功能為例,進行以下內容的分析。其他情況本文不討論。
1、校驗
直接在NorFlash上校驗環境變量,實際上這一步是確定環境變量的源。如果NorFlash上存儲的是有效的環境變量的話,那么就從NorFlash上讀取數據。倘若NorFlash上存儲的是無效的環境變量,那么使用默認的環境變量作為源。
2、重定位
將從存儲環境變量的存儲區加載到系統指定的位置。
3、涉及到的函數
(1)env_init
完成校驗,並決定環境變量的源
此函數在start_armboot函數的開始階段,會依次執行“init_sequence”中的每一個函數,其中一個就是env_init。
(2)env_relocate
首先,在堆區為環境變量開辟存儲緩沖區。另外,當NorFlash上的環境變量區是無效的時候,選擇默認的環境變量區進行定位。當NorFlash上的環境變量區是有效的時候,調用env_relocate_spec進行重定位。
此函數是在start_armboot函數的中途階段,在NorFlash和NandFlash都初始化后,會調用這個函數。
(3)env_relocate_spec
此函數是被env_relocate所調用。
三、代碼分析
1、相關全局變量
1> env_ptr(common/env_flash.c)
env_t *env_ptr = (env_t *)CFG_ENV_ADDR; // 定義flash中環境變量的地址 #ifdef CMD_SAVEENV static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;
2> default_environment(common/env_common.c)
uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif
somestrings
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" };
2、env_init(common/env_flash.c)
if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { //如果校驗成功
gd->env_addr = (ulong)&(env_ptr->data); gd->env_valid = 1; //1代表flash中存在環境變量 return(0); } gd->env_addr = (ulong)&default_environment[0]; //不成功,則使用系統默認的環境變量 //default_environment僅僅是環境變量的data區,不包含頭部crc 、flags gd->env_valid = 0; //0代表使用默認的環境變量 return (0);
3、env_relocate(common/env_common.c)
void env_relocate (void) { /* * We must allocate a buffer for the environment */ env_ptr = (env_t *)malloc (CFG_ENV_SIZE); //為環境變量區分配空間 DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); if (gd->env_valid == 0) { //env_init中決定了env_valid的值,倘若flash中存在的環境變量校驗錯誤 #if defined(CONFIG_GTH) || defined(CFG_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 (-1); #endif //#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE) ENV_SIZE在include/environment.h中定義 if (sizeof(default_environment) > ENV_SIZE) { puts ("*** Error - default environment is too large\n\n"); return; } memset (env_ptr, 0, sizeof(env_t)); //為環境變量存儲區清零空間 memcpy (env_ptr->data, default_environment, sizeof(default_environment)); //將環境默認變量拷貝到RAM中的指定區域 env_crc_update (); //更新crc gd->env_valid = 1; //env_valid確認有效了 } else { //倘若flash中存在的環境變量校驗成功 env_relocate_spec (); /*該函數實現真正的重定位功能,先從NAND flash中讀取環境變量,如果讀取成*/ } gd->env_addr = (ulong)&(env_ptr->data); //將環境變量的首地址(不含crc頭部)賦給全局變量gd->env_addr }
修改env_common.c中第47行“#undef DEBUG_ENV”變為“#define DEBUG_ENV”,並在函數一開始處加上語句
DEBUGF ("%s[%d] env_valid = 0x%8x\n", __FUNCTION__,__LINE__, gd->env_valid);
重新編譯后下載,調試結果如下。

可見執行完env_init()函數中env_valid被置為0,這是由於一開始NorFlash上並沒有存儲環境變量,當然會校驗錯誤。並且,可以看到環境變量緩沖區被開在堆區。
4、 env_relocate_spec(common/env_flash.c)
void env_relocate_spec (void) //此函數被env_relocate()函數調用 { memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE); //在env_relocate已經將env_ptr指向環境變量存儲區 }
四、環境變量的源位置和加載位置
回過頭來,再看一下環境變量的源在哪兒,加載位置又在哪兒?看一下重定位的關鍵函數調用:
memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE);
flash_addr是源位置,在0x70000
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR; #define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */ #define CFG_FLASH_BASE PHYS_FLASH_1 #define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x070000) /* addr of environment */ #define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
env_ptr是加載位置,位於堆區
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
總結
env_init()函數讀取flash中環境變量區然后校驗,“gd->env_valid ”記錄了校驗成功與否。進入env_relocate函數,先將環境變量區設定在堆區,然后根據校驗標志位決定是從flash中拷貝,還是從默認環境變量區拷貝(這種情況還需要更新校驗crc)。
