env分区在读写过程中,如果突然断电,有极小的概率会出现数据损坏,导致env丢失,一但数据丢失系统会启动失败,为保证系统的启动,当env分区数据CRC校验失败时,会自动从default值去启动。
在uboot的初始化过程中,环境变量的初始化加载过程如下:
board_r.c/initr_env() ---> env/common.c/env_relocate() ---> env/env.c/env_load()
在env_load()函数中,会根据启动介质(mmc spi)去选择env存储位置,然后调用相应介质的load函数将环境变量读取到系统中。
int env_load(void)
{
struct env_driver *drv;
int best_prio = -1;
int prio;
for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
int ret;
if (!env_has_inited(drv->location))
continue;
printf("Loading Environment from %s... ", drv->name);
/*
* In error case, the error message must be printed during
* drv->load() in some underlying API, and it must be exactly
* one message.
*/
ret = drv->load();
if (!ret) {
printf("OK\n");
gd->env_load_prio = prio;
#if !CONFIG_IS_ENABLED(ENV_APPEND)
return 0;
#endif
} else if (ret == -ENOMSG) {
/* Handle "bad CRC" case */
if (best_prio == -1)
best_prio = prio;
} else {
debug("Failed (%d)\n", ret);
}
}
/*
* In case of invalid environment, we set the 'default' env location
* to the best choice, i.e.:
* 1. Environment location with bad CRC, if such location was found
* 2. Otherwise use the location with highest priority
*
* This way, next calls to env_save() will restore the environment
* at the right place.
*/
if (best_prio >= 0)
debug("Selecting environment with bad CRC\n");
else
best_prio = 0;
gd->env_load_prio = best_prio;
return -ENODEV;
}
./env/mmc.c:391:
U_BOOT_ENV_LOCATION(mmc) = {
.location = ENVL_MMC,
ENV_NAME("MMC")
.load = env_mmc_load,
#ifndef CONFIG_SPL_BUILD
.save = env_save_ptr(env_mmc_save),
#if defined(CONFIG_CMD_ERASEENV)
.erase = env_mmc_erase,
#endif
#endif
};
static enum env_location env_locations[] = {
#ifdef CONFIG_ENV_IS_IN_EEPROM
ENVL_EEPROM,
#endif
#ifdef CONFIG_ENV_IS_IN_EXT4
ENVL_EXT4,
#endif
#ifdef CONFIG_ENV_IS_IN_FAT
ENVL_FAT,
#endif
#ifdef CONFIG_ENV_IS_IN_FLASH
ENVL_FLASH,
#endif
#ifdef CONFIG_ENV_IS_IN_MMC
ENVL_MMC,
#endif
#ifdef CONFIG_ENV_IS_IN_NAND
ENVL_NAND,
#endif
#ifdef CONFIG_ENV_IS_IN_NVRAM
ENVL_NVRAM,
#endif
#ifdef CONFIG_ENV_IS_IN_REMOTE
ENVL_REMOTE,
#endif
#ifdef CONFIG_ENV_IS_IN_SATA
ENVL_ESATA,
#endif
#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
ENVL_SPI_FLASH,
#endif
#ifdef CONFIG_ENV_IS_IN_UBI
ENVL_UBI,
#endif
#ifdef CONFIG_ENV_IS_NOWHERE
ENVL_NOWHERE,
#endif
};
#ifdef CONFIG_ENV_OFFSET_REDUND
static int env_mmc_load(void)
{
#if !defined(ENV_IS_EMBEDDED)
struct mmc *mmc;
u32 offset1, offset2;
int read1_fail = 0, read2_fail = 0;
int ret;
int dev = mmc_get_env_dev();
const char *errmsg = NULL;
ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
mmc_initialize(NULL);
mmc = find_mmc_device(dev);
errmsg = init_mmc_for_env(mmc);
if (errmsg) {
ret = -EIO;
goto err;
}
if (mmc_get_env_addr(mmc, 0, &offset1) ||
mmc_get_env_addr(mmc, 1, &offset2)) {
ret = -EIO;
goto fini;
}
read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
read2_fail, H_EXTERNAL);
fini:
fini_mmc_for_env(mmc);
err:
if (ret)
env_set_default(errmsg, 0);
#endif
return ret;
}
int env_import_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail,
int flags)
{
env_t *ep;
int ret;
ret = env_check_redund(buf1, buf1_read_fail, buf2, buf2_read_fail);
if (ret == -EIO) {
env_set_default("bad env area", 0);
return -EIO;
} else if (ret == -EINVAL) {
return env_import((char *)buf1, 1, flags);
} else if (ret == -ENOENT) {
return env_import((char *)buf2, 1, flags);
} else if (ret == -ENOMSG) {
env_set_default("bad CRC", 0);
return -ENOMSG;
}
if (gd->env_valid == ENV_VALID)
ep = (env_t *)buf1;
else
ep = (env_t *)buf2;
env_flags = ep->flags;
return env_import((char *)ep, 0, flags);
}
int env_check_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail)
{
int crc1_ok, crc2_ok;
env_t *tmp_env1, *tmp_env2;
tmp_env1 = (env_t *)buf1;
tmp_env2 = (env_t *)buf2;
if (buf1_read_fail && buf2_read_fail) {
puts("*** Error - No Valid Environment Area found\n");
} else if (buf1_read_fail || buf2_read_fail) {
puts("*** Warning - some problems detected ");
puts("reading environment; recovered successfully\n");
}
if (buf1_read_fail && buf2_read_fail) {
return -EIO;
} else if (!buf1_read_fail && buf2_read_fail) {
gd->env_valid = ENV_VALID;
return -EINVAL;
} else if (buf1_read_fail && !buf2_read_fail) {
gd->env_valid = ENV_REDUND;
return -ENOENT;
}
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) {
return -ENOMSG; /* needed for env_load() */
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = ENV_VALID;
} else if (!crc1_ok && crc2_ok) {
gd->env_valid = ENV_REDUND;
} else {
/* both ok - check serial */
if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
gd->env_valid = ENV_REDUND;
else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
gd->env_valid = ENV_VALID;
else if (tmp_env1->flags > tmp_env2->flags)
gd->env_valid = ENV_VALID;
else if (tmp_env2->flags > tmp_env1->flags)
gd->env_valid = ENV_REDUND;
else /* flags are equal - almost impossible */
gd->env_valid = ENV_VALID;
}
return 0;
}
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags ENVF_REDUND_ */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;