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

Ben Gardiner bengardiner at nanometrics.ca
Mon May 31 23:29:46 CEST 2010


This is a re-submission of the patch by Harald Welte
<laforge at openmoko.org> with modifications to make it a sub-command and changes
as suggested by Scott Wood <scottwood at freescale.com> in
http://article.gmane.org/gmane.comp.boot-loaders.u-boot/43916.

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.

---

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

Tested with the da850 support patch series from Sudhakar Rjashekhara [1]

./MAKEALL arm on master
--------------------- SUMMARY ----------------------------
Boards compiled: 150
Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50
SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3
afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek
at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2
CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570
pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4
ixdp425 ixdpg425 pdnb3 scpu )
----------------------------------------------------------

./MAKEALL arm with the patch
--------------------- SUMMARY ----------------------------
Boards compiled: 150
Boards with warnings or errors: 47 ( shannon evb4510 lpc2292sodimm modnet50
SMN42 guruplug mv88f6281gtw_ge netstar openrd_base rd6281a sheevaplug suen3
afeb9260 at91cap9adk at91rm9200dk at91rm9200ek at91sam9260ek at91sam9261ek
at91sam9263ek at91sam9g10ek at91sam9g20ek at91sam9m10g45ek at91sam9rlek cmc_pu2
CPUAT91 CPU9260 CPU9G20 csb637 eb_cpux9k2 kb9202 meesc mp2usb m501sk otc570
pm9261 pm9263 SBC35_A9G20 TNY_A9260 TNY_A9G20 actux1 actux2 actux3 actux4
ixdp425 ixdpg425 pdnb3 scpu )
----------------------------------------------------------

[1] http://article.gmane.org/gmane.comp.boot-loaders.u-boot/78947

Signed-off-by: Ben Gardiner <bengardiner at nanometrics.ca>
---
 common/cmd_nand.c     |  107 ++++++++++++++++++++++++++++++++++++++++++++++++-
 common/env_nand.c     |   45 ++++++++++++++++++++
 include/environment.h |   20 ++++++---
 include/nand.h        |    9 ++++
 4 files changed, 174 insertions(+), 7 deletions(-)

diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index f611fd7..5af5b9e 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -6,6 +6,8 @@
  *
  * Added 16-bit nand support
  * (C) 2004 Texas Instruments
+ * Added env offset in OOB
+ * (C) 2010 Nanometrics, Inc.
  */
 
 #include <common.h>
@@ -193,6 +195,91 @@ 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 *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 +359,20 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *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 a15a950..2289eb2 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
@@ -288,6 +289,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)
 {
@@ -357,6 +392,16 @@ 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(!ret) {
+		/* fall through to the normal environment reading code below */
+		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 b9924fd..9820d23 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -74,15 +74,23 @@
 #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 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