先看下uboot環境變量的處理流程
start_armboot() arch/arm/lib/board.c
-->init_sequence[] arch/arm/lib/board.c
-->env_init() common/env_nand.c
-->env_relocate() common/env_common.c
-->env_relocate_spec() common/env_nand.c
主要涉及三個函數env_init()、env_relocate()和env_relocate_spec()
int env_init(void)
{
gd->env_addr = (ulong)&default_environment[0];//先指向默認的環境變量數組
gd->env_valid = 1;
}
本函數實際代碼中會涉及ENV_IS_EMBEDDED和CONFIG_NAND_ENV_DST兩個宏定義,其含義可以參考代碼中的註釋,一般不會用到。
void env_relocate (void)
{
if (gd->env_valid == 0) {
set_default_env();
}
else {
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
}
env_relocate()函數會調用env_relocate_spec()。
查看common/env_nand.c,common/env_dataflash.c,common/env_eeprom.c,common/env_sf.c文件
會發現這些文件中都含有env_get_char_spec()、saveenv()、env_init()、env_relocate_spec()函數,
可以推斷出common/env_common.c文件向u-boot提供通用的函數接口,
它們隱藏了env的不同實現方式,比如dataflash, epprom, nand,spiflash等,
而common/env_nand.c,common/env_nand.c,common/env_eeprom.c,common/env_sf.c則是env在具體設備上的實現
nandflash 一般會保存兩次環境變量
void env_relocate_spec (void)
{
int crc1_ok = 0, crc2_ok = 0;
env_t *tmp_env1, *tmp_env2;
tmp_env1 = (env_t *) malloc(CONFIG_ENV_SIZE);//分配內存空間用於存放第一塊環境變量
tmp_env2 = (env_t *) malloc(CONFIG_ENV_SIZE);//分配內存空間用於存放第二塊環境變量
if ((tmp_env1 == NULL) || (tmp_env2 == NULL)) {
puts("Can't allocate buffers for environment\n");
free (tmp_env1);
free (tmp_env2);
return use_default();
}
if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))//從nand flash中讀取第一塊環境變量到tmp_env1,該函數會進行BBT的掃描
puts("No Valid Environment Area Found\n");
if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))//從nand flash中讀取第一塊環境變量到tmp_env2
puts("No Valid Reundant Environment Area Found\n");
//計算crc,並和讀取到的crc比較。nandflash保存環境變量的區域,
//除了實際的環境變量之外,還含有crc值和flags。可以參見environment_s數據結構
crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
//判斷使用第幾塊環境變量
if(!crc1_ok && !crc2_ok) {
free(tmp_env1);
free(tmp_env2);
return use_default();//crc都不對,就用默認的環境變量
} else if(crc1_ok && !crc2_ok)
gd->env_valid = 1;
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;
}
free(env_ptr);
if(gd->env_valid == 1) {
env_ptr = tmp_env1;
free(tmp_env2);
} else {
env_ptr = tmp_env2;
free(tmp_env1);
}
}
函數返回後,gd->env_addr 等於 (ulong)&(env_ptr->data);
環境變量就被讀到RAM中,以後訪問環境變量就可以通過全局變量gd->env_addr進行訪問
uchar env_get_char_spec (int index)
{
return ( *((uchar *)(gd->env_addr + index)) );
}
下面分析readenv()函數,瞭解下flash BBT的建立流程
int readenv (size_t offset, u_char * buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t amount_loaded = 0;
size_t blocksize, len;
u_char *char_ptr;
int ret;
blocksize = nand_info[0].erasesize;//獲取nand flash的塊大小
if (!blocksize)
{
printf("readenv blocksize = 0\n");
return 1;
}
len = min(blocksize, CONFIG_ENV_SIZE);//如果環境變量的大小大於一個塊的大小,則一塊一塊的讀取
while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
//判斷是否是壞塊,是則讀取下一塊。第一次調用該函數時,會建立BBT
if (nand_block_isbad(&nand_info[0], offset)) {
offset += blocksize;
} else {
char_ptr = &buf[amount_loaded];
//從nand flash中讀取數據,偏移地址offset,讀取長度len,保存地址char_ptr
if (ret = nand_read(&nand_info[0], offset, &len, char_ptr))
{
printf("readenv nand_read ret = %d\n",ret);
return 1;
}
//調整偏移地址
offset += blocksize;
//調整保存地址
amount_loaded += len;
printf("readenv 2 amount_loaded = %d\n",amount_loaded);
}
}
if (amount_loaded != CONFIG_ENV_SIZE)
{
printf("readenv amount_loaded = %d\n",amount_loaded);
return 1;
}
return 0;
}