env_mmc: add support for redundant environment

This patch add support for storing the environment redundant on
mmc devices. Substantially it re-uses the logic from the NAND implementation,
that means using an incremental counter for marking newer data.

Signed-off-by: Michael Heimpold <mhei@heimpold.de>
This commit is contained in:
Michael Heimpold 2013-04-10 10:36:19 +00:00 committed by Tom Rini
parent d2eae43ba8
commit d196bd8803
2 changed files with 124 additions and 10 deletions

View File

@ -32,7 +32,7 @@
#define ESDHC_BOOT_IMAGE_SIZE 0x48 #define ESDHC_BOOT_IMAGE_SIZE 0x48
#define ESDHC_BOOT_IMAGE_ADDR 0x50 #define ESDHC_BOOT_IMAGE_ADDR 0x50
int mmc_get_env_addr(struct mmc *mmc, u32 *env_addr) int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
{ {
u8 *tmp_buf; u8 *tmp_buf;
u32 blklen, code_offset, code_len, n; u32 blklen, code_offset, code_len, n;

View File

@ -32,6 +32,11 @@
#include <search.h> #include <search.h>
#include <errno.h> #include <errno.h>
#if defined(CONFIG_ENV_SIZE_REDUND) && \
(CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
#error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
#endif
char *env_name_spec = "MMC"; char *env_name_spec = "MMC";
#ifdef ENV_IS_EMBEDDED #ifdef ENV_IS_EMBEDDED
@ -46,9 +51,13 @@ DECLARE_GLOBAL_DATA_PTR;
#define CONFIG_ENV_OFFSET 0 #define CONFIG_ENV_OFFSET 0
#endif #endif
__weak int mmc_get_env_addr(struct mmc *mmc, u32 *env_addr) __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
{ {
*env_addr = CONFIG_ENV_OFFSET; *env_addr = CONFIG_ENV_OFFSET;
#ifdef CONFIG_ENV_OFFSET_REDUND
if (copy)
*env_addr = CONFIG_ENV_OFFSET_REDUND;
#endif
return 0; return 0;
} }
@ -110,6 +119,10 @@ static inline int write_env(struct mmc *mmc, unsigned long size,
return (n == blk_cnt) ? 0 : -1; return (n == blk_cnt) ? 0 : -1;
} }
#ifdef CONFIG_ENV_OFFSET_REDUND
static unsigned char env_flags;
#endif
int saveenv(void) int saveenv(void)
{ {
ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
@ -117,16 +130,11 @@ int saveenv(void)
char *res; char *res;
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
u32 offset; u32 offset;
int ret; int ret, copy = 0;
if (init_mmc_for_env(mmc)) if (init_mmc_for_env(mmc))
return 1; return 1;
if (mmc_get_env_addr(mmc, &offset)) {
ret = 1;
goto fini;
}
res = (char *)&env_new->data; res = (char *)&env_new->data;
len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
if (len < 0) { if (len < 0) {
@ -136,7 +144,21 @@ int saveenv(void)
} }
env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE); env_new->crc = crc32(0, &env_new->data[0], ENV_SIZE);
printf("Writing to MMC(%d)... ", CONFIG_SYS_MMC_ENV_DEV);
#ifdef CONFIG_ENV_OFFSET_REDUND
env_new->flags = ++env_flags; /* increase the serial */
if (gd->env_valid == 1)
copy = 1;
#endif
if (mmc_get_env_addr(mmc, copy, &offset)) {
ret = 1;
goto fini;
}
printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "",
CONFIG_SYS_MMC_ENV_DEV);
if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) { if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
puts("failed\n"); puts("failed\n");
ret = 1; ret = 1;
@ -146,6 +168,10 @@ int saveenv(void)
puts("done\n"); puts("done\n");
ret = 0; ret = 0;
#ifdef CONFIG_ENV_OFFSET_REDUND
gd->env_valid = gd->env_valid == 2 ? 1 : 2;
#endif
fini: fini:
fini_mmc_for_env(mmc); fini_mmc_for_env(mmc);
return ret; return ret;
@ -166,6 +192,93 @@ static inline int read_env(struct mmc *mmc, unsigned long size,
return (n == blk_cnt) ? 0 : -1; return (n == blk_cnt) ? 0 : -1;
} }
#ifdef CONFIG_ENV_OFFSET_REDUND
void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
u32 offset1, offset2;
int read1_fail = 0, read2_fail = 0;
int crc1_ok = 0, crc2_ok = 0;
env_t *ep, *tmp_env1, *tmp_env2;
int ret;
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");
ret = 1;
goto err;
}
if (init_mmc_for_env(mmc)) {
ret = 1;
goto err;
}
if (mmc_get_env_addr(mmc, 0, &offset1) ||
mmc_get_env_addr(mmc, 1, &offset2)) {
ret = 1;
goto fini;
}
read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
if (read1_fail && read2_fail)
puts("*** Error - No Valid Environment Area found\n");
else if (read1_fail || read2_fail)
puts("*** Warning - some problems detected "
"reading environment; recovered successfully\n");
crc1_ok = !read1_fail &&
(crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
crc2_ok = !read2_fail &&
(crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
if (!crc1_ok && !crc2_ok) {
ret = 1;
goto fini;
} 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)
ep = tmp_env1;
else
ep = tmp_env2;
env_flags = ep->flags;
env_import((char *)ep, 0);
ret = 0;
fini:
fini_mmc_for_env(mmc);
err:
if (ret)
set_default_env(NULL);
free(tmp_env1);
free(tmp_env2);
#endif
}
#else /* ! CONFIG_ENV_OFFSET_REDUND */
void env_relocate_spec(void) void env_relocate_spec(void)
{ {
#if !defined(ENV_IS_EMBEDDED) #if !defined(ENV_IS_EMBEDDED)
@ -179,7 +292,7 @@ void env_relocate_spec(void)
goto err; goto err;
} }
if (mmc_get_env_addr(mmc, &offset)) { if (mmc_get_env_addr(mmc, 0, &offset)) {
ret = 1; ret = 1;
goto fini; goto fini;
} }
@ -199,3 +312,4 @@ err:
set_default_env(NULL); set_default_env(NULL);
#endif #endif
} }
#endif /* CONFIG_ENV_OFFSET_REDUND */