本例中,nand flash BBT(Bad Block Table)的建立是在读取环境变量时建立的。
先看下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()
本函数实际代码中会涉及ENV_IS_EMBEDDED和CONFIG_NAND_ENV_DST两个宏定义,其含义可以参考代码中的注释,一般不会用到。
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 一般会保存两次环境变量
函数返回后,gd->env_addr 等于 (ulong)&(env_ptr->data);
环境变量就被读到RAM中,以后访问环境变量就可以通过全局变量gd->env_addr进行访问
下面分析readenv()函数,了解下flash BBT的建立流程
先看下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;
}