[U-Boot-Users] [PATCH] Support dynamic/patched NAND ENV offset

Harald Welte
2008-07-09T08:11:29Z

Hi Scott!

This is now the 'dynenv' patch based on the u-boot-nand-flash/testing
git tree:

==========================

NAND: Support dynamic location of enviromnent (CFG_ENV_OFFSET_OOB)

This patch enables the environment parttion 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 CFG_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 'dynenv' command, which can be called from the u-boot
command line. 'dynenv get' reads the address of the environment partition from
the OOB data, 'dynenv set {offset,partition-name}' allows the setting of the
marker by specifying a numeric offset or a partition name.

Signed-off-by: Harald Welte <laforge@openmoko.org>

diff --git a/common/Makefile b/common/Makefile
index ecf755f..d1d50bf 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -55,6 +55,7 @@ endif
COBJS-$(CONFIG_CMD_DISPLAY) += cmd_display.o
COBJS-$(CONFIG_CMD_DOC) += cmd_doc.o
COBJS-$(CONFIG_CMD_DTT) += cmd_dtt.o
+COBJS-y += cmd_dynenv.o
COBJS-y += cmd_eeprom.o
COBJS-$(CONFIG_CMD_ELF) += cmd_elf.o
COBJS-$(CONFIG_CMD_EXT2) += cmd_ext2.o
diff --git a/common/cmd_dynenv.c b/common/cmd_dynenv.c
new file mode 100644
index 0000000..8fd21c1
--- /dev/null
+++ b/common/cmd_dynenv.c
@@ -0,0 +1,121 @@
+/*
+ * (C) Copyright 2006-2007 OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ * (C) Copyright 2008 Harald Welte <laforge@openmoko.org>
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <environment.h>
+#include <nand.h>
+#include "cmd_nand.h"
+#include <asm/errno.h>
+
+#if defined(CFG_ENV_OFFSET_OOB)
+
+int do_dynenv(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ struct mtd_info *mtd = &nand_info[0];
+ int ret, size = 8;
+ struct mtd_oob_ops ops;
+ uint8_t *buf;
+
+ char *cmd = argv[1];
+
+ buf = malloc(mtd->oobsize);
+ if (!buf)
+ return -ENOMEM;
+
+ ops.mode = MTD_OOB_RAW;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+ ops.oobbuf = buf;
+ ops.datbuf = buf;
+ ops.len = size;
+
+ ret = mtd->read_oob(mtd, 8, &ops);
+ if (!strcmp(cmd, "get")) {
+
+ if (buf[0] == 'E' && buf[1] == 'N' &&
+ buf[2] == 'V' && buf[3] == '0')
+ printf("0x%08x\n", *((u_int32_t *) &buf[4]));
+ else
+ printf("No dynamic environment marker in OOB block 0\n");
+
+ } else if (!strcmp(cmd, "set")) {
+ unsigned long addr, dummy;
+
+ if (argc < 3)
+ goto usage;
+
+ buf[0] = 'E';
+ buf[1] = 'N';
+ buf[2] = 'V';
+ buf[3] = '0';
+
+ if (arg_off_size(argc-2, argv+2, mtd, &addr, &dummy, 1) < 0) {
+ printf("Offset or partition name expected\n");
+ goto fail;
+ }
+ if (!ret) {
+ uint8_t tmp[4];
+ int i;
+
+ memcpy(&tmp, &addr, 4);
+ for (i = 0; i != 4; i++)
+ if (tmp[i] & ~buf[i+4]) {
+ printf("ERROR: erase OOB block to "
+ "write this value\n");
+ goto fail;
+ }
+ }
+ memcpy(buf+4, &addr, 4);
+
+ printf("%02x %02x %02x %02x - %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ ops.mode = MTD_OOB_RAW;
+ ops.ooboffs = 0;
+ ops.ooblen = mtd->oobsize;
+ ops.oobbuf = buf;
+ ops.datbuf = buf;
+ ops.len = size;
+ ret = mtd->write_oob(mtd, 8, &ops);
+ if (!ret)
+ CFG_ENV_OFFSET = addr;
+ } else
+ goto usage;
+
+ free(buf);
+ return ret;
+
+usage:
+ printf("Usage:\n%s\n", cmdtp->usage);
+fail:
+ free(buf);
+ return 1;
+}
+
+U_BOOT_CMD(dynenv, 3, 1, do_dynenv,
+ "dynenv - dynamically placed (NAND) environment\n",
+ "dynenv set off - set enviromnent offset\n"
+ "dynenv get - get environment offset\n");
+
+#endif /* CFG_ENV_OFFSET_OOB */
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 7c26ceb..a72e553 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -101,7 +101,7 @@ static inline int str2long(char *p, ulong *num)
return (*p != '\0' && *endptr == '\0') ? 1 : 0;
}

-static int
+int
arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off, size_t *size)
{
int idx = nand_curr_device;
diff --git a/common/cmd_nand.h b/common/cmd_nand.h
new file mode 100644
index 0000000..bedcda9
--- /dev/null
+++ b/common/cmd_nand.h
@@ -0,0 +1,33 @@
+/*
+ * cmd_nand.h - Convenience functions
+ *
+ * (C) Copyright 2006-2007 OpenMoko, Inc.
+ * Author: Werner Almesberger <werner@openmoko.org>
+ *
+ * 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
+ */
+
+#ifndef CMD_NAND_H
+#define CMD_NAND_H
+
+#include <nand.h>
+
+
+/* common/cmd_nand.c */
+int arg_off_size(int argc, char *argv[], nand_info_t *nand, ulong *off,
+ ulong *size, int net);
+
+#endif /* CMD_NAND_H */
diff --git a/common/env_nand.c b/common/env_nand.c
index 1fe874a..14ea480 100644
--- a/common/env_nand.c
+++ b/common/env_nand.c
@@ -292,6 +292,33 @@ void env_relocate_spec (void)
int crc1_ok = 0, crc2_ok = 0;
env_t *tmp_env1, *tmp_env2;

+#if defined(CFG_ENV_OFFSET_OOB)
+ struct mtd_info *mtd = &nand_info[0];
+ struct nand_chip *this = mtd->priv;
+ int buf_len;
+ uint8_t *buf;
+
+ buf_len = (1 << this->bbt_erase_shift);
+ buf_len += (buf_len >> this->page_shift) * mtd->oobsize;
+ buf = malloc(buf_len);
+ if (!buf)
+ return;
+
+ nand_read_raw(mtd, buf, 0, mtd->oobblock, mtd->oobsize);
+ if (buf[mtd->oobblock + 8 + 0] == 'E' &&
+ buf[mtd->oobblock + 8 + 1] == 'N' &&
+ buf[mtd->oobblock + 8 + 2] == 'V' &&
+ buf[mtd->oobblock + 8 + 3] == '0') {
+ CFG_ENV_OFFSET = *((unsigned long *) &buf[mtd->oobblock + 8 + 4]);
+ /* fall through to the normal environment reading code below */
+ free(buf);
+ puts("Found Environment offset in OOB..\n");
+ } else {
+ free(buf);
+ return use_default();
+ }
+#endif
+
total = CFG_ENV_SIZE;

tmp_env1 = (env_t *) malloc(CFG_ENV_SIZE);
diff --git a/common/environment.c b/common/environment.c
index 3b9914f..50d5210 100644
--- a/common/environment.c
+++ b/common/environment.c
@@ -29,6 +29,12 @@
#undef __ASSEMBLY__
#include <environment.h>

+#if defined(CFG_ENV_OFFSET_PATCHED)
+unsigned long env_offset = CFG_ENV_OFFSET_PATCHED;
+#elif defined(CFG_ENV_OFFSET_OOB)
+unsigned long env_offset = CFG_ENV_OFFSET_OOB;
+#endif
+
/*
* Handle HOSTS that have prepended
* crap on symbol names, not TARGETS.
diff --git a/include/environment.h b/include/environment.h
index bf9f669..3256044 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -70,6 +70,10 @@
#endif /* CFG_ENV_IS_IN_FLASH */

#if defined(CFG_ENV_IS_IN_NAND)
+#if defined(CFG_ENV_OFFSET_PATCHED) || defined(CFG_ENV_OFFSET_OOB)
+extern unsigned long env_offset;
+#define CFG_ENV_OFFSET env_offset
+#else
# ifndef CFG_ENV_OFFSET
# error "Need to define CFG_ENV_OFFSET when using CFG_ENV_IS_IN_NAND"
# endif
@@ -82,6 +86,7 @@
# ifdef CFG_ENV_IS_EMBEDDED
# define ENV_IS_EMBEDDED 1
# endif
+#endif /* CFG_ENV_NAND_PATCHED */
#endif /* CFG_ENV_IS_IN_NAND */

#ifdef USE_HOSTCC

--
- Harald Welte <laforge@openmoko.org> http://openmoko.org/
============================================================================
Software for the world's first truly open Free Software mobile phone