[U-Boot] [PATCH v4] NAND: environment offset in OOB (CONFIG_ENV_OFFSET_OOB)

Ben Gardiner bengardiner at nanometrics.ca
Mon Jul 5 19:27:07 CEST 2010


This is a re-submission of the patch by Harald Welte
<laforge at openmoko.org> with minor modifications for rebase and changes
as suggested by Scott Wood <scottwood at freescale.com> [1] [2].

This patch enables the environment partition to have a run-time dynamic
location (offset) in the NAND flash.  The reason for this is simply that
all NAND flashes have factory-default bad blocks, and a fixed compile
time offset would mean that sometimes the environment partition would
live inside factory bad blocks. Since the number of factory default
blocks can be quite high (easily 1.3MBytes in current standard
components), it is not economic to keep that many spare blocks inside
the environment partition.

With this patch and CONFIG_ENV_OFFSET_OOB enabled, the location of the
environment partition is stored in the out-of-band (OOB) data of the
first block in flash. Since the first block is where most systems boot
from, the vendors guarantee that the first block is not a factory
default block.

This patch introduces the 'nand env.oob' command, which can be called
from the u-boot command line. 'nand env.oob get' reads the address of
the environment partition from the OOB data, 'nand env.oob set
{offset,partition-name}' allows the setting of the marker by specifying
a numeric offset or a partition name.

[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916
[2] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/79195

Signed-off-by: Ben Gardiner <bengardiner at nanometrics.ca>
Acked-by: Harald Welte <laforge at gnumonks.org>

---

Changes in v4:
 * Added 'Acked-by Harald Welte' with permission.
 * rebased to 54841ab50c20d6fa6c9cc3eb826989da3a22d934 of
   git://git.denx.de/u-boot.git
 * adding OpenMoko copyright statement as requested by
   Harald Welte <laforge at gnumonks.org> in review
 * committing forgotten fixes for checkpath errors -- only two warnings
   remain, both of which are not applicable
 * fix warning created by passing const pointer to the do_nand_env_oob
   function which did not declare argv as a const pointer

Changes in v3:
 * updated commit message
 * rebased to 39ddd10b046fb791f47281ffb2100be01909ad72 of
   git://git.denx.de/u-boot.git
 * tested using small config changes to include/configs/da850evm.h

Changes in v2:
 * don't use generic names for the env-offset global and the comand
 * make a sub-command of the nand command
 * store the offset in units of eraseblocks
 * allocate oob write/read buffers on stack
 * verify write of new offset
 * make setting new offset affect the live values
 * update copyright of file
 * use the global variable instead of the macro in address statements
 * don't make the oob used bytes configurable
 * don't undef CONFIG_ENV_OFFSET

I verified the patch with checkpath.pl. The checkpatch.pl output follows:

WARNING: suspect code indent for conditional statements (8, 14)
+       if (strcmp(cmd, "env.oob") == 0)
+             return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1);

WARNING: Use #include <linux/errno.h> instead of <asm/errno.h>
+#include <asm/errno.h>

total: 0 errors, 2 warnings, 247 lines checked

Neither of these warnings appear to be applicable.

I tested the binary size and compiler warnings on ARM9 and 8xx with the
following commands:

 #checkout u-boot/master, apply changes to da850evm_config for testing,
 #commit
 ./MAKEALL ARM9 2>&1 > ../makeall-master.log
 ./MAKEALL 8xx 2>&1 > ../makeall-master.log
 #apply patch, commit
 ./MAKEALL ARM9 2>&1 > ../makeall-env_oob.log
 ./MAKEALL 8xx 2>&1 > ../makeall-8xx-env_oob.log
 diff -burp ../makeall-8xx-master.log ../makeall-8xx-env_oob.log
 diff -burp ../makeall-master.log ../makeall-env_oob.log

The only output of the diff commands was in the modified da850evm_config.
The diff shows the text section has grown by 1352 bytes with the feature
introduced by this patch enabled.

@@ -48,7 +48,7 @@ Configuring for da830evm board...
  147617           4888  295320  447825   6d551 ./u-boot
 Configuring for da850evm board...
    text           data     bss     dec     hex filename
- 198497          10332  296608  505437   7b65d ./u-boot
+ 199849          10332  296612  506793   7bba9 ./u-boot
 Configuring for edb9301 board...
    text           data     bss     dec     hex filename
  133899           3772  213400  351071   55b5f ./u-boot
---
 common/cmd_nand.c     |  107 ++++++++++++++++++++++++++++++++++++++++++++++++-
 common/env_nand.c     |   46 +++++++++++++++++++++
 include/environment.h |   21 +++++++---
 include/nand.h        |    9 ++++
 4 files changed, 176 insertions(+), 7 deletions(-)

diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index ea80555..a4c67c1 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -4,6 +4,10 @@
  * (c) 1999 Machine Vision Holdings, Inc.
  * (c) 1999, 2000 David Woodhouse <dwmw2 at infradead.org>
  *
+ * Ported 'dynenv' to 'nand env.oob' command
+ * (C) 2010 Nanometrics, Inc.
+ * 'dynenv' -- Dynamic environment offset in NAND OOB
+ * (C) Copyright 2006-2007 OpenMoko, Inc.
  * Added 16-bit nand support
  * (C) 2004 Texas Instruments
  */
@@ -193,6 +197,90 @@ static void do_nand_status(nand_info_t *nand)
 }
 #endif
 
+#ifdef CONFIG_ENV_OFFSET_OOB
+unsigned long nand_env_oob_offset;
+
+int do_nand_env_oob(cmd_tbl_t *cmdtp, nand_info_t *nand,
+				 int argc, char * const argv[])
+{
+	int ret;
+	uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
+
+	char *cmd = argv[1];
+
+	if (!strcmp(cmd, "get")) {
+		ret = get_nand_env_oob(nand, &nand_env_oob_offset);
+		if (!ret)
+			printf("0x%08lx\n", nand_env_oob_offset);
+		else
+			return 1;
+	} else if (!strcmp(cmd, "set")) {
+		ulong addr;
+		size_t dummy_size;
+		struct mtd_oob_ops ops;
+
+		if (argc < 3)
+			goto usage;
+
+		if (arg_off_size(argc-2, argv + 2, nand, &addr,
+						  &dummy_size) < 0) {
+			printf("Offset or partition name expected\n");
+			return 1;
+		}
+
+		if (nand->oobavail < ENV_OFFSET_SIZE) {
+			printf("Insufficient available OOB bytes: %d OOB bytes"
+			    " available but %d required for env.oob support\n",
+								nand->oobavail,
+							      ENV_OFFSET_SIZE);
+			return 1;
+		}
+
+		if ((addr & (nand->erasesize - 1)) != 0) {
+			printf("Environment offset must be block-aligned\n");
+			return 1;
+		}
+
+		ops.datbuf = NULL;
+		ops.mode = MTD_OOB_AUTO;
+		ops.ooboffs = 0;
+		ops.ooblen = ENV_OFFSET_SIZE;
+		ops.oobbuf = (void *) oob_buf;
+
+		oob_buf[0] = ENV_OOB_MARKER;
+		oob_buf[1] = addr / nand->erasesize;
+
+		ret = nand->write_oob(nand, ENV_OFFSET_SIZE, &ops);
+		if (!ret) {
+			ret = get_nand_env_oob(nand, &nand_env_oob_offset);
+			if (ret) {
+				printf("Error reading env offset in OOB\n");
+				return ret;
+			}
+
+			if (addr != nand_env_oob_offset) {
+				printf("Verification of env offset in OOB "
+				       "failed: 0x%08lx expected but got "
+				       "0x%08lx\n", addr, nand_env_oob_offset);
+				return 1;
+			}
+		} else {
+			printf("Error writing OOB block 0\n");
+			return ret;
+		}
+	} else {
+		goto usage;
+	}
+
+	return ret;
+
+usage:
+	cmd_usage(cmdtp);
+	return 1;
+}
+
+#endif
+
 static void nand_print_info(int idx)
 {
 	nand_info_t *nand = &nand_info[idx];
@@ -272,9 +360,19 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
 	    strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 &&
 	    strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 &&
 	    strcmp(cmd, "biterr") != 0 &&
-	    strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 )
+	    strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0
+#ifdef CONFIG_ENV_OFFSET_OOB
+	    && strcmp(cmd, "env.oob") != 0
+#endif
+	    )
 		goto usage;
 
+#ifdef CONFIG_ENV_OFFSET_OOB
+	/* this command operates only on the first nand device */
+	if (strcmp(cmd, "env.oob") == 0)
+	      return do_nand_env_oob(cmdtp, &nand_info[0], argc - 1, argv + 1);
+#endif
+
 	/* the following commands operate on the current device */
 	if (nand_curr_device < 0 || nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
 	    !nand_info[nand_curr_device].name) {
@@ -502,6 +600,13 @@ U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand,
 	"    bring nand to lock state or display locked pages\n"
 	"nand unlock [offset] [size] - unlock section"
 #endif
+#ifdef CONFIG_ENV_OFFSET_OOB
+	"\n"
+	"nand env.oob - environment offset in OOB of block 0 of"
+	"    first device.\n"
+	"nand env.oob set off|partition - set enviromnent offset\n"
+	"nand env.oob get - get environment offset"
+#endif
 );
 
 static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
diff --git a/common/env_nand.c b/common/env_nand.c
index 50bc111..47d9848 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -38,6 +38,7 @@
 #include <linux/stddef.h>
 #include <malloc.h>
 #include <nand.h>
+#include <asm/errno.h>
 
 #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND)
 #define CMD_SAVEENV
@@ -284,6 +285,40 @@ int readenv (size_t offset, u_char * buf)
 	return 0;
 }
 
+#ifdef CONFIG_ENV_OFFSET_OOB
+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)];
+	int ret;
+
+	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) {
+		if (oob_buf[0] == ENV_OOB_MARKER) {
+			*result = oob_buf[1] * nand->erasesize;
+		} else if (oob_buf[0] == ENV_OOB_MARKER_OLD) {
+			*result = oob_buf[1];
+		} else {
+			printf("No dynamic environment marker in OOB block 0"
+									"\n");
+			ret = -ENOENT;
+			goto fail;
+		}
+	} else {
+		printf("error reading OOB block 0\n");
+	}
+fail:
+	return ret;
+}
+#endif
+
 #ifdef CONFIG_ENV_OFFSET_REDUND
 void env_relocate_spec (void)
 {
@@ -353,6 +388,17 @@ void env_relocate_spec (void)
 #if !defined(ENV_IS_EMBEDDED)
 	int ret;
 
+#if defined(CONFIG_ENV_OFFSET_OOB)
+	ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset);
+	/* If unable to read environment offset from NAND OOB then fall through
+	 * to the normal environment reading code below
+	 */
+	if (!ret)
+		printf("Found Environment offset in OOB..\n");
+	else
+		return use_default();
+#endif
+
 	ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
 	if (ret)
 		return use_default();
diff --git a/include/environment.h b/include/environment.h
index 203f731..fbccf6a 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -74,15 +74,24 @@
 #endif	/* CONFIG_ENV_IS_IN_FLASH */
 
 #if defined(CONFIG_ENV_IS_IN_NAND)
-# ifndef CONFIG_ENV_OFFSET
-#  error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND"
-# endif
+# if defined(CONFIG_ENV_OFFSET_OOB)
+#  ifdef CONFIG_ENV_OFFSET_REDUND
+#   error "CONFIG_ENV_OFFSET_REDUND is not supported when CONFIG_ENV_OFFSET_OOB"
+#   error "is set"
+#  endif
+extern unsigned long nand_env_oob_offset;
+#  define CONFIG_ENV_OFFSET nand_env_oob_offset
+# else
+#  ifndef CONFIG_ENV_OFFSET
+#   error "Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND"
+#  endif
+#  ifdef CONFIG_ENV_OFFSET_REDUND
+#   define CONFIG_SYS_REDUNDAND_ENVIRONMENT
+#  endif
+# endif /* CONFIG_ENV_OFFSET_OOB */
 # ifndef CONFIG_ENV_SIZE
 #  error "Need to define CONFIG_ENV_SIZE when using CONFIG_ENV_IS_IN_NAND"
 # endif
-# ifdef CONFIG_ENV_OFFSET_REDUND
-#  define CONFIG_SYS_REDUNDAND_ENVIRONMENT
-# endif
 #endif /* CONFIG_ENV_IS_IN_NAND */
 
 #if defined(CONFIG_ENV_IS_IN_MG_DISK)
diff --git a/include/nand.h b/include/nand.h
index 2a81597..8bdf419 100644
--- a/include/nand.h
+++ b/include/nand.h
@@ -130,3 +130,12 @@ void board_nand_select_device(struct nand_chip *nand, int chip);
 __attribute__((noreturn)) void nand_boot(void);
 
 #endif
+
+#ifdef CONFIG_ENV_OFFSET_OOB
+#define ENV_OOB_MARKER 0x30425645 /*"EVB0" in little-endian -- offset is stored
+				    as block number*/
+#define ENV_OOB_MARKER_OLD 0x30564e45 /*"ENV0" in little-endian -- offset is
+					stored as byte number */
+#define ENV_OFFSET_SIZE 8
+int get_nand_env_oob(nand_info_t *nand, unsigned long *result);
+#endif
-- 
1.7.0.4



More information about the U-Boot mailing list