* (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Andreas Heppel <aheppel@sysgo.de>
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier: GPL-2.0+
*/
-#define DEBUG
-
#include <common.h>
#include <command.h>
#include <environment.h>
#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND)
#define CMD_SAVEENV
#elif defined(CONFIG_ENV_OFFSET_REDUND)
-#error Cannot use CONFIG_ENV_OFFSET_REDUND without CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND
+#error CONFIG_ENV_OFFSET_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND
#endif
-#if defined(CONFIG_ENV_SIZE_REDUND) && (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
+#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
#define CONFIG_ENV_RANGE CONFIG_ENV_SIZE
#endif
-/* references to names in env_common.c */
-extern uchar default_environment[];
-
char *env_name_spec = "NAND";
-
#if defined(ENV_IS_EMBEDDED)
-extern uchar environment[];
-env_t *env_ptr = (env_t *)(&environment[0]);
+env_t *env_ptr = &environment;
#elif defined(CONFIG_NAND_ENV_DST)
env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST;
#else /* ! ENV_IS_EMBEDDED */
-env_t *env_ptr = 0;
+env_t *env_ptr;
#endif /* ENV_IS_EMBEDDED */
DECLARE_GLOBAL_DATA_PTR;
-uchar env_get_char_spec (int index)
-{
- return ( *((uchar *)(gd->env_addr + index)) );
-}
-
/*
* This is called before nand_init() so we can't read NAND to
* validate env data.
env_t *tmp_env2;
tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
- crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
+ crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
#endif
-
tmp_env1 = env_ptr;
-
- crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
+ crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
if (!crc1_ok && !crc2_ok) {
- gd->env_addr = 0;
- gd->env_valid = 0;
+ gd->env_addr = 0;
+ gd->env_valid = 0;
return 0;
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = 2;
} else {
/* both ok - check serial */
- if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
+ if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
gd->env_valid = 2;
- else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
+ else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
gd->env_valid = 1;
- else if(tmp_env1->flags > tmp_env2->flags)
+ else if (tmp_env1->flags > tmp_env2->flags)
gd->env_valid = 1;
- else if(tmp_env2->flags > tmp_env1->flags)
+ else if (tmp_env2->flags > tmp_env1->flags)
gd->env_valid = 2;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
gd->env_addr = (ulong)env_ptr->data;
#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
- gd->env_addr = (ulong)&default_environment[0];
- gd->env_valid = 1;
+ gd->env_addr = (ulong)&default_environment[0];
+ gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
- return (0);
+ return 0;
}
#ifdef CMD_SAVEENV
* The legacy NAND code saved the environment in the first NAND device i.e.,
* nand_dev_desc + 0. This is also the behaviour using the new NAND code.
*/
-int writeenv(size_t offset, u_char *buf)
+static int writeenv(size_t offset, u_char *buf)
{
size_t end = offset + CONFIG_ENV_RANGE;
size_t amount_saved = 0;
size_t blocksize, len;
-
u_char *char_ptr;
blocksize = nand_info[0].erasesize;
offset += blocksize;
} else {
char_ptr = &buf[amount_saved];
- if (nand_write(&nand_info[0], offset, &len,
- char_ptr))
+ if (nand_write(&nand_info[0], offset, &len, char_ptr))
return 1;
+
offset += blocksize;
amount_saved += len;
}
return 0;
}
-#ifdef CONFIG_ENV_OFFSET_REDUND
-int saveenv(void)
-{
- env_t env_new;
- ssize_t len;
- char *res;
- int ret = 0;
- nand_erase_options_t nand_erase_options;
- nand_erase_options.length = CONFIG_ENV_RANGE;
- nand_erase_options.quiet = 0;
- nand_erase_options.jffs2 = 0;
- nand_erase_options.scrub = 0;
+struct env_location {
+ const char *name;
+ const nand_erase_options_t erase_opts;
+};
- if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
- return 1;
+static int erase_and_write_env(const struct env_location *location,
+ u_char *env_new)
+{
+ int ret = 0;
- res = (char *)&env_new.data;
- len = hexport_r(&env_htab, '\0', &res, ENV_SIZE);
- 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; /* 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;
- env_t env_new;
- ssize_t len;
- char *res;
- nand_erase_options_t nand_erase_options;
+ int ret = 0;
+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
+ 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
+ };
- nand_erase_options.length = CONFIG_ENV_RANGE;
- nand_erase_options.quiet = 0;
- nand_erase_options.jffs2 = 0;
- nand_erase_options.scrub = 0;
- nand_erase_options.offset = CONFIG_ENV_OFFSET;
if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
return 1;
- res = (char *)&env_new.data;
- len = hexport_r(&env_htab, '\0', &res, ENV_SIZE);
- if (len < 0) {
- error("Cannot export environment: errno = %d\n", errno);
- return 1;
- }
- env_new.crc = crc32(0, env_new.data, ENV_SIZE);
+ ret = env_export(env_new);
+ if (ret)
+ return ret;
- puts("Erasing Nand...\n");
- if (nand_erase_opts(&nand_info[0], &nand_erase_options))
- return 1;
+#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)
+static 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;
blocksize = nand_info[0].erasesize;
if (!blocksize)
return 1;
+
len = min(blocksize, CONFIG_ENV_SIZE);
while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
offset += blocksize;
} else {
char_ptr = &buf[amount_loaded];
- if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr))
+ if (nand_read_skip_bad(&nand_info[0], offset,
+ &len, NULL,
+ nand_info[0].size, char_ptr))
return 1;
+
offset += blocksize;
amount_loaded += len;
}
}
+
if (amount_loaded != CONFIG_ENV_SIZE)
return 1;
int get_nand_env_oob(nand_info_t *nand, unsigned long *result)
{
struct mtd_oob_ops ops;
- uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
+ uint32_t oob_buf[ENV_OFFSET_SIZE / sizeof(uint32_t)];
int ret;
- ops.datbuf = NULL;
- ops.mode = MTD_OOB_AUTO;
- ops.ooboffs = 0;
- ops.ooblen = ENV_OFFSET_SIZE;
- ops.oobbuf = (void *) oob_buf;
+ ops.datbuf = NULL;
+ ops.mode = MTD_OOB_AUTO;
+ ops.ooboffs = 0;
+ ops.ooblen = ENV_OFFSET_SIZE;
+ ops.oobbuf = (void *)oob_buf;
ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops);
if (ret) {
void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
+ int read1_fail = 0, read2_fail = 0;
int crc1_ok = 0, crc2_ok = 0;
env_t *ep, *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)) {
+ if (tmp_env1 == NULL || tmp_env2 == NULL) {
puts("Can't allocate buffers for environment\n");
- free(tmp_env1);
- free(tmp_env2);
set_default_env("!malloc() failed");
- return;
+ goto done;
}
- if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
- puts("No Valid Environment Area found\n");
+ read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
+ read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
- if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
- puts("No Valid Redundant Environment Area found\n");
+ 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 = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
- crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
+ 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) {
- free(tmp_env1);
- free(tmp_env2);
set_default_env("!bad CRC");
- return;
+ goto done;
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = 1;
} else if (!crc1_ok && crc2_ok) {
gd->env_valid = 2;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
-
}
free(env_ptr);
else
ep = tmp_env2;
+ env_flags = ep->flags;
env_import((char *)ep, 0);
+done:
free(tmp_env1);
free(tmp_env2);
* device i.e., nand_dev_desc + 0. This is also the behaviour using
* the new NAND code.
*/
-void env_relocate_spec (void)
+void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
int ret;
- char buf[CONFIG_ENV_SIZE];
+ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
#if defined(CONFIG_ENV_OFFSET_OOB)
ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset);