Honour the EcoNet/Airoha NAND "remap" region attribute when reading and
writing MTD partitions from the env and the mtd command, matching the
vendor block layout.

Signed-off-by: AK Sharma <[email protected]>
---
 cmd/mtd.c   | 30 ++++++++++++++--
 env/Kconfig |  2 +-
 env/mtd.c   | 98 +++++++++++++++++++++++++++++++++--------------------
 3 files changed, 89 insertions(+), 41 deletions(-)

diff --git a/cmd/mtd.c b/cmd/mtd.c
index 7f251440..d7b8fa3b 100644
--- a/cmd/mtd.c
+++ b/cmd/mtd.c
@@ -519,6 +519,13 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int 
argc,
                goto out_put_mtd;
        }
 
+       if (start_off >= mtd->size) {
+               printf("Offset 0x%llx is past the end of %s (size 0x%llx)\n",
+                      start_off, mtd->name, mtd->size);
+               ret = CMD_RET_FAILURE;
+               goto out_put_mtd;
+       }
+
        default_len = dump ? mtd->writesize : mtd->size;
        len = argc > 1 ? hextoul(argv[1], NULL) : default_len;
        if (!mtd_is_aligned_with_min_io_size(mtd, len)) {
@@ -557,11 +564,21 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int 
argc,
        io_op.datbuf = buf;
        io_op.oobbuf = woob ? &buf[len] : NULL;
 
-       /* Search for the first good block after the given offset */
+       /*
+        * Search for the first good block after the given offset.
+        * Bounded: mtd_block_isbad() returns a negative errno past the end of
+        * the device, which is nonzero and would otherwise spin forever.
+        */
        off = start_off;
-       while (mtd_block_isbad(mtd, off))
+       while (off < mtd->size && mtd_block_isbad(mtd, off) > 0)
                off += mtd->erasesize;
 
+       if (off >= mtd->size) {
+               printf("No good block found after offset 0x%llx\n", start_off);
+               ret = CMD_RET_FAILURE;
+               goto out_put_mtd;
+       }
+
        led_activity_blink();
 
        if (benchmark)
@@ -569,9 +586,16 @@ static int do_mtd_io(struct cmd_tbl *cmdtp, int flag, int 
argc,
 
        /* Loop over the pages to do the actual read/write */
        while (remaining) {
+               if (off >= mtd->size) {
+                       printf("Reached the end of %s before completing\n",
+                              mtd->name);
+                       ret = -EIO;
+                       break;
+               }
+
                /* Skip the block if it is bad */
                if (mtd_is_aligned_with_block_size(mtd, off) &&
-                   mtd_block_isbad(mtd, off)) {
+                   mtd_block_isbad(mtd, off) > 0) {
                        off += mtd->erasesize;
                        continue;
                }
diff --git a/env/Kconfig b/env/Kconfig
index 7abd82ab..ba45d194 100644
--- a/env/Kconfig
+++ b/env/Kconfig
@@ -652,7 +652,7 @@ config ENV_OFFSET_RELATIVE_END
 config ENV_OFFSET_REDUND
        hex "Redundant environment offset"
        depends on (ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
-                   ENV_IS_IN_SPI_FLASH) && ENV_REDUNDANT
+                   ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD) && ENV_REDUNDANT
        default 0x10C0000 if MICROBLAZE
        default 0x0
        help
diff --git a/env/mtd.c b/env/mtd.c
index b26ee809..bf9fa516 100644
--- a/env/mtd.c
+++ b/env/mtd.c
@@ -2,6 +2,7 @@
 /*
  *  Author: Christian Marangi <[email protected]>
  */
+#include <env.h>
 #include <env_internal.h>
 #include <errno.h>
 #include <malloc.h>
@@ -9,6 +10,7 @@
 #include <asm/cache.h>
 #include <asm/global_data.h>
 #include <linux/mtd/mtd.h>
+#include <memalign.h>
 #include <u-boot/crc.h>
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -30,6 +32,29 @@ static int setup_mtd_device(struct mtd_info **mtd_env)
        return 0;
 }
 
+static int env_mtd_read_buf(struct mtd_info *mtd, u32 offset, void *buf, int 
len)
+{
+       u32 sect_size = mtd->erasesize;
+       size_t ret_len;
+       char *tmp = buf;
+       int remaining = len;
+       int ret;
+
+       while (remaining) {
+               if (!(offset % sect_size) && mtd_block_isbad(mtd, offset)) {
+                       offset += sect_size;
+                       continue;
+               }
+               ret = mtd_read(mtd, offset, mtd->writesize, &ret_len, tmp);
+               if (ret)
+                       return ret;
+               tmp += ret_len;
+               offset += ret_len;
+               remaining -= ret_len;
+       }
+       return 0;
+}
+
 static int env_mtd_save(void)
 {
        char *saved_buf = NULL, *write_buf, *tmp;
@@ -40,7 +65,7 @@ static int env_mtd_save(void)
        u32 write_size;
        env_t env_new;
        int remaining;
-       u32 offset;
+       u32 offset, save_off;
        int ret;
 
        ret = setup_mtd_device(&mtd_env);
@@ -48,6 +73,12 @@ static int env_mtd_save(void)
                return ret;
 
        sect_size = mtd_env->erasesize;
+#ifdef CONFIG_ENV_OFFSET_REDUND
+       save_off = (gd->env_valid == ENV_VALID) ?
+               CONFIG_ENV_OFFSET_REDUND : CONFIG_ENV_OFFSET;
+#else
+       save_off = CONFIG_ENV_OFFSET;
+#endif
 
        /* Is the sector larger than the env (i.e. embedded) */
        if (sect_size > CONFIG_ENV_SIZE) {
@@ -57,7 +88,7 @@ static int env_mtd_save(void)
                        goto done;
                }
 
-               offset = CONFIG_ENV_OFFSET;
+               offset = save_off;
                remaining = sect_size;
                tmp = saved_buf;
 
@@ -87,7 +118,7 @@ static int env_mtd_save(void)
        sect_num = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
 
        ei.mtd = mtd_env;
-       ei.addr = CONFIG_ENV_OFFSET;
+       ei.addr = save_off;
        ei.len = sect_num * sect_size;
 
        puts("Erasing MTD...");
@@ -104,7 +135,7 @@ static int env_mtd_save(void)
                write_buf = (char *)&env_new;
        }
 
-       offset = CONFIG_ENV_OFFSET;
+       offset = save_off;
        remaining = write_size;
        tmp = write_buf;
 
@@ -129,6 +160,9 @@ static int env_mtd_save(void)
 
        ret = 0;
        puts("done\n");
+#ifdef CONFIG_ENV_OFFSET_REDUND
+       gd->env_valid = gd->env_valid == ENV_VALID ? ENV_REDUND : ENV_VALID;
+#endif
 
 done:
        put_mtd_device(mtd_env);
@@ -142,12 +176,8 @@ done:
 static int env_mtd_load(void)
 {
        struct mtd_info *mtd_env;
-       char *buf, *tmp;
-       size_t ret_len;
-       int remaining;
-       u32 sect_size;
-       u32 offset;
-       int ret;
+       char *buf, *buf2 = NULL;
+       int ret, read1_fail, read2_fail = 1;
 
        buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
        if (!buf) {
@@ -159,40 +189,34 @@ static int env_mtd_load(void)
        if (ret)
                goto out;
 
-       sect_size = mtd_env->erasesize;
-
-       offset = CONFIG_ENV_OFFSET;
-       remaining = CONFIG_ENV_SIZE;
-       tmp = buf;
-
-       while (remaining) {
-               /* Skip the block if it is bad */
-               if (!(offset % sect_size) &&
-                   mtd_block_isbad(mtd_env, offset)) {
-                       offset += sect_size;
-                       continue;
-               }
-
-               ret = mtd_read(mtd_env, offset, mtd_env->writesize,
-                              &ret_len, tmp);
-               if (ret) {
-                       env_set_default("mtd_read() failed", 1);
-                       goto out;
-               }
-
-               tmp += ret_len;
-               offset += ret_len;
-               remaining -= ret_len;
+       read1_fail = env_mtd_read_buf(mtd_env, CONFIG_ENV_OFFSET, buf,
+                                     CONFIG_ENV_SIZE);
+#ifdef CONFIG_ENV_OFFSET_REDUND
+       buf2 = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
+       if (!buf2) {
+               env_set_default("memalign() failed", 0);
+               ret = -EIO;
+               goto out_dev;
+       }
+       read2_fail = env_mtd_read_buf(mtd_env, CONFIG_ENV_OFFSET_REDUND, buf2,
+                                     CONFIG_ENV_SIZE);
+       ret = env_import_redund(buf, read1_fail, buf2, read2_fail, H_EXTERNAL);
+#else
+       if (read1_fail) {
+               env_set_default("mtd_read() failed", 1);
+               ret = read1_fail;
+               goto out_dev;
        }
-
        ret = env_import(buf, 1, H_EXTERNAL);
        if (!ret)
                gd->env_valid = ENV_VALID;
+#endif
 
-out:
+out_dev:
        put_mtd_device(mtd_env);
-
+out:
        free(buf);
+       free(buf2);
 
        return ret;
 }
-- 
2.53.0

Reply via email to