- 論壇徽章:
- 0
|
1.相關(guān)文件common/env_common.c
供u-boot調(diào)用的通用函數(shù)接口,它們隱藏了env的不同實現(xiàn)方式,比如dataflash, epprom, flash等
common/env_dataflash.c
env 存儲在dataflash中的實現(xiàn)
common/env_epprom.c
env 存儲在epprom中的實現(xiàn)
common/env_flash.c
env 存儲在flash中的實現(xiàn)
common/env_nand.c
env 存儲在nand中的實現(xiàn)
common/env_nvedit.c
實現(xiàn)u-boot對環(huán)境變量的操作命令
environment.c
環(huán)境變量以及一些宏定義
env如果存儲在Flash中還需要Flash的支持。
2.數(shù)據(jù)結(jié)構(gòu)env 在 u-boot 中通常有兩種存在方式,在永久性存儲介質(zhì)中( Flash NVRAM等 )在SDRAM,可以配置不使用 env 的永久存儲方式,但這不常用。u-boot 在啟動的時候會將存儲在永久性存儲介質(zhì)中的 env 重新定位到 RAM 中,這樣可以快速訪問,同時可以通過saveenv將 RAM 中的 env 保存到永久性存儲介質(zhì)中。
在include/environment.h中定義了表示env的數(shù)據(jù)結(jié)構(gòu)
typedef struct environment_s
{
unsigned long crc; /* CRC32 over data bytes */
#ifdef CFG_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;
關(guān)于以上結(jié)構(gòu)的說明:
crc是u-boot在保存env 的時候加上去的校驗頭,在第一次啟動時一般 crc校驗會出錯,這很正常,因為這時 Flash中的數(shù)據(jù)無效。
data字段保存實際的環(huán)境變量。u-boot 的 env 按 name=value”\0”的方式存儲,在所有env的最后以”\0\0”表示整個 env 的結(jié)束。新的name=value對總是被添加到 env 數(shù)據(jù)塊的末尾,當(dāng)刪除一個name=value對時,后面的環(huán)境變量將前移,對一個已經(jīng)存在的環(huán)境變量的修改實際上先刪除再插入。
env 可以保存在 u-boot 的 TEXT 段中,這樣 env 就可以同 u-boot 一同加載入RAM中,這種方法沒有測試過。
上文提到u-boot會將 env 從 flash 等存儲設(shè)備重定位到 RAM 中,在 env 的不同實現(xiàn)版本( env_xxx.c )中定義了 env_ptr, 它指向 env在RAM中的位置。u-boot在重定位 env后對環(huán)境變量的操作都是針對 env_ptr。
env_t 中除了數(shù)據(jù)之外還包含校驗頭,u-boot 把env_t 的數(shù)據(jù)指針有保存在了另外一個地方,這就是 gd_t 結(jié)構(gòu)( 不同平臺有不同的 gd_t結(jié)構(gòu) ),這里以ARM為例僅列出和 env 相關(guān)的部分
typedef struct global_data
{
…
unsigned long env_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct ??? */
unsigned long env_valid /* Checksum of Environment valid */
…
} gd_t;
ta.h>
gd_t.env_addr 即指向 env_ptr->data。
3.ENV 的初始化
![]()
500)this.width=500;" border=0>
start_armboot : ( lib_arm/board.c )
*env_init : env_xxx.c( xxx = nand | flash | epprom … )
env_relocate : env_common.c
*env_relocate_spec : env_xxx.c( xxx=nand | flash | eporom… )
3.1env_init實現(xiàn) env 的第一次初始化,對于nand env (非embedded方式):
Env_nand.c : env_init
gd->env_addr = (ulong)&default_environment[0]; //先使gd->env_addr指向默認(rèn)的環(huán)境變量
gd->env_valid = 1;// env 有效位置1
3.2 env_relocate#ifdefine ENV_IS_EMBEDDED
…(略)
#else
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
#endif
if( gd->env_valid == 0) // 在 Env_annd.c : env_init 中已經(jīng)將 gd->env_valid 置1
{
…
}
else
env_relocate_spec ();// 調(diào)用具體的 env_relocate_spec 函數(shù)
gd->env_addr = (ulong)&(env_ptr->data);// 最終完成將環(huán)境變量搬移到內(nèi)存
這里涉及到兩個和環(huán)境變量有關(guān)的宏
ENV_IS_EMBEDDED : env 是否存在于 u-boot TEXT 段中
CFG_ENV_SIZE : env 塊的大小
實際上還需要幾個宏來控制u-boot 對環(huán)境變量的處理
CFG_ENV_IS_IN_NAND : env 塊是否存在于Nand Flash 中
CFG_ENV_OFFSET : env 塊在 Flash 中偏移地址
3.3*env_relocate_spec這里僅分析 Nand Flash 的 env_relocate_spec 實現(xiàn)
如果未設(shè)置 CFG_ENV_OFFSET_REDUND,env_relocate_spec的實現(xiàn)如下 :
void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
ulong total;
int ret;
total = CFG_ENV_SIZE;
ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
if (ret || total != CFG_ENV_SIZE)
return use_default();
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}
上面的代碼很清楚的表明了 env_relocate_spec 的意圖,調(diào)用 nand_read 將環(huán)境變量從 CFG_ENV_OFFSET 處讀出,環(huán)境變量的大小為CFG_ENV_SIZE 注意 CFG_ENV_OFFSET和 CFG_ENV_SIZE 要和 Nand Flash 的塊/頁邊界對齊。讀出數(shù)據(jù)后再調(diào)用crc32 對env_ptr->data進行校驗并與保存在 env_ptr->crc 的校驗碼對比,看數(shù)據(jù)是否出錯,從這里也可以看出在系統(tǒng)第一次啟動時,Nand Flash 里面沒有存儲任何環(huán)境變量,crc校驗肯定回出錯,當(dāng)我們保存環(huán)境變量后,接下來再啟動板子u-boot就不會再報crc32出錯了。
4. ENV 的保存由上問的論述得知, env 將從永久性存儲介質(zhì)中搬到RAM里面,以后對env 的操作,比如修改環(huán)境變量的值,刪除環(huán)境變量的值都是對這個 env 在RAM中的拷貝進行操作,由于RAM的特性,下次啟動時所做的修改將全部消失,u-boot提供了將env 寫回 永久性存儲介質(zhì)的命令支持 : saveenv,不同版本的 env ( nand flash, flash … )實現(xiàn)方式不同,以Nand Flash 的實現(xiàn)(未定義CFG_ENV_OFFSET_REDUND)為例
Env_nand.c : saveenv
int saveenv(void)
{
ulong total;
int ret = 0;
puts ("Erasing Nand...");
if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE))
return 1;
puts ("Writing to Nand... ");
total = CFG_ENV_SIZE;
ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
if (ret || total != CFG_ENV_SIZE)
return 1;
puts ("done\n");
return ret;
}
Nand Flash 的 saveenv 命令實現(xiàn)很簡單,調(diào)用nand_erase 和nand_write進行Nand Flash的 erase, write。nand_write/erase使用的是u-boot 的nand驅(qū)動框架,我在做開發(fā)的過程中使用的是nand_legacy驅(qū)動,所以可以把nand_erase和nand_write改成nand_legacy_erase和nand_legacy_rw就可實現(xiàn)nand_legacy驅(qū)動的保存環(huán)境變量版本。
1、參數(shù)表的結(jié)構(gòu)定義在environment.c中,如下:
#ifdef CFG_REDUNDAND_ENVIRONMENT
# define ENV_HEADER_SIZE (sizeof(unsigned long) + 1)
#else
# define ENV_HEADER_SIZE (sizeof(unsigned long))
#endif
//除去參數(shù)表頭后參數(shù)的長度最值
#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE)
typedef struct environment_s {
unsigned long crc; /* CRC32 over data bytes */
#ifdef CFG_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;
結(jié)構(gòu)env_t參數(shù)表的結(jié)構(gòu)非常簡單,第一個成員就是crc,用于crc32校驗,第二個參數(shù)是冗余的標(biāo)志,最后一個就是參數(shù)數(shù)組了。所以參數(shù)頭的長度ENV_HEADER_SIZE就是crc與flags之和,即為sizeof(long)+sizeof(char)。這個結(jié)構(gòu)就是在內(nèi)存和flash上表示參數(shù)表的結(jié)構(gòu)。
參數(shù)表的最后一個成員data數(shù)組中存放所有的環(huán)境變量值,每個變量和值用‘=’號連接,而兩個變量之間則通過’\0’分開,如下:
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "\0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
#endif
……
"\0"
};
2、環(huán)境變量的初始化env_relocate()
Uboot在完成匯編部分的初始化之后,將跳到start_armboot()去執(zhí)行,其中便會執(zhí)行env_relocate()初始化環(huán)境變量。
去除了一些不執(zhí)行的代碼后,這個函數(shù)如下:
void env_relocate (void)
{
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE); // 1
/*
* After relocation to RAM, we can always use the "memory" functions
*/
env_get_char = env_get_char_memory; // 2
if (gd->env_valid==0)
default_env(); // 3
else {
env_relocate_spec (); // 4
}
gd->env_addr = (ulong)&(env_ptr->data); // 5
}
第一步,初始化一個全局指針,它被定義為:
env_t *env_ptr = 0;
第二步,重新初始化函數(shù)指針,
static uchar env_get_char_init (int index);
uchar (*env_get_char)(int) = env_get_char_init;
該函數(shù)指針原來被初始化為env_get_char_init,現(xiàn)在改為env_get_char_memory。對于nand flash,這兩個函數(shù)是一樣的。
第三步,如果flash沒有參數(shù)表,則使用默認(rèn)參數(shù),這里是通過default_env()來加載。
void default_env(void)
{
memset (env_ptr, 0, sizeof(env_t));
memcpy (env_ptr->data,
default_environment,
sizeof(default_environment)); //拷貝環(huán)境變量
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update (); //更新crc32校驗
gd->env_valid = 1; //標(biāo)識環(huán)境變量可用
}
第四步,如果flash上有參數(shù)表可用,則從flash上加載,通過env_relocate_spec()來實現(xiàn):
void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED) //如果不是使用嵌入?yún)?shù)的形式,即為參數(shù)表的形式
ulong total;
int ret;
total = CFG_ENV_SIZE; //參數(shù)表大小,包括參數(shù)表頭部
//讀出操作,flash設(shè)備為nand_info,偏移為CFG_ENV_OFFSET,讀出的大小為total,目標(biāo)地址由env_ptr所指。
ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
//如果讀出的長度不對或出錯,則使用默認(rèn)值
if (ret || total != CFG_ENV_SIZE)
return use_default();
//如果校驗出錯,使用默認(rèn)值
if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)
return use_default();
#endif /* ! ENV_IS_EMBEDDED */
}
此外,uboot的參數(shù)表還支持一種被稱為CFG_ENV_OFFSET_REDUND的冗余模式,它會在flash上保存兩個參數(shù)表副本,這樣在一個副本出錯的時候,還可以從另一個副本中去讀取,通過這種方式,提高了數(shù)據(jù)的安全性。
第五步,gd->env_addr = (ulong)&(env_ptr->data)
即將環(huán)境變量的值賦值給全局變量gd->env_addr,這樣只要通過這個全局變量就可以訪問這些變量了。
值得一提的是,字符串?dāng)?shù)組data里面的變量與變量之間是通過’\0’來分割的。
3、環(huán)境變量的保存,保存是讀取的反過程,所以跟上面的過程相似,如下:
int saveenv(void)
{
ulong total;
int ret = 0;
//先擦除
if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE))
//寫入
total = CFG_ENV_SIZE;
ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
if (ret || total != CFG_ENV_SIZE)
return 1;
puts ("done\n");
return ret;
}
4、讀取環(huán)境變量
Uboot中經(jīng)常要讀取環(huán)境變量,這是通過getenv來實現(xiàn)的:
/ * Look up variable from environment,
* return address of storage for that variable,
* or NULL if not found
*/
char *getenv (char *name)
{
int i, nxt;
for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
int val;
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
if (nxt >= CFG_ENV_SIZE) {
return (NULL);
}
}
if ((val=envmatch((uchar *)name, i))
continue;
//通過所得的下標(biāo)返回變量值的指針,由于是字符串指針,所以它在碰到’\0’符合時結(jié)束,即為該變量的值。
return ((char *)env_get_addr(val));
}
return (NULL);
}
通過輸入變量的名字,返回變量的值。
前面已經(jīng)提到,函數(shù)指針env_get_char已經(jīng)被初始化為env_get_char_memory:
該函數(shù)獲取環(huán)境變量數(shù)組中下標(biāo)為index的字符。
uchar env_get_char_memory (int index)
{
if (gd->env_valid) {
return ( *((uchar *)(gd->env_addr + index)) );
} else {
return ( default_environment[index] );
}
}
/************************************************************************
* Match a name / pair
*
* s1 is either a simple 'name', or a ' pair.
* i2 is the environment index for a 'name2=value2' pair.
* If the names match, return the index for the value2, else NULL.
*/
查找符號變量,如果找到則返回等號后面的字符串指針,即為變量的值。
static int
envmatch (uchar *s1, int i2)
{
while (*s1 == env_get_char(i2++))
if (*s1++ == '=')
return(i2);
if (*s1 == '\0' && env_get_char(i2-1) == '=')
return(i2);
return(-1);
}
如前所述,環(huán)境變量表是一個字符串?dāng)?shù)組,而其中的變量之間通過’\0’符號隔開,即是當(dāng)遇到該符號時,則表示一個變量結(jié)束而另一個變量開始。
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u3/91445/showart_2184050.html |
|