summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorPhil Sutter <phil.sutter@viprinet.com>2013-07-19 12:20:26 +0200
committerScott Wood <scottwood@freescale.com>2013-08-22 17:49:47 -0500
commit2b26201a2aef0b310b7c04702b0dba5dea493f77 (patch)
tree1c251e0d0ae78df6b5f51cbd4470e783a4cb4c12 /common
parent47b6dad319af3b0d059d1d16003f8ac73e98cdb0 (diff)
env_nand.c: support falling back to redundant env when writing
Without this patch, when the currently chosen environment to be written has bad blocks, saveenv fails completely. Instead, when there is redundant environment fall back to the other copy. Environment reading needs no adjustment, as the fallback logic for incomplete writes applies to this case as well. Signed-off-by: Phil Sutter <phil.sutter@viprinet.com>
Diffstat (limited to 'common')
-rw-r--r--common/env_nand.c116
1 files changed, 54 insertions, 62 deletions
diff --git a/common/env_nand.c b/common/env_nand.c
index 9a6b8a6fb2d..75309621b03 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -152,72 +152,57 @@ int writeenv(size_t offset, u_char *buf)
return 0;
}
-#ifdef CONFIG_ENV_OFFSET_REDUND
-static unsigned char env_flags;
+struct env_location {
+ const char *name;
+ const nand_erase_options_t erase_opts;
+};
-int saveenv(void)
+static int erase_and_write_env(const struct env_location *location,
+ u_char *env_new)
{
- env_t env_new;
- ssize_t len;
- char *res;
- int ret = 0;
- nand_erase_options_t nand_erase_options;
+ int ret = 0;
- memset(&nand_erase_options, 0, sizeof(nand_erase_options));
- nand_erase_options.length = CONFIG_ENV_RANGE;
-
- if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
- return 1;
-
- res = (char *)&env_new.data;
- len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
- if (len < 0) {
- error("Cannot export environment: errno = %d\n", errno);
- return 1;
- }
- env_new.crc = crc32(0, env_new.data, ENV_SIZE);
- env_new.flags = ++env_flags; /* increase the serial */
-
- if (gd->env_valid == 1) {
- puts("Erasing redundant NAND...\n");
- nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
- return 1;
-
- puts("Writing to redundant NAND... ");
- ret = writeenv(CONFIG_ENV_OFFSET_REDUND, (u_char *)&env_new);
- } else {
- puts("Erasing NAND...\n");
- nand_erase_options.offset = CONFIG_ENV_OFFSET;
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
- return 1;
-
- puts("Writing to NAND... ");
- ret = writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new);
- }
- if (ret) {
- puts("FAILED!\n");
+ printf("Erasing %s...\n", location->name);
+ if (nand_erase_opts(&nand_info[0], &location->erase_opts))
return 1;
- }
-
- puts("done\n");
- gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+ printf("Writing to %s... ", location->name);
+ ret = writeenv(location->erase_opts.offset, env_new);
+ puts(ret ? "FAILED!\n" : "OK\n");
return ret;
}
-#else /* ! CONFIG_ENV_OFFSET_REDUND */
+
+#ifdef CONFIG_ENV_OFFSET_REDUND
+static unsigned char env_flags;
+#endif
+
int saveenv(void)
{
int ret = 0;
ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
ssize_t len;
char *res;
- nand_erase_options_t nand_erase_options;
+ int env_idx = 0;
+ static const struct env_location location[] = {
+ {
+ .name = "NAND",
+ .erase_opts = {
+ .length = CONFIG_ENV_RANGE,
+ .offset = CONFIG_ENV_OFFSET,
+ },
+ },
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ {
+ .name = "redundant NAND",
+ .erase_opts = {
+ .length = CONFIG_ENV_RANGE,
+ .offset = CONFIG_ENV_OFFSET_REDUND,
+ },
+ },
+#endif
+ };
- memset(&nand_erase_options, 0, sizeof(nand_erase_options));
- nand_erase_options.length = CONFIG_ENV_RANGE;
- nand_erase_options.offset = CONFIG_ENV_OFFSET;
if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
return 1;
@@ -228,22 +213,29 @@ int saveenv(void)
error("Cannot export environment: errno = %d\n", errno);
return 1;
}
- env_new->crc = crc32(0, env_new->data, ENV_SIZE);
-
- puts("Erasing Nand...\n");
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
- return 1;
+ env_new->crc = crc32(0, env_new->data, ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ env_new->flags = ++env_flags; /* increase the serial */
+ env_idx = (gd->env_valid == 1);
+#endif
- puts("Writing to Nand... ");
- if (writeenv(CONFIG_ENV_OFFSET, (u_char *)env_new)) {
- puts("FAILED!\n");
- return 1;
+ ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+ if (!ret) {
+ /* preset other copy for next write */
+ gd->env_valid = gd->env_valid == 2 ? 1 : 2;
+ return ret;
}
- puts("done\n");
+ env_idx = (env_idx + 1) & 1;
+ ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
+ if (!ret)
+ printf("Warning: primary env write failed,"
+ " redundancy is lost!\n");
+#endif
+
return ret;
}
-#endif /* CONFIG_ENV_OFFSET_REDUND */
#endif /* CMD_SAVEENV */
int readenv(size_t offset, u_char *buf)